Skip to content

Commit a3eb48a

Browse files
committed
Merge branch 'feat/frizlab/swift_5.1' into develop
2 parents f45df0b + 66b771b commit a3eb48a

File tree

9 files changed

+177
-43
lines changed

9 files changed

+177
-43
lines changed

Package.resolved

Lines changed: 22 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,21 @@
1-
// swift-tools-version:5.8
1+
// swift-tools-version:5.1
22
import PackageDescription
33

44

5-
let swiftSettings: [SwiftSetting] = [
6-
.enableExperimentalFeature("StrictConcurrency")
7-
]
8-
95
let package = Package(
106
name: "json-logger",
11-
platforms: [
12-
.macOS(.v11),
13-
.tvOS(.v14),
14-
.iOS(.v14),
15-
.watchOS(.v7),
16-
],
177
products: [
188
.library(name: "JSONLogger", targets: ["JSONLogger"])
199
],
2010
dependencies: [
2111
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.1"),
22-
.package(url: "https://github.com/Frizlab/generic-json.git", from: "3.0.0"),
12+
.package(url: "https://github.com/Frizlab/generic-json.git", from: "3.1.3"),
2313
],
2414
targets: [
2515
.target(name: "JSONLogger", dependencies: [
2616
.product(name: "GenericJSON", package: "generic-json"),
2717
.product(name: "Logging", package: "swift-log"),
28-
], path: "Sources", swiftSettings: swiftSettings),
29-
.testTarget(name: "JSONLoggerTests", dependencies: ["JSONLogger"], path: "Tests", swiftSettings: swiftSettings)
18+
], path: "Sources"),
19+
.testTarget(name: "JSONLoggerTests", dependencies: ["JSONLogger"]),
3020
]
3121
)

[email protected]

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// swift-tools-version:5.8
2+
import PackageDescription
3+
4+
5+
//let swiftSettings: [SwiftSetting] = []
6+
let swiftSettings: [SwiftSetting] = [.enableExperimentalFeature("StrictConcurrency")]
7+
8+
let package = Package(
9+
name: "json-logger",
10+
platforms: [
11+
.macOS(.v11),
12+
.tvOS(.v14),
13+
.iOS(.v14),
14+
.watchOS(.v7),
15+
],
16+
products: [
17+
.library(name: "JSONLogger", targets: ["JSONLogger"])
18+
],
19+
dependencies: [
20+
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.1"),
21+
.package(url: "https://github.com/Frizlab/generic-json.git", from: "3.1.3"),
22+
],
23+
targets: [
24+
.target(name: "JSONLogger", dependencies: [
25+
.product(name: "GenericJSON", package: "generic-json"),
26+
.product(name: "Logging", package: "swift-log"),
27+
], path: "Sources", swiftSettings: swiftSettings),
28+
.testTarget(name: "JSONLoggerTests", dependencies: ["JSONLogger"], swiftSettings: swiftSettings),
29+
]
30+
)

Sources/DummySendable.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Foundation
2+
3+
4+
#if swift(>=5.5)
5+
public protocol JSONLogger_Sendable : Sendable {}
6+
#else
7+
public protocol JSONLogger_Sendable {}
8+
#endif

Sources/JSONLogger.swift

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,17 @@ public struct JSONLogger : LogHandler {
3939

4040
public static let defaultJSONEncoder: JSONEncoder = {
4141
let res = JSONEncoder()
42+
#if swift(>=5.3)
4243
res.outputFormatting = [.withoutEscapingSlashes]
44+
#endif
4345
res.keyEncodingStrategy = .useDefaultKeys
4446
res.dateEncodingStrategy = .iso8601
4547
res.dataEncodingStrategy = .base64
4648
res.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "+inf", negativeInfinity: "-inf", nan: "nan")
4749
return res
4850
}()
4951

