Skip to content

Commit

Permalink
Increased accuracy of log timestamps up to nanoseconds. Removed templ…
Browse files Browse the repository at this point in the history
…ates of tests for Linux. Minor readme corrections.
  • Loading branch information
DnV1eX committed Jul 7, 2020
1 parent 373a0a2 commit 287947b
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 36 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# GoogleCloudLogging

Event logging for client applications on [Apple platforms](#supported-platforms) with support for offline work and automatic upload to [Google Cloud (GCP)](https://cloud.google.com). The package depends on [SwiftLog](https://github.com/apple/swift-log) - an official logging API for Swift, so it can be easly integrated into the project and combined with other logging backends. Log events are stored locally in the [JSON Lines](http://jsonlines.org) file format and bulk uploaded to GCP using the [Cloud Logging API v2](https://cloud.google.com/logging/docs/reference/v2/rest) at time intervals, upon defined event or explicit request. _And yes, it logs itself! (with recursion protection)_ 🤘
Event logging for client applications on [Apple platforms](#supported-platforms) with support for offline work and automatic upload to [Google Cloud (GCP)](https://cloud.google.com). The package depends on [SwiftLog](https://github.com/apple/swift-log) - an official logging API for Swift, so it can be easly integrated into the project and combined with other logging backends. Log events are stored locally in the [JSON Lines](http://jsonlines.org) file format and bulk uploaded to GCP using the [Cloud Logging API v2](https://cloud.google.com/logging/docs/reference/v2/rest) at time intervals, upon defined event or explicit request.

> And yes, it logs itself! (with recursion protection) 🤘
## Rationale
Google recommended logging solution for client applications is Analytics framework, which is now part of the Firebase SDK. Here is a comparison of that framework and this library in terms of logging:
Google-recommended logging solution for client applications is the Analytics framework, which is now part of the Firebase SDK. Here is a comparison of their framework and this library in terms of logging:
Library | FirebaseAnalytics | GoogleCloudLogging
--- | --- | ---
Platform | Mobile only. _Even Catalyst is not currently supported._ | All modern Apple's OSs. _It is essential for development of universal SwiftUI apps._
Expand All @@ -19,7 +21,9 @@ Logging | Proprietary logging functions and implicit usage tracking. | SwiftLog
Open your application project in Xcode 11 or later, go to menu `File -> Swift Packages -> Add Package Dependency...` and paste the package repository URL `https://github.com/DnV1eX/GoogleCloudLogging.git`.

### Create Service Account
In your web browser open the [Google Cloud Console](https://console.cloud.google.com) and create a new project. In `IAM & Admin -> Service Accounts` create a service account choosing `Logging -> Logs Writer` role. In the last step, create and download private key choosing `JSON` format. You need to include this file in your application bundle, just drag the file into the Xcode project and tick the desired targets in the file inspector.
In your web browser, open the [Google Cloud Console](https://console.cloud.google.com) and create a new project. In `IAM & Admin -> Service Accounts` create a service account choosing `Logging -> Logs Writer` role. In the last step, create and download private key choosing `JSON` format. You need to include this file in your application bundle.

> Just drag the file into the Xcode project and tick the desired targets in the file inspector.
### Setup Logging
1. Import both `SwiftLog` and `GoogleCloudLogging` modules:
Expand Down Expand Up @@ -74,7 +78,7 @@ logger.error(/* Logged error message */, metadata: [LogKey.error: "\(error)"])
> `GoogleCloudLogHandler.globalMetadata` takes precedence over `Logger` metadata which in turn takes precedence over log message metadata in case of key overlapping.
### Analyze Logs
In your web browser open the [GCP Operations Logging](https://console.cloud.google.com/logs) and select your project. You will see a list of logs for a given **time range** which can be filtered by **log name** _(logger label)_, **severity** _(log level)_, **text payload** _(message)_, **labels** _(metadata)_ etc. **Resource type** for logs produced by GoogleCloudLogHandler is always _Global_.
In your web browser, open the [GCP Operations Logging](https://console.cloud.google.com/logs) and select your project. You will see a list of logs for a given **time range** which can be filtered by **log name** _(logger label)_, **severity** _(log level)_, **text payload** _(message)_, **labels** _(metadata)_ etc. **Resource type** for logs produced by GoogleCloudLogHandler is always _Global_.

> You can switch to the new Logs Viewer Preview that introduces new features, such as advanced log queries and histograms.
Expand Down
23 changes: 17 additions & 6 deletions Sources/GoogleCloudLogging/GoogleCloudLogging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ class GoogleCloudLogging {
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601WithFractionalSeconds
encoder.dateEncodingStrategy = .iso8601WithNanoseconds
let entries: [Log.Entry] = entries.map {
var entry = $0
entry.logName = Log.name(projectId: self.serviceAccountCredentials.projectId, logId: $0.logName)
Expand Down Expand Up @@ -342,13 +342,25 @@ extension Data {



extension ISO8601DateFormatter {

static func internetDateTimeWithNanosecondsString(from date: Date, timeZone: TimeZone = TimeZone(secondsFromGMT: 0)!) -> String {
var string = ISO8601DateFormatter.string(from: date, timeZone: timeZone, formatOptions: .withInternetDateTime)
var timeInterval = date.timeIntervalSinceReferenceDate
if timeInterval < 0 {
timeInterval += (-timeInterval * 2).rounded(.up)
}
string.insert(contentsOf: "\(timeInterval)".drop { $0 != "." }.prefix(10), at: string.index(string.startIndex, offsetBy: 19))
return string
}
}


extension JSONEncoder.DateEncodingStrategy {

static let iso8601WithFractionalSeconds = custom { date, encoder in
let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
static let iso8601WithNanoseconds = custom { date, encoder in
var container = encoder.singleValueContainer()
try container.encode(dateFormatter.string(from: date))
try container.encode(ISO8601DateFormatter.internetDateTimeWithNanosecondsString(from: date))
}
}

Expand All @@ -369,7 +381,6 @@ extension CharacterSet {
}



extension String {

func safeLogId() -> String {
Expand Down
20 changes: 10 additions & 10 deletions Tests/GoogleCloudLoggingTests/GoogleCloudLoggingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ final class GoogleCloudLoggingTests: XCTestCase {
}


func testISO8601DateFormatterNanoseconds() {

XCTAssertEqual(ISO8601DateFormatter.internetDateTimeWithNanosecondsString(from: Date(timeIntervalSinceReferenceDate: 615695580)), "2020-07-06T02:33:00.0Z")
XCTAssertEqual(ISO8601DateFormatter.internetDateTimeWithNanosecondsString(from: Date(timeIntervalSinceReferenceDate: 615695580.235942)), "2020-07-06T02:33:00.235942Z")
XCTAssertEqual(ISO8601DateFormatter.internetDateTimeWithNanosecondsString(from: Date(timeIntervalSinceReferenceDate: 615695580.987654321)), "2020-07-06T02:33:00.9876543Z")
XCTAssertEqual(ISO8601DateFormatter.internetDateTimeWithNanosecondsString(from: Date(timeIntervalSinceReferenceDate: 0.987654321)), "2001-01-01T00:00:00.987654321Z")
XCTAssertEqual(ISO8601DateFormatter.internetDateTimeWithNanosecondsString(from: Date(timeIntervalSinceReferenceDate: -0.9876543211)), "2000-12-31T23:59:59.012345678Z")
}


func testSafeLogId() {

XCTAssertEqual("My_class-1.swift".safeLogId(), "My_class-1.swift")
Expand All @@ -97,14 +107,4 @@ final class GoogleCloudLoggingTests: XCTestCase {
logger.critical("LoggerMessage", metadata: ["MessageMetadataKey": "MessageMetadataValue"])
Thread.sleep(forTimeInterval: 3)
}


static var allTests = [
("testTokenRequest", testTokenRequest),
("testEntriesWrite", testEntriesWrite),
("testLogHandler", testLogHandler),
("testDictionaryUpdate", testDictionaryUpdate),
("testSafeLogId", testSafeLogId),
("testGoogleCloudLogHandler", testGoogleCloudLogHandler),
]
}
9 changes: 0 additions & 9 deletions Tests/GoogleCloudLoggingTests/XCTestManifests.swift

This file was deleted.

7 changes: 0 additions & 7 deletions Tests/LinuxMain.swift

This file was deleted.

0 comments on commit 287947b

Please sign in to comment.