52+
#if swift(>=5.7)
5053
public static let defaultJSONCodersForStringConvertibles: (JSONEncoder, JSONDecoder) = {
5154
let encoder = JSONEncoder()
5255
encoder.outputFormatting = [.withoutEscapingSlashes]
@@ -72,6 +75,7 @@ public struct JSONLogger : LogHandler {
7275
decoder.nonConformingFloatDecodingStrategy = .throw
7376
return (encoder, decoder)
7477
}()
78+
#endif
7579

7680
public var logLevel: Logger.Level = .info
7781

@@ -88,11 +92,14 @@ public struct JSONLogger : LogHandler {
8892
public let suffix: Data
8993

9094
public let jsonEncoder: JSONEncoder
95+
#if swift(>=5.7)
9196
/**
9297
If non-`nil`, the `Encodable` stringConvertible properties in the metadata will be encoded as `JSON` using the `JSONEncoder` and `JSONDecoder`.
9398
If the encoding fails or this property is set to `nil` the String value will be used. */
9499
public let jsonCodersForStringConvertibles: (JSONEncoder, JSONDecoder)?
100+
#endif
95101

102+
#if swift(>=5.7)
96103
public static func forJSONSeq(
97104
on fh: FileHandle = .standardOutput,
98105
label: String,
@@ -129,6 +136,41 @@ public struct JSONLogger : LogHandler {
129136
self.metadataProvider = metadataProvider
130137
}
131138

139+
#else
140+
141+
public static func forJSONSeq(
142+
on fh: FileHandle = .standardOutput,
143+
label: String,
144+
jsonEncoder: JSONEncoder = Self.defaultJSONEncoder,
145+
metadataProvider: Logger.MetadataProvider? = LoggingSystem.metadataProvider
146+
) -> Self {
147+
return Self(
148+
label: label,
149+
fileHandle: fh,
150+
lineSeparator: Data(), prefix: Data([0x1e]), suffix: Data([0x0a]),
151+
jsonEncoder: jsonEncoder,
152+
metadataProvider: metadataProvider
153+
)
154+
}
155+
156+
public init(
157+
label: String,
158+
fileHandle: FileHandle = .standardOutput,
159+
lineSeparator: Data = Data(), prefix: Data = Data(), suffix: Data = Data("\n".utf8),
160+
jsonEncoder: JSONEncoder = Self.defaultJSONEncoder,
161+
metadataProvider: Logger.MetadataProvider? = LoggingSystem.metadataProvider
162+
) {
163+
self.label = label
164+
self.outputFileHandle = fileHandle
165+
self.lineSeparator = lineSeparator
166+
self.prefix = prefix
167+
self.suffix = suffix
168+
self.jsonEncoder = jsonEncoder
169+
170+
self.metadataProvider = metadataProvider
171+
}
172+
#endif
173+
132174
public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
133175
get {metadata[metadataKey]}
134176
set {metadata[metadataKey] = newValue}
@@ -206,7 +248,7 @@ extension JSONLogger {
206248
Merge the logger’s metadata, the provider’s metadata and the given explicit metadata and return the new metadata.
207249
If the provider’s metadata and the explicit metadata are `nil`, returns `nil` to signify the current `jsonMetadataCache` can be used. */
208250
private func mergedMetadata(with explicit: Logger.Metadata?) -> Logger.Metadata? {
209-
var metadata = metadata
251+
var metadata = self.metadata
210252
let provided = metadataProvider?.get() ?? [:]
211253

212254
guard !provided.isEmpty || !((explicit ?? [:]).isEmpty) else {
@@ -233,6 +275,8 @@ extension JSONLogger {
233275
case let .array(array): return .array (array .map (jsonMetadataValue(_:)))
234276
case let .dictionary(dictionary): return .object(dictionary.mapValues(jsonMetadataValue(_:)))
235277
case let .stringConvertible(s):
278+
/* Swift 5.7 and more. */
279+
#if swift(>=5.7)
236280
if let (encoder, decoder) = jsonCodersForStringConvertibles,
237281
let c = s as? any Encodable,
238282
let data = try? encoder.encode(c),
@@ -242,6 +286,9 @@ extension JSONLogger {
242286
} else {
243287
return .string(s.description)
244288
}
289+
#else
290+
return .string(s.description)
291+
#endif
245292
}
246293

247294
}

Sources/LogLine.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import Logging
55

66

77

8-
public struct LogLine : Hashable, Codable, Sendable {
8+
/* Technically Sendable is available starting at Swift 5.5, but swift-log was not ready at the time so we have to use Swift 5.6 instead. */
9+
#if swift(>=5.6)
10+
extension LogLine : JSONLogger_Sendable {}
11+
#endif
12+
public struct LogLine : Hashable, Codable {
913

1014
public var level: Logger.Level
1115
public var message: String

Sources/String+Utils.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import Foundation
55
internal extension String {
66

77
func safifyForJSON() -> String {
8-
let ascii = unicodeScalars.lazy.map{ scalar in
8+
let ascii = unicodeScalars.lazy.map{ scalar -> String in
9+
/* Note: The #""# syntax is available starting at Swift 5.4. */
910
switch scalar {
1011
case _ where !scalar.isASCII: return "-"
11-
case #"\"#: return #"\\"#
12-
case #"""#: return #"\""#
13-
case "\n": return #"\n"#
14-
case "\r": return #"\r"#
12+
case "\\": return "\\\\"
13+
case "\"": return "\\\""
14+
case "\n": return "\\n"
15+
case "\r": return "\\r"
1516
/* `scalar.value` should never be bigger than Int32.max, but we still use bitPattern not to crash if it is. */
1617
case _ where isprint(Int32(bitPattern: scalar.value)) == 0: return "-"
1718
default: return String(scalar)

Tests/JSONLoggerTests.swift renamed to Tests/JSONLoggerTests/JSONLoggerTests.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ final class JSONLoggerTests : XCTestCase {
6969
XCTAssertEqual(data.first, 0x0a)
7070
}
7171

72+
#if swift(>=5.7)
7273
func testEncodeMetadataAsJSON() throws {
7374
struct BestStruct : Encodable, CustomStringConvertible {
7475
var val: Int
@@ -81,12 +82,13 @@ final class JSONLoggerTests : XCTestCase {
8182
jsonLogger.log(level: ref.level, message: "\(ref.message)", metadata: ["yolo": .stringConvertible(BestStruct(val: 21))], source: ref.source, file: ref.file, function: ref.function, line: ref.line)
8283
try pipe.fileHandleForWriting.close()
8384
let data = try pipe.fileHandleForReading.readToEnd() ?? Data()
84-
print(data.reduce("", { $0 + String(format: "%02x", $1) }))
85+
//print(data.reduce("", { $0 + String(format: "%02x", $1) }))
8586
var line = try Self.defaultJSONDecoder.decode(LogLine.self, from: data)
8687
XCTAssertLessThanOrEqual(line.date.timeIntervalSince(ref.date), 0.1)
8788
line.date = ref.date
8889
XCTAssertEqual(line, ref)
8990
}
91+
#endif
9092

9193
func testFallbackOnLogLineEncodeFailure() throws {
9294
struct BestStruct : Encodable, CustomStringConvertible {
@@ -100,7 +102,7 @@ final class JSONLoggerTests : XCTestCase {
100102
]), date: Date(), label: "best-logger", source: "dummy-source", file: "dummy-file", function: "dummy-function", line: 42)
101103

102104
struct AnError : Error {}
103-
let failEncoder = {
105+
let failEncoder = { () -> JSONEncoder in
104106
let res = JSONEncoder()
105107
res.dateEncodingStrategy = .custom({ _, _ in throw AnError() })
106108
return res

Tests/LinuxMain.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import Foundation
2+
import XCTest
3+
4+
@testable import JSONLoggerTests
5+
6+
7+
8+
let tests_swift57_JSONLoggerTests: [(String, (JSONLoggerTests) -> () throws -> Void)]
9+
#if swift(>=5.7)
10+
tests_swift57_JSONLoggerTests = [
11+
("testEncodeMetadataAsJSON", JSONLoggerTests.testEncodeMetadataAsJSON),
12+
]
13+
#else
14+
tests_swift57_JSONLoggerTests = []
15+
#endif
16+
var tests: [XCTestCaseEntry] = [
17+
testCase([
18+
("test0NoSeparatorForFirstLog", JSONLoggerTests.test0NoSeparatorForFirstLog),
19+
("testFromDoc", JSONLoggerTests.testFromDoc),
20+
("testSeparatorForNotFirstLog", JSONLoggerTests.testSeparatorForNotFirstLog),
21+
("testFallbackOnLogLineEncodeFailure", JSONLoggerTests.testFallbackOnLogLineEncodeFailure),
22+
("testDecodeLogLineWithBothValidDateAndMangledDate", JSONLoggerTests.testDecodeLogLineWithBothValidDateAndMangledDate),
23+
("testDecodeLogLineWithBothInvalidDateAndMangledDate", JSONLoggerTests.testDecodeLogLineWithBothInvalidDateAndMangledDate),
24+
] + tests_swift57_JSONLoggerTests),
25+
]
26+
#if !os(WASI)
27+
XCTMain(tests)
28+
29+
#else
30+
/* Compilation fails for Swift <5.5… */
31+
//await XCTMain(tests)
32+
33+
/* Let’s print a message to inform the tests on WASI are disabled. */
34+
let brightRed = "\u{1B}[91;1m"
35+
let gray = "\u{1B}[38;5;245m"
36+
let magenta = "\u{1B}[35;1m"
37+
let reset = "\u{1B}[0m"
38+
try FileHandle.standardError.write(contentsOf: Data("""
39+
\(brightRed)Tests are disabled on WASI\(reset):
40+
\(gray)This package is compatible with Swift <5.4, so we have to add a LinuxMain file in which we call XCTMain.
41+
On WASI the XCTMain function is async, so we have to #if the XCTMain call, one with the await keyword, the other without.
42+
However, on Swift <5.5 the LinuxMain setup like this does not compile because the old compiler does not know the await keyword
43+
(even though the whole code is ignored because we do not compile for WASI whe compiling with an old compiler).
44+
I also tried doing a #if swift(>=5.5) check, but that do not work either.\(reset)
45+
46+
\(magenta)To temporarily enable the tests for WASI, uncomment the `await XCTMain(tests)` line in LinuxMain.swift.\(reset)
47+
48+
""".utf8))
49+
50+
#endif

0 commit comments

Comments
 (0)