diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..08891d83 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..44d6607d --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,28 @@ +name: Main + +on: + push: + branches: [main] + schedule: + - cron: "0 8,20 * * *" + +jobs: + unit-tests: + name: Unit tests + uses: apple/swift-nio/.github/workflows/unit_tests.yml@main + with: + linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + windows_6_0_enabled: true + windows_nightly_6_0_enabled: true + windows_nightly_main_enabled: true + windows_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + windows_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + windows_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + + cxx-interop: + name: Cxx interop + uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 00000000..fcf771b8 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,32 @@ +name: PR + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + soundness: + name: Soundness + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + license_header_check_project_name: "Swift Logging API" + + unit-tests: + name: Unit tests + uses: apple/swift-nio/.github/workflows/unit_tests.yml@main + with: + linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + windows_6_0_enabled: true + windows_nightly_6_0_enabled: true + windows_nightly_main_enabled: true + windows_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + windows_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + windows_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable" + + cxx-interop: + name: Cxx interop + uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml new file mode 100644 index 00000000..86f199f3 --- /dev/null +++ b/.github/workflows/pull_request_label.yml @@ -0,0 +1,18 @@ +name: PR label + +on: + pull_request: + types: [labeled, unlabeled, opened, reopened, synchronize] + +jobs: + semver-label-check: + name: Semantic Version label check + runs-on: ubuntu-latest + timeout-minutes: 1 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Check for Semantic Version label + uses: apple/swift-nio/.github/actions/pull_request_semver_label_checker@main diff --git a/.licenseignore b/.licenseignore new file mode 100644 index 00000000..e77f8dec --- /dev/null +++ b/.licenseignore @@ -0,0 +1,42 @@ +.gitignore +**/.gitignore +.licenseignore +.unacceptablelanguageignore +.gitattributes +.git-blame-ignore-revs +.mailfilter +.mailmap +.spi.yml +.swift-format +.editorconfig +.github/* +*.md +*.txt +*.yml +*.yaml +*.json +Package.swift +**/Package.swift +Package@-*.swift +**/Package@-*.swift +Package.resolved +**/Package.resolved +Makefile +*.modulemap +**/*.modulemap +**/*.docc/* +*.xcprivacy +**/*.xcprivacy +*.symlink +**/*.symlink +Dockerfile +**/Dockerfile +Snippets/* +dev/git.commit.template +dev/update-benchmark-thresholds +*.crt +**/*.crt +*.pem +**/*.pem +*.der +**/*.der diff --git a/.swift-format b/.swift-format new file mode 100644 index 00000000..7fa06fb3 --- /dev/null +++ b/.swift-format @@ -0,0 +1,62 @@ +{ + "version" : 1, + "indentation" : { + "spaces" : 4 + }, + "tabWidth" : 4, + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "spacesAroundRangeFormationOperators" : false, + "indentConditionalCompilationBlocks" : false, + "indentSwitchCaseLabels" : false, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : true, + "lineBreakBeforeEachGenericRequirement" : true, + "lineLength" : 120, + "maximumBlankLines" : 1, + "respectsExistingLineBreaks" : true, + "prioritizeKeepingFunctionOutputTogether" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : false, + "AmbiguousTrailingClosureOverload" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : true, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : false, + "UseLetInEveryBoundCaseVariable" : false, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : false, + "UseSynthesizedInitializer" : false, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + } +} diff --git a/.swiftformat b/.swiftformat deleted file mode 100644 index ff93c0ca..00000000 --- a/.swiftformat +++ /dev/null @@ -1,15 +0,0 @@ -# file options - ---swiftversion 5.0 ---exclude .build - -# format options - ---self insert ---patternlet inline ---stripunusedargs unnamed-only ---ifdef no-indent - -# rules - ---disable redundantParens # https://github.com/nicklockwood/SwiftFormat/issues/638 diff --git a/Package.swift b/Package.swift index 728b0342..c3c24a81 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ import PackageDescription let package = Package( name: "swift-log", products: [ - .library(name: "Logging", targets: ["Logging"]), + .library(name: "Logging", targets: ["Logging"]) ], targets: [ .target( diff --git a/README.md b/README.md index 68a4d81c..974cbf99 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,9 @@ public protocol LogHandler { Instructing `SwiftLog` to use your logging backend as the one the whole application (including all libraries) should use is very simple: - LoggingSystem.bootstrap(MyLogHandler.init) +```swift +LoggingSystem.bootstrap(MyLogHandler.init) +``` ### Implementation considerations @@ -211,7 +213,7 @@ Please see [SECURITY.md](SECURITY.md) for SwiftLog's security process. ## Design -This logging API was designed with the contributors to the Swift on Server community and approved by the [SSWG (Swift Server Work Group)](https://swift.org/server/) to the 'sandbox level' of the SSWG's [incubation process](https://github.com/swift-server/sswg/blob/master/process/incubation.md). +This logging API was designed with the contributors to the Swift on Server community and approved by the [SSWG (Swift Server Work Group)](https://swift.org/server/) to the 'sandbox level' of the SSWG's [incubation process](https://www.swift.org/sswg/incubation-process.html). - [pitch](https://forums.swift.org/t/logging/16027), [discussion](https://forums.swift.org/t/discussion-server-logging-api/18834), [feedback](https://forums.swift.org/t/feedback-server-logging-api-with-revisions/19375) - [log levels](https://forums.swift.org/t/logging-levels-for-swifts-server-side-logging-apis-and-new-os-log-apis/20365) diff --git a/SECURITY.md b/SECURITY.md index cc3fc838..6b9f283e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -38,6 +38,6 @@ with the all the required detail. and in the Server → Security Updates category on the [Swift forums][swift-forums-sec]. [sswg]: https://github.com/swift-server/sswg -[sswg-security]: https://github.com/swift-server/sswg/blob/main/security/README.md +[sswg-security]: https://www.swift.org/sswg/security/ [swift-forums-sec]: https://forums.swift.org/c/server/security-updates/ [mitre]: https://cveform.mitre.org/ diff --git a/Sources/Logging/Locks.swift b/Sources/Logging/Locks.swift index a4d062ad..1cc81fe0 100644 --- a/Sources/Logging/Locks.swift +++ b/Sources/Logging/Locks.swift @@ -34,6 +34,8 @@ import Darwin import WinSDK #elseif canImport(Glibc) import Glibc +#elseif canImport(Android) +import Android #elseif canImport(Musl) import Musl #else diff --git a/Sources/Logging/LogHandler.swift b/Sources/Logging/LogHandler.swift index 11944ae9..6f26ab9f 100644 --- a/Sources/Logging/LogHandler.swift +++ b/Sources/Logging/LogHandler.swift @@ -132,18 +132,27 @@ public protocol LogHandler: _SwiftLogSendableLogHandler { /// - file: The file the log message was emitted from. /// - function: The function the log line was emitted from. /// - line: The line the log message was emitted from. - func log(level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt) + func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) /// SwiftLog 1.0 compatibility method. Please do _not_ implement, implement /// `log(level:message:metadata:source:file:function:line:)` instead. @available(*, deprecated, renamed: "log(level:message:metadata:source:file:function:line:)") - func log(level: Logging.Logger.Level, message: Logging.Logger.Message, metadata: Logging.Logger.Metadata?, file: String, function: String, line: UInt) + func log( + level: Logging.Logger.Level, + message: Logging.Logger.Message, + metadata: Logging.Logger.Metadata?, + file: String, + function: String, + line: UInt + ) /// Add, remove, or change the logging metadata. /// @@ -179,7 +188,16 @@ extension LogHandler { set { #if DEBUG if LoggingSystem.warnOnceLogHandlerNotSupportedMetadataProvider(Self.self) { - self.log(level: .warning, message: "Attempted to set metadataProvider on \(Self.self) that did not implement support for them. Please contact the log handler maintainer to implement metadata provider support.", metadata: nil, source: "Logging", file: #file, function: #function, line: #line) + self.log( + level: .warning, + message: + "Attempted to set metadataProvider on \(Self.self) that did not implement support for them. Please contact the log handler maintainer to implement metadata provider support.", + metadata: nil, + source: "Logging", + file: #file, + function: #function, + line: #line + ) } #endif } @@ -188,25 +206,36 @@ extension LogHandler { extension LogHandler { @available(*, deprecated, message: "You should implement this method instead of using the default implementation") - public func log(level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt) { + public func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { self.log(level: level, message: message, metadata: metadata, file: file, function: function, line: line) } @available(*, deprecated, renamed: "log(level:message:metadata:source:file:function:line:)") - public func log(level: Logging.Logger.Level, message: Logging.Logger.Message, metadata: Logging.Logger.Metadata?, file: String, function: String, line: UInt) { - self.log(level: level, - message: message, - metadata: metadata, - source: Logger.currentModule(filePath: file), - file: file, - function: function, - line: line) + public func log( + level: Logging.Logger.Level, + message: Logging.Logger.Message, + metadata: Logging.Logger.Metadata?, + file: String, + function: String, + line: UInt + ) { + self.log( + level: level, + message: message, + metadata: metadata, + source: Logger.currentModule(filePath: file), + file: file, + function: function, + line: line + ) } } diff --git a/Sources/Logging/Logging.swift b/Sources/Logging/Logging.swift index 04ef0b1a..2867a4c2 100644 --- a/Sources/Logging/Logging.swift +++ b/Sources/Logging/Logging.swift @@ -22,6 +22,8 @@ import CRT #else import Glibc #endif +#elseif canImport(Android) +import Android #elseif canImport(Musl) import Musl #elseif canImport(WASILibc) @@ -33,7 +35,7 @@ import WASILibc /// A `Logger` is the central type in `SwiftLog`. Its central function is to emit log messages using one of the methods /// corresponding to a log level. /// -/// `Logger`s are value types with respect to the ``logLevel`` and the ``metadata`` (as well as the immutable `label` +/// `Logger`s are value types with respect to the ``Logger/Level`` and the ``Metadata`` (as well as the immutable `label` /// and the selected ``LogHandler``). Therefore, `Logger`s are suitable to be passed around between libraries if you want /// to preserve metadata across libraries. /// @@ -44,8 +46,9 @@ import WASILibc /// ``` public struct Logger { /// Storage class to hold the label and log handler + // The storage implements CoW to become Sendable @usableFromInline - internal final class Storage: @unchecked /* and not actually */ Sendable /* but safe if only used with CoW */ { + internal final class Storage: @unchecked Sendable { @usableFromInline var label: String @@ -60,21 +63,21 @@ public struct Logger { @inlinable func copy() -> Storage { - return Storage(label: self.label, handler: self.handler) + Storage(label: self.label, handler: self.handler) } } @usableFromInline internal var _storage: Storage public var label: String { - return self._storage.label + self._storage.label } /// A computed property to access the `LogHandler`. @inlinable public var handler: any LogHandler { get { - return self._storage.handler + self._storage.handler } set { if !isKnownUniquelyReferenced(&self._storage) { @@ -87,7 +90,7 @@ public struct Logger { /// The metadata provider this logger was created with. @inlinable public var metadataProvider: Logger.MetadataProvider? { - return self.handler.metadataProvider + self.handler.metadataProvider } @usableFromInline @@ -115,17 +118,25 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func log(level: Logger.Level, - _ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - source: @autoclosure () -> String? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func log( + level: Logger.Level, + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { if self.logLevel <= level { - self.handler.log(level: level, - message: message(), - metadata: metadata(), - source: source() ?? Logger.currentModule(fileID: (file)), - file: file, function: function, line: line) + self.handler.log( + level: level, + message: message(), + metadata: metadata(), + source: source() ?? Logger.currentModule(fileID: (file)), + file: file, + function: function, + line: line + ) } } @@ -145,10 +156,14 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func log(level: Logger.Level, - _ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func log( + level: Logger.Level, + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.log(level: level, message(), metadata: metadata(), source: nil, file: file, function: function, line: line) } @@ -159,7 +174,7 @@ extension Logger { @inlinable public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { get { - return self.handler[metadataKey: metadataKey] + self.handler[metadataKey: metadataKey] } set { self.handler[metadataKey: metadataKey] = newValue @@ -175,7 +190,7 @@ extension Logger { @inlinable public var logLevel: Logger.Level { get { - return self.handler.logLevel + self.handler.logLevel } set { self.handler.logLevel = newValue @@ -201,11 +216,23 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func trace(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - source: @autoclosure () -> String? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { - self.log(level: .trace, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + public func trace( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.log( + level: .trace, + message(), + metadata: metadata(), + source: source(), + file: file, + function: function, + line: line + ) } /// Log a message passing with the ``Logger/Level/trace`` log level. @@ -223,9 +250,13 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func trace(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func trace( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.trace(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) } @@ -246,11 +277,23 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func debug(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - source: @autoclosure () -> String? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { - self.log(level: .debug, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + public func debug( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.log( + level: .debug, + message(), + metadata: metadata(), + source: source(), + file: file, + function: function, + line: line + ) } /// Log a message passing with the ``Logger/Level/debug`` log level. @@ -268,9 +311,13 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func debug(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func debug( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.debug(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) } @@ -291,11 +338,23 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func info(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - source: @autoclosure () -> String? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { - self.log(level: .info, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + public func info( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.log( + level: .info, + message(), + metadata: metadata(), + source: source(), + file: file, + function: function, + line: line + ) } /// Log a message passing with the ``Logger/Level/info`` log level. @@ -313,9 +372,13 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func info(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func info( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.info(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) } @@ -336,11 +399,23 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func notice(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - source: @autoclosure () -> String? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { - self.log(level: .notice, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + public func notice( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.log( + level: .notice, + message(), + metadata: metadata(), + source: source(), + file: file, + function: function, + line: line + ) } /// Log a message passing with the ``Logger/Level/notice`` log level. @@ -358,9 +433,13 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func notice(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func notice( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.notice(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) } @@ -381,11 +460,23 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func warning(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - source: @autoclosure () -> String? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { - self.log(level: .warning, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + public func warning( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.log( + level: .warning, + message(), + metadata: metadata(), + source: source(), + file: file, + function: function, + line: line + ) } /// Log a message passing with the ``Logger/Level/warning`` log level. @@ -403,9 +494,13 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func warning(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func warning( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.warning(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) } @@ -426,11 +521,23 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func error(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - source: @autoclosure () -> String? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { - self.log(level: .error, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + public func error( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.log( + level: .error, + message(), + metadata: metadata(), + source: source(), + file: file, + function: function, + line: line + ) } /// Log a message passing with the ``Logger/Level/error`` log level. @@ -448,9 +555,13 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func error(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func error( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.error(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) } @@ -470,11 +581,23 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func critical(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - source: @autoclosure () -> String? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { - self.log(level: .critical, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) + public func critical( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + source: @autoclosure () -> String? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.log( + level: .critical, + message(), + metadata: metadata(), + source: source(), + file: file, + function: function, + line: line + ) } /// Log a message passing with the ``Logger/Level/critical`` log level. @@ -491,9 +614,13 @@ extension Logger { /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it /// defaults to `#line`). @inlinable - public func critical(_ message: @autoclosure () -> Logger.Message, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func critical( + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.critical(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) } } @@ -502,9 +629,14 @@ extension Logger { /// configured. `LoggingSystem` is set up just once in a given program to set up the desired logging backend /// implementation. public enum LoggingSystem { - private static let _factory = FactoryBox({ label, _ in StreamLogHandler.standardError(label: label) }, - violationErrorMesage: "logging system can only be initialized once per process.") - private static let _metadataProviderFactory = MetadataProviderBox(nil, violationErrorMesage: "logging system can only be initialized once per process.") + private static let _factory = FactoryBox( + { label, _ in StreamLogHandler.standardError(label: label) }, + violationErrorMesage: "logging system can only be initialized once per process." + ) + private static let _metadataProviderFactory = MetadataProviderBox( + nil, + violationErrorMesage: "logging system can only be initialized once per process." + ) #if DEBUG private static let _warnOnceBox: WarnOnceBox = WarnOnceBox() @@ -517,10 +649,13 @@ public enum LoggingSystem { /// - parameters: /// - factory: A closure that given a `Logger` identifier, produces an instance of the `LogHandler`. @preconcurrency - public static func bootstrap(_ factory: @escaping @Sendable(String) -> any LogHandler) { - self._factory.replace({ label, _ in - factory(label) - }, validate: true) + public static func bootstrap(_ factory: @escaping @Sendable (String) -> any LogHandler) { + self._factory.replace( + { label, _ in + factory(label) + }, + validate: true + ) } /// `bootstrap` is a one-time configuration function which globally selects the desired logging backend @@ -534,29 +669,36 @@ public enum LoggingSystem { /// - metadataProvider: The `MetadataProvider` used to inject runtime-generated metadata from the execution context. /// - factory: A closure that given a `Logger` identifier, produces an instance of the `LogHandler`. @preconcurrency - public static func bootstrap(_ factory: @escaping @Sendable(String, Logger.MetadataProvider?) -> any LogHandler, - metadataProvider: Logger.MetadataProvider?) { + public static func bootstrap( + _ factory: @escaping @Sendable (String, Logger.MetadataProvider?) -> any LogHandler, + metadataProvider: Logger.MetadataProvider? + ) { self._metadataProviderFactory.replace(metadataProvider, validate: true) self._factory.replace(factory, validate: true) } // for our testing we want to allow multiple bootstrapping - internal static func bootstrapInternal(_ factory: @escaping @Sendable(String) -> any LogHandler) { + internal static func bootstrapInternal(_ factory: @escaping @Sendable (String) -> any LogHandler) { self._metadataProviderFactory.replace(nil, validate: false) - self._factory.replace({ label, _ in - factory(label) - }, validate: false) + self._factory.replace( + { label, _ in + factory(label) + }, + validate: false + ) } // for our testing we want to allow multiple bootstrapping - internal static func bootstrapInternal(_ factory: @escaping @Sendable(String, Logger.MetadataProvider?) -> any LogHandler, - metadataProvider: Logger.MetadataProvider?) { + internal static func bootstrapInternal( + _ factory: @escaping @Sendable (String, Logger.MetadataProvider?) -> any LogHandler, + metadataProvider: Logger.MetadataProvider? + ) { self._metadataProviderFactory.replace(metadataProvider, validate: false) self._factory.replace(factory, validate: false) } fileprivate static var factory: (String, Logger.MetadataProvider?) -> any LogHandler { - return { label, metadataProvider in + { label, metadataProvider in self._factory.underlying(label, metadataProvider) } } @@ -571,14 +713,16 @@ public enum LoggingSystem { /// factory to avoid using the bootstrapped metadata provider may sometimes be useful, usually it will lead to /// un-expected behavior, so make sure to always propagate it to your handlers. public static var metadataProvider: Logger.MetadataProvider? { - return self._metadataProviderFactory.underlying + self._metadataProviderFactory.underlying } #if DEBUG /// Used to warn only once about a specific ``LogHandler`` type when it does not support ``Logger/MetadataProvider``, /// but an attempt was made to set a metadata provider on such handler. In order to avoid flooding the system with /// warnings such warning is only emitted in debug mode, and even then at-most once for a handler type. - internal static func warnOnceLogHandlerNotSupportedMetadataProvider(_ type: Handler.Type) -> Bool { + internal static func warnOnceLogHandlerNotSupportedMetadataProvider( + _ type: Handler.Type + ) -> Bool { self._warnOnceBox.warnOnceLogHandlerNotSupportedMetadataProvider(type: type) } #endif @@ -620,7 +764,7 @@ public enum LoggingSystem { } var underlying: BoxedType { - return self._underlying + self._underlying } init(underlying: BoxedType, violationErrorMessage: String) { @@ -632,8 +776,12 @@ public enum LoggingSystem { private let storage: RWLockedValueBox init(_ underlying: BoxedType, violationErrorMesage: String) { - self.storage = .init(initialValue: ReplaceOnce(underlying: underlying, - violationErrorMessage: violationErrorMesage)) + self.storage = .init( + initialValue: ReplaceOnce( + underlying: underlying, + violationErrorMessage: violationErrorMesage + ) + ) } func replace(_ newUnderlying: BoxedType, validate: Bool) { @@ -645,7 +793,9 @@ public enum LoggingSystem { } } - private typealias FactoryBox = ReplaceOnceBox< @Sendable(_ label: String, _ provider: Logger.MetadataProvider?) -> any LogHandler> + private typealias FactoryBox = ReplaceOnceBox< + @Sendable (_ label: String, _ provider: Logger.MetadataProvider?) -> any LogHandler + > private typealias MetadataProviderBox = ReplaceOnceBox } @@ -780,11 +930,14 @@ extension Logger { /// - metadataProvider: The custom metadata provider this logger should invoke, /// instead of the system wide bootstrapped one, when a log statement is about to be emitted. public init(label: String, metadataProvider: MetadataProvider) { - self = Logger(label: label, factory: { label in - var handler = LoggingSystem.factory(label, metadataProvider) - handler.metadataProvider = metadataProvider - return handler - }) + self = Logger( + label: label, + factory: { label in + var handler = LoggingSystem.factory(label, metadataProvider) + handler.metadataProvider = metadataProvider + return handler + } + ) } } @@ -811,7 +964,7 @@ extension Logger.Level { extension Logger.Level: Comparable { public static func < (lhs: Logger.Level, rhs: Logger.Level) -> Bool { - return lhs.naturalIntegralValue < rhs.naturalIntegralValue + lhs.naturalIntegralValue < rhs.naturalIntegralValue } } @@ -848,7 +1001,9 @@ extension Logger { /// /// logger.info("Hello \(world)") /// - public struct Message: ExpressibleByStringLiteral, Equatable, CustomStringConvertible, ExpressibleByStringInterpolation { + public struct Message: ExpressibleByStringLiteral, Equatable, CustomStringConvertible, + ExpressibleByStringInterpolation + { public typealias StringLiteralType = String private var value: String @@ -858,7 +1013,7 @@ extension Logger { } public var description: String { - return self.value + self.value } } } @@ -934,7 +1089,7 @@ public struct MultiplexLogHandler: LogHandler { public var logLevel: Logger.Level { get { - return self.effectiveLogLevel + self.effectiveLogLevel } set { self.mutatingForEachHandler { $0.logLevel = newValue } @@ -979,15 +1134,25 @@ public struct MultiplexLogHandler: LogHandler { } } - public func log(level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt) { + public func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { for handler in self.handlers where handler.logLevel <= level { - handler.log(level: level, message: message, metadata: metadata, source: source, file: file, function: function, line: line) + handler.log( + level: level, + message: message, + metadata: metadata, + source: source, + file: file, + function: function, + line: line + ) } } @@ -996,7 +1161,9 @@ public struct MultiplexLogHandler: LogHandler { var effective: Logger.Metadata = [:] // as a rough estimate we assume that the underlying handlers have a similar metadata count, // and we use the first one's current count to estimate how big of a dictionary we need to allocate: - effective.reserveCapacity(self.handlers.first!.metadata.count) // !-safe, we always have at least one handler + + // !-safe, we always have at least one handler + effective.reserveCapacity(self.handlers.first!.metadata.count) for handler in self.handlers { effective.merge(handler.metadata, uniquingKeysWith: { _, handlerMetadata in handlerMetadata }) @@ -1094,6 +1261,8 @@ internal struct StdioOutputStream: TextOutputStream, @unchecked Sendable { let systemStderr = CRT.stderr #elseif canImport(Glibc) let systemStderr = Glibc.stderr! + #elseif canImport(Android) + let systemStderr = Android.stderr #elseif canImport(Musl) let systemStderr = Musl.stderr! #elseif canImport(WASILibc) @@ -1112,6 +1281,8 @@ internal struct StdioOutputStream: TextOutputStream, @unchecked Sendable { let systemStdout = CRT.stdout #elseif canImport(Glibc) let systemStdout = Glibc.stdout! + #elseif canImport(Android) + let systemStdout = Android.stdout #elseif canImport(Musl) let systemStdout = Musl.stdout! #elseif canImport(WASILibc) @@ -1141,22 +1312,30 @@ public struct StreamLogHandler: LogHandler { /// Factory that makes a `StreamLogHandler` to directs its output to `stdout` public static func standardOutput(label: String) -> StreamLogHandler { - return StreamLogHandler(label: label, stream: StdioOutputStream.stdout, metadataProvider: LoggingSystem.metadataProvider) + StreamLogHandler( + label: label, + stream: StdioOutputStream.stdout, + metadataProvider: LoggingSystem.metadataProvider + ) } /// Factory that makes a `StreamLogHandler` that directs its output to `stdout` public static func standardOutput(label: String, metadataProvider: Logger.MetadataProvider?) -> StreamLogHandler { - return StreamLogHandler(label: label, stream: StdioOutputStream.stdout, metadataProvider: metadataProvider) + StreamLogHandler(label: label, stream: StdioOutputStream.stdout, metadataProvider: metadataProvider) } /// Factory that makes a `StreamLogHandler` that directs its output to `stderr` public static func standardError(label: String) -> StreamLogHandler { - return StreamLogHandler(label: label, stream: StdioOutputStream.stderr, metadataProvider: LoggingSystem.metadataProvider) + StreamLogHandler( + label: label, + stream: StdioOutputStream.stderr, + metadataProvider: LoggingSystem.metadataProvider + ) } /// Factory that makes a `StreamLogHandler` that direct its output to `stderr` public static func standardError(label: String, metadataProvider: Logger.MetadataProvider?) -> StreamLogHandler { - return StreamLogHandler(label: label, stream: StdioOutputStream.stderr, metadataProvider: metadataProvider) + StreamLogHandler(label: label, stream: StdioOutputStream.stderr, metadataProvider: metadataProvider) } private let stream: any _SendableTextOutputStream @@ -1175,7 +1354,7 @@ public struct StreamLogHandler: LogHandler { public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { get { - return self.metadata[metadataKey] + self.metadata[metadataKey] } set { self.metadata[metadataKey] = newValue @@ -1194,14 +1373,20 @@ public struct StreamLogHandler: LogHandler { self.metadataProvider = metadataProvider } - public func log(level: Logger.Level, - message: Logger.Message, - metadata explicitMetadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt) { - let effectiveMetadata = StreamLogHandler.prepareMetadata(base: self.metadata, provider: self.metadataProvider, explicit: explicitMetadata) + public func log( + level: Logger.Level, + message: Logger.Message, + metadata explicitMetadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { + let effectiveMetadata = StreamLogHandler.prepareMetadata( + base: self.metadata, + provider: self.metadataProvider, + explicit: explicitMetadata + ) let prettyMetadata: String? if let effectiveMetadata = effectiveMetadata { @@ -1211,10 +1396,16 @@ public struct StreamLogHandler: LogHandler { } var stream = self.stream - stream.write("\(self.timestamp()) \(level) \(self.label) :\(prettyMetadata.map { " \($0)" } ?? "") [\(source)] \(message)\n") + stream.write( + "\(self.timestamp()) \(level) \(self.label) :\(prettyMetadata.map { " \($0)" } ?? "") [\(source)] \(message)\n" + ) } - internal static func prepareMetadata(base: Logger.Metadata, provider: Logger.MetadataProvider?, explicit: Logger.Metadata?) -> Logger.Metadata? { + internal static func prepareMetadata( + base: Logger.Metadata, + provider: Logger.MetadataProvider?, + explicit: Logger.Metadata? + ) -> Logger.Metadata? { var metadata = base let provided = provider?.get() ?? [:] @@ -1274,33 +1465,42 @@ public struct SwiftLogNoOpLogHandler: LogHandler { public init(_: String) {} - @inlinable public func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) {} - - public func log(level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt) {} + @inlinable public func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + file: String, + function: String, + line: UInt + ) {} + + public func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) {} @inlinable public subscript(metadataKey _: String) -> Logger.Metadata.Value? { get { - return nil + nil } set {} } @inlinable public var metadata: Logger.Metadata { get { - return [:] + [:] } set {} } @inlinable public var logLevel: Logger.Level { get { - return .critical + .critical } set {} } @@ -1312,7 +1512,7 @@ extension Logger { let utf8All = filePath.utf8 return filePath.utf8.lastIndex(of: UInt8(ascii: "/")).flatMap { lastSlash -> Substring? in utf8All[.. Substring in - filePath[utf8All.index(after: secondLastSlash) ..< lastSlash] + filePath[utf8All.index(after: secondLastSlash).. Metadata + internal let _provideMetadata: @Sendable () -> Metadata /// Create a new `MetadataProvider`. /// /// - Parameter provideMetadata: A closure extracting metadata from the current execution context. - public init(_ provideMetadata: @escaping @Sendable() -> Metadata) { + public init(_ provideMetadata: @escaping @Sendable () -> Metadata) { self._provideMetadata = provideMetadata } /// Invoke the metadata provider and return the generated contextual ``Logger/Metadata``. public func get() -> Metadata { - return self._provideMetadata() + self._provideMetadata() } } } diff --git a/Tests/LoggingTests/CompatibilityTest.swift b/Tests/LoggingTests/CompatibilityTest.swift index e6492f6a..732310d9 100644 --- a/Tests/LoggingTests/CompatibilityTest.swift +++ b/Tests/LoggingTests/CompatibilityTest.swift @@ -12,7 +12,8 @@ // //===----------------------------------------------------------------------===// -/* NOT @testable */ import Logging +// Not testable +import Logging import XCTest final class CompatibilityTest: XCTestCase { @@ -43,20 +44,22 @@ final class CompatibilityTest: XCTestCase { } private struct OldSchoolTestLogging { - private let _config = Config() // shared among loggers - private let recorder = Recorder() // shared among loggers + private let _config = Config() // shared among loggers + private let recorder = Recorder() // shared among loggers @available(*, deprecated, message: "Testing deprecated functionality") func make(label: String) -> any LogHandler { - return OldSchoolLogHandler(label: label, - config: self.config, - recorder: self.recorder, - metadata: [:], - logLevel: .info) + OldSchoolLogHandler( + label: label, + config: self.config, + recorder: self.recorder, + metadata: [:], + logLevel: .info + ) } - var config: Config { return self._config } - var history: some History { return self.recorder } + var config: Config { self._config } + var history: some History { self.recorder } } @available(*, deprecated, message: "Testing deprecated functionality") @@ -66,16 +69,23 @@ private struct OldSchoolLogHandler: LogHandler { let recorder: Recorder func make(label: String) -> some LogHandler { - return TestLogHandler(label: label, config: self.config, recorder: self.recorder) + TestLogHandler(label: label, config: self.config, recorder: self.recorder) } - func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) { + func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + file: String, + function: String, + line: UInt + ) { self.recorder.record(level: level, metadata: metadata, message: message, source: "no source") } subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { get { - return self.metadata[metadataKey] + self.metadata[metadataKey] } set { self.metadata[metadataKey] = newValue diff --git a/Tests/LoggingTests/GlobalLoggingTest.swift b/Tests/LoggingTests/GlobalLoggingTest.swift index 72a12d54..68d011c3 100644 --- a/Tests/LoggingTests/GlobalLoggingTest.swift +++ b/Tests/LoggingTests/GlobalLoggingTest.swift @@ -11,8 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// -@testable import Logging + import XCTest + +@testable import Logging + #if compiler(>=6.0) || canImport(Darwin) import Dispatch #else @@ -63,10 +66,18 @@ class GlobalLoggerTest: XCTestCase { logging.history.assertNotExist(level: .info, message: "Struct2::doSomethingElse") logging.history.assertExist(level: .error, message: "Struct3::doSomething") logging.history.assertExist(level: .error, message: "Struct3::doSomethingElse", metadata: ["foo": "bar"]) - logging.history.assertNotExist(level: .warning, message: "Struct3::doSomethingElseAsync", metadata: ["foo": "bar"]) + logging.history.assertNotExist( + level: .warning, + message: "Struct3::doSomethingElseAsync", + metadata: ["foo": "bar"] + ) logging.history.assertNotExist(level: .info, message: "TestLibrary::doSomething", metadata: ["foo": "bar"]) logging.history.assertNotExist(level: .info, message: "TestLibrary::doSomethingAsync", metadata: ["foo": "bar"]) - logging.history.assertNotExist(level: .debug, message: "Struct3::doSomethingElse::Local", metadata: ["baz": "qux"]) + logging.history.assertNotExist( + level: .debug, + message: "Struct3::doSomethingElse::Local", + metadata: ["baz": "qux"] + ) logging.history.assertNotExist(level: .debug, message: "Struct3::doSomethingElse::end") logging.history.assertNotExist(level: .debug, message: "Struct3::doSomething::end") logging.history.assertNotExist(level: .debug, message: "Struct2::doSomethingElse::end") @@ -95,7 +106,11 @@ class GlobalLoggerTest: XCTestCase { logging.history.assertExist(level: .warning, message: "Struct3::doSomethingElseAsync", metadata: ["foo": "bar"]) logging.history.assertExist(level: .info, message: "TestLibrary::doSomething", metadata: ["foo": "bar"]) logging.history.assertExist(level: .info, message: "TestLibrary::doSomethingAsync", metadata: ["foo": "bar"]) - logging.history.assertNotExist(level: .debug, message: "Struct3::doSomethingElse::Local", metadata: ["baz": "qux"]) + logging.history.assertNotExist( + level: .debug, + message: "Struct3::doSomethingElse::Local", + metadata: ["baz": "qux"] + ) logging.history.assertNotExist(level: .debug, message: "Struct3::doSomethingElse::end") logging.history.assertNotExist(level: .debug, message: "Struct3::doSomething::end") logging.history.assertNotExist(level: .debug, message: "Struct2::doSomethingElse::end") diff --git a/Tests/LoggingTests/LocalLoggingTest.swift b/Tests/LoggingTests/LocalLoggingTest.swift index 87cb37f7..4233800f 100644 --- a/Tests/LoggingTests/LocalLoggingTest.swift +++ b/Tests/LoggingTests/LocalLoggingTest.swift @@ -11,8 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// -@testable import Logging + import XCTest + +@testable import Logging + #if compiler(>=6.0) || canImport(Darwin) import Dispatch #else @@ -40,7 +43,11 @@ class LocalLoggerTest: XCTestCase { logging.history.assertExist(level: .warning, message: "Struct3::doSomethingElseAsync", metadata: ["bar": "baz"]) logging.history.assertExist(level: .info, message: "TestLibrary::doSomething") logging.history.assertExist(level: .info, message: "TestLibrary::doSomethingAsync") - logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::Local", metadata: ["bar": "baz", "baz": "qux"]) + logging.history.assertExist( + level: .debug, + message: "Struct3::doSomethingElse::Local", + metadata: ["bar": "baz", "baz": "qux"] + ) logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::end", metadata: ["bar": "baz"]) logging.history.assertExist(level: .debug, message: "Struct3::doSomething::end", metadata: ["bar": "baz"]) logging.history.assertExist(level: .debug, message: "Struct2::doSomethingElse::end") @@ -59,21 +66,40 @@ class LocalLoggerTest: XCTestCase { let context = Context() Struct1().doSomething(context: context) // test results - logging.history.assertNotExist(level: .debug, message: "Struct1::doSomething") // global context - logging.history.assertNotExist(level: .debug, message: "Struct1::doSomethingElse") // global context - logging.history.assertExist(level: .info, message: "Struct2::doSomething") // local context - logging.history.assertExist(level: .info, message: "Struct2::doSomethingElse") // local context - logging.history.assertExist(level: .error, message: "Struct3::doSomething", metadata: ["bar": "baz"]) // local context - logging.history.assertExist(level: .error, message: "Struct3::doSomethingElse", metadata: ["bar": "baz"]) // local context - logging.history.assertExist(level: .warning, message: "Struct3::doSomethingElseAsync", metadata: ["bar": "baz"]) // local context - logging.history.assertNotExist(level: .info, message: "TestLibrary::doSomething") // global context - logging.history.assertNotExist(level: .info, message: "TestLibrary::doSomethingAsync") // global context - logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::Local", metadata: ["bar": "baz", "baz": "qux"]) // hyper local context - logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::end", metadata: ["bar": "baz"]) // local context - logging.history.assertExist(level: .debug, message: "Struct2::doSomethingElse::end") // local context - logging.history.assertExist(level: .debug, message: "Struct3::doSomething::end", metadata: ["bar": "baz"]) // local context - logging.history.assertNotExist(level: .debug, message: "Struct1::doSomethingElse::end") // global context - logging.history.assertNotExist(level: .debug, message: "Struct1::doSomething::end") // global context + // global context + logging.history.assertNotExist(level: .debug, message: "Struct1::doSomething") + // global context + logging.history.assertNotExist(level: .debug, message: "Struct1::doSomethingElse") + // local context + logging.history.assertExist(level: .info, message: "Struct2::doSomething") + // local context + logging.history.assertExist(level: .info, message: "Struct2::doSomethingElse") + // local context + logging.history.assertExist(level: .error, message: "Struct3::doSomething", metadata: ["bar": "baz"]) + // local context + logging.history.assertExist(level: .error, message: "Struct3::doSomethingElse", metadata: ["bar": "baz"]) + // local context + logging.history.assertExist(level: .warning, message: "Struct3::doSomethingElseAsync", metadata: ["bar": "baz"]) + // global context + logging.history.assertNotExist(level: .info, message: "TestLibrary::doSomething") + // global context + logging.history.assertNotExist(level: .info, message: "TestLibrary::doSomethingAsync") + // hyper local context + logging.history.assertExist( + level: .debug, + message: "Struct3::doSomethingElse::Local", + metadata: ["bar": "baz", "baz": "qux"] + ) + // local context + logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::end", metadata: ["bar": "baz"]) + // local context + logging.history.assertExist(level: .debug, message: "Struct2::doSomethingElse::end") + // local context + logging.history.assertExist(level: .debug, message: "Struct3::doSomething::end", metadata: ["bar": "baz"]) + // global context + logging.history.assertNotExist(level: .debug, message: "Struct1::doSomethingElse::end") + // global context + logging.history.assertNotExist(level: .debug, message: "Struct1::doSomething::end") } } @@ -83,13 +109,13 @@ private struct Context { // since logger is a value type, we can reuse our copy to manage logLevel var logLevel: Logger.Level { - get { return self.logger.logLevel } + get { self.logger.logLevel } set { self.logger.logLevel = newValue } } // since logger is a value type, we can reuse our copy to manage metadata subscript(metadataKey: String) -> Logger.Metadata.Value? { - get { return self.logger[metadataKey: metadataKey] } + get { self.logger[metadataKey: metadataKey] } set { self.logger[metadataKey: metadataKey] = newValue } } } @@ -114,7 +140,7 @@ private struct Struct1 { private struct Struct2 { func doSomething(context: Context) { var c = context - c.logLevel = .info // only effects from this point on + c.logLevel = .info // only effects from this point on c.logger.info("Struct2::doSomething") self.doSomethingElse(context: c) c.logger.debug("Struct2::doSomething::end") @@ -122,7 +148,7 @@ private struct Struct2 { private func doSomethingElse(context: Context) { var c = context - c.logLevel = .debug // only effects from this point on + c.logLevel = .debug // only effects from this point on c.logger.info("Struct2::doSomethingElse") Struct3().doSomething(context: c) c.logger.debug("Struct2::doSomethingElse::end") @@ -134,7 +160,7 @@ private struct Struct3 { func doSomething(context: Context) { var c = context - c["bar"] = "baz" // only effects from this point on + c["bar"] = "baz" // only effects from this point on c.logger.error("Struct3::doSomething") self.doSomethingElse(context: c) c.logger.debug("Struct3::doSomething::end") diff --git a/Tests/LoggingTests/LoggingTest.swift b/Tests/LoggingTests/LoggingTest.swift index f77b9dfe..141de3dc 100644 --- a/Tests/LoggingTests/LoggingTest.swift +++ b/Tests/LoggingTests/LoggingTest.swift @@ -11,9 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// -@testable import Logging + import XCTest +@testable import Logging + #if canImport(Darwin) import Darwin #elseif os(Windows) @@ -22,14 +24,14 @@ import WinSDK import Glibc #endif -private extension LogHandler { - func with(logLevel: Logger.Level) -> any LogHandler { +extension LogHandler { + fileprivate func with(logLevel: Logger.Level) -> any LogHandler { var result = self result.logLevel = logLevel return result } - func withMetadata(_ key: String, _ value: Logger.MetadataValue) -> any LogHandler { + fileprivate func withMetadata(_ key: String, _ value: Logger.MetadataValue) -> any LogHandler { var result = self result.metadata[key] = value return result @@ -44,27 +46,40 @@ class LoggingTest: XCTestCase { var logger = Logger(label: "test") logger.logLevel = .info - logger.log(level: .debug, { - XCTFail("debug should not be called") - return "debug" - }()) - logger.trace({ - XCTFail("trace should not be called") - return "trace" - }()) - logger.debug({ - XCTFail("debug should not be called") - return "debug" - }()) - logger.info({ - "info" - }()) - logger.warning({ - "warning" - }()) - logger.error({ - "error" - }()) + logger.log( + level: .debug, + { + XCTFail("debug should not be called") + return "debug" + }() + ) + logger.trace( + { + XCTFail("trace should not be called") + return "trace" + }() + ) + logger.debug( + { + XCTFail("debug should not be called") + return "debug" + }() + ) + logger.info( + { + "info" + }() + ) + logger.warning( + { + "warning" + }() + ) + logger.error( + { + "error" + }() + ) XCTAssertEqual(3, logging.history.entries.count, "expected number of entries to match") logging.history.assertNotExist(level: .debug, message: "trace") logging.history.assertNotExist(level: .debug, message: "debug") @@ -138,8 +153,8 @@ class LoggingTest: XCTestCase { messageMaterializations += 1 return "info" }(), - metadata: { () -> - Logger.Metadata in metadataMaterializations += 1 + metadata: { () -> Logger.Metadata in + metadataMaterializations += 1 return [:] }() ) @@ -170,42 +185,66 @@ class LoggingTest: XCTestCase { // each logs its own metadata multiplexLogger.info("info") - logging1.history.assertExist(level: .info, message: "info", metadata: [ - "one": "111", - "in": "in-1", - ]) - logging2.history.assertExist(level: .info, message: "info", metadata: [ - "two": "222", - "in": "in-2", - ]) + logging1.history.assertExist( + level: .info, + message: "info", + metadata: [ + "one": "111", + "in": "in-1", + ] + ) + logging2.history.assertExist( + level: .info, + message: "info", + metadata: [ + "two": "222", + "in": "in-2", + ] + ) // if modified, change applies to both underlying handlers multiplexLogger[metadataKey: "new"] = "new" multiplexLogger.info("info") - logging1.history.assertExist(level: .info, message: "info", metadata: [ - "one": "111", - "in": "in-1", - "new": "new", - ]) - logging2.history.assertExist(level: .info, message: "info", metadata: [ - "two": "222", - "in": "in-2", - "new": "new", - ]) + logging1.history.assertExist( + level: .info, + message: "info", + metadata: [ + "one": "111", + "in": "in-1", + "new": "new", + ] + ) + logging2.history.assertExist( + level: .info, + message: "info", + metadata: [ + "two": "222", + "in": "in-2", + "new": "new", + ] + ) // overriding an existing value works the same way as adding a new one multiplexLogger[metadataKey: "in"] = "multi" multiplexLogger.info("info") - logging1.history.assertExist(level: .info, message: "info", metadata: [ - "one": "111", - "in": "multi", - "new": "new", - ]) - logging2.history.assertExist(level: .info, message: "info", metadata: [ - "two": "222", - "in": "multi", - "new": "new", - ]) + logging1.history.assertExist( + level: .info, + message: "info", + metadata: [ + "one": "111", + "in": "multi", + "new": "new", + ] + ) + logging2.history.assertExist( + level: .info, + message: "info", + metadata: [ + "two": "222", + "in": "multi", + "new": "new", + ] + ) } func testMultiplexLogHandlerMetadata_readingHandlerMetadata() { @@ -225,11 +264,14 @@ class LoggingTest: XCTestCase { let multiplexLogger = Logger(label: "test") - XCTAssertEqual(multiplexLogger.handler.metadata, [ - "one": "111", - "two": "222", - "in": "in-2", - ]) + XCTAssertEqual( + multiplexLogger.handler.metadata, + [ + "one": "111", + "two": "222", + "in": "in-2", + ] + ) } func testMultiplexMetadataProviderSet() { @@ -263,19 +305,25 @@ class LoggingTest: XCTestCase { let multiplexLogger = Logger(label: "test") - XCTAssertEqual(multiplexLogger.handler.metadata, [ - "one": "111", - "two": "222", - "in": "in-2", - "provider-1": "provided-111", - "provider-2": "provided-222", - "provider-overlap": "provided-222", - ]) - XCTAssertEqual(multiplexLogger.handler.metadataProvider?.get(), [ - "provider-1": "provided-111", - "provider-2": "provided-222", - "provider-overlap": "provided-222", - ]) + XCTAssertEqual( + multiplexLogger.handler.metadata, + [ + "one": "111", + "two": "222", + "in": "in-2", + "provider-1": "provided-111", + "provider-2": "provided-222", + "provider-overlap": "provided-222", + ] + ) + XCTAssertEqual( + multiplexLogger.handler.metadataProvider?.get(), + [ + "provider-1": "provided-111", + "provider-2": "provided-222", + "provider-overlap": "provided-222", + ] + ) } func testMultiplexMetadataProviderExtract() { @@ -301,24 +349,30 @@ class LoggingTest: XCTestCase { return handler2 }() - LoggingSystem.bootstrapInternal({ _, metadataProvider in - MultiplexLogHandler( - [handler1, handler2], - metadataProvider: metadataProvider - ) - }, metadataProvider: .constant([ - "provider-overlap": "provided-outer", - ])) + LoggingSystem.bootstrapInternal( + { _, metadataProvider in + MultiplexLogHandler( + [handler1, handler2], + metadataProvider: metadataProvider + ) + }, + metadataProvider: .constant([ + "provider-overlap": "provided-outer" + ]) + ) let multiplexLogger = Logger(label: "test") let provider = multiplexLogger.metadataProvider! - XCTAssertEqual(provider.get(), [ - "provider-1": "provided-111", - "provider-2": "provided-222", - "provider-overlap": "provided-outer", - ]) + XCTAssertEqual( + provider.get(), + [ + "provider-1": "provided-111", + "provider-2": "provided-222", + "provider-overlap": "provided-outer", + ] + ) } enum TestError: Error { @@ -334,11 +388,15 @@ class LoggingTest: XCTestCase { logger[metadataKey: "empty-dict"] = [:] logger[metadataKey: "nested-dict"] = ["l1key": ["l2key": ["l3key": "l3value"]]] logger.info("hello world!") - testLogging.history.assertExist(level: .info, - message: "hello world!", - metadata: ["foo": ["bar": "buz"], - "empty-dict": [:], - "nested-dict": ["l1key": ["l2key": ["l3key": "l3value"]]]]) + testLogging.history.assertExist( + level: .info, + message: "hello world!", + metadata: [ + "foo": ["bar": "buz"], + "empty-dict": [:], + "nested-dict": ["l1key": ["l2key": ["l3key": "l3value"]]], + ] + ) } func testListMetadata() { @@ -350,11 +408,15 @@ class LoggingTest: XCTestCase { logger[metadataKey: "empty-list"] = [] logger[metadataKey: "nested-list"] = ["l1str", ["l2str1", "l2str2"]] logger.info("hello world!") - testLogging.history.assertExist(level: .info, - message: "hello world!", - metadata: ["foo": ["bar", "buz"], - "empty-list": [], - "nested-list": ["l1str", ["l2str1", "l2str2"]]]) + testLogging.history.assertExist( + level: .info, + message: "hello world!", + metadata: [ + "foo": ["bar", "buz"], + "empty-list": [], + "nested-list": ["l1str", ["l2str1", "l2str2"]], + ] + ) } // Example of custom "box" which may be used to implement "render at most once" semantics @@ -380,7 +442,7 @@ class LoggingTest: XCTestCase { } public var description: String { - return "\(self.value)" + "\(self.value)" } } @@ -393,10 +455,14 @@ class LoggingTest: XCTestCase { let lazyBox = LazyMetadataBox { "rendered-at-first-use" } logger[metadataKey: "lazy"] = .stringConvertible(lazyBox) logger.info("hello world!") - testLogging.history.assertExist(level: .info, - message: "hello world!", - metadata: ["foo": .stringConvertible("raw-string"), - "lazy": .stringConvertible(LazyMetadataBox { "rendered-at-first-use" })]) + testLogging.history.assertExist( + level: .info, + message: "hello world!", + metadata: [ + "foo": .stringConvertible("raw-string"), + "lazy": .stringConvertible(LazyMetadataBox { "rendered-at-first-use" }), + ] + ) } private func dontEvaluateThisString(file: StaticString = #filePath, line: UInt = #line) -> Logger.Message { @@ -429,26 +495,38 @@ class LoggingTest: XCTestCase { logger.warning("hello world!") logger.error("hello world!", metadata: ["baz": "quc"]) testLogging.history.assertExist(level: .info, message: "hello world!", metadata: ["foo": "bar"]) - testLogging.history.assertExist(level: .warning, message: "hello world!", metadata: ["bar": "baz", "baz": "qux"]) + testLogging.history.assertExist( + level: .warning, + message: "hello world!", + metadata: ["bar": "baz", "baz": "qux"] + ) testLogging.history.assertExist(level: .error, message: "hello world!", metadata: ["bar": "baz", "baz": "quc"]) } func testCustomFactory() { struct CustomHandler: LogHandler { - func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, source: String, file: String, function: String, line: UInt) {} + func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) {} subscript(metadataKey _: String) -> Logger.Metadata.Value? { - get { return nil } + get { nil } set {} } var metadata: Logger.Metadata { - get { return Logger.Metadata() } + get { Logger.Metadata() } set {} } var logLevel: Logger.Level { - get { return .info } + get { .info } set {} } } @@ -580,7 +658,7 @@ class LoggingTest: XCTestCase { emitLogMessage("hello", to: logger) - let moduleName = "LoggingTests" // the actual name + let moduleName = "LoggingTests" // the actual name testLogging.history.assertExist(level: .trace, message: "hello", source: moduleName) testLogging.history.assertExist(level: .debug, message: "hello", source: moduleName) @@ -598,10 +676,12 @@ class LoggingTest: XCTestCase { var logger = Logger(label: "\(#function)") logger.logLevel = .debug - let someInt = Int.random(in: 23 ..< 42) + let someInt = Int.random(in: 23..<42) logger.debug("My favourite number is \(someInt) and not \(someInt - 1)") - testLogging.history.assertExist(level: .debug, - message: "My favourite number is \(someInt) and not \(someInt - 1)" as String) + testLogging.history.assertExist( + level: .debug, + message: "My favourite number is \(someInt) and not \(someInt - 1)" as String + ) } func testLoggingAString() { @@ -625,66 +705,85 @@ class LoggingTest: XCTestCase { let providerA = Logger.MetadataProvider { ["provider": "a", "a": "foo"] } let providerB = Logger.MetadataProvider { ["provider": "b", "b": "bar"] } - let logger = Logger(label: #function, - factory: { label in - logging.makeWithMetadataProvider(label: label, metadataProvider: .multiplex([providerA, providerB])) - }) + let logger = Logger( + label: #function, + factory: { label in + logging.makeWithMetadataProvider(label: label, metadataProvider: .multiplex([providerA, providerB])) + } + ) logger.log(level: .info, "test", metadata: ["one-off": "42"]) - logging.history.assertExist(level: .info, - message: "test", - metadata: ["provider": "b", "a": "foo", "b": "bar", "one-off": "42"]) + logging.history.assertExist( + level: .info, + message: "test", + metadata: ["provider": "b", "a": "foo", "b": "bar", "one-off": "42"] + ) } func testLoggerWithoutFactoryOverrideDefaultsToUsingLoggingSystemMetadataProvider() { let logging = TestLogging() - LoggingSystem.bootstrapInternal({ logging.makeWithMetadataProvider(label: $0, metadataProvider: $1) }, - metadataProvider: .init { ["provider": "42"] }) + LoggingSystem.bootstrapInternal( + { logging.makeWithMetadataProvider(label: $0, metadataProvider: $1) }, + metadataProvider: .init { ["provider": "42"] } + ) let logger = Logger(label: #function) logger.log(level: .info, "test", metadata: ["one-off": "42"]) - logging.history.assertExist(level: .info, - message: "test", - metadata: ["provider": "42", "one-off": "42"]) + logging.history.assertExist( + level: .info, + message: "test", + metadata: ["provider": "42", "one-off": "42"] + ) } func testLoggerWithPredefinedLibraryMetadataProvider() { let logging = TestLogging() LoggingSystem.bootstrapInternal( { logging.makeWithMetadataProvider(label: $0, metadataProvider: $1) }, - metadataProvider: .exampleMetadataProvider + metadataProvider: .exampleProvider ) let logger = Logger(label: #function) logger.log(level: .info, "test", metadata: ["one-off": "42"]) - logging.history.assertExist(level: .info, - message: "test", - metadata: ["example": "example-value", "one-off": "42"]) + logging.history.assertExist( + level: .info, + message: "test", + metadata: ["example": "example-value", "one-off": "42"] + ) } func testLoggerWithFactoryOverrideDefaultsToUsingLoggingSystemMetadataProvider() { let logging = TestLogging() - LoggingSystem.bootstrapInternal({ logging.makeWithMetadataProvider(label: $0, metadataProvider: $1) }, - metadataProvider: .init { ["provider": "42"] }) + LoggingSystem.bootstrapInternal( + { logging.makeWithMetadataProvider(label: $0, metadataProvider: $1) }, + metadataProvider: .init { ["provider": "42"] } + ) - let logger = Logger(label: #function, factory: { label in - logging.makeWithMetadataProvider(label: label, metadataProvider: LoggingSystem.metadataProvider) - }) + let logger = Logger( + label: #function, + factory: { label in + logging.makeWithMetadataProvider(label: label, metadataProvider: LoggingSystem.metadataProvider) + } + ) logger.log(level: .info, "test", metadata: ["one-off": "42"]) - logging.history.assertExist(level: .info, - message: "test", - metadata: ["provider": "42", "one-off": "42"]) + logging.history.assertExist( + level: .info, + message: "test", + metadata: ["provider": "42", "one-off": "42"] + ) } func testMultiplexerIsValue() { - let multi = MultiplexLogHandler([StreamLogHandler.standardOutput(label: "x"), StreamLogHandler.standardOutput(label: "y")]) + let multi = MultiplexLogHandler([ + StreamLogHandler.standardOutput(label: "x"), StreamLogHandler.standardOutput(label: "y"), + ]) LoggingSystem.bootstrapInternal { _ in print("new multi") return multi @@ -752,7 +851,7 @@ class LoggingTest: XCTestCase { var logLevel: Logger.Level { // when we get asked for the log level, we check if it was globally overridden or not get { - return LogHandlerWithGlobalLogLevelOverride.overrideLogLevel.underlying ?? self._logLevel + LogHandlerWithGlobalLogLevelOverride.overrideLogLevel.underlying ?? self._logLevel } // we set the log level whenever we're asked (note: this might not have an effect if globally // overridden) @@ -761,14 +860,21 @@ class LoggingTest: XCTestCase { } } - func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, - source: String, file: String, function: String, line: UInt) { + func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { self.recorder.record(level: level, metadata: metadata, message: message, source: source) } subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { get { - return self.metadata[metadataKey] + self.metadata[metadataKey] } set(newValue) { self.metadata[metadataKey] = newValue @@ -861,7 +967,8 @@ class LoggingTest: XCTestCase { let testString = "my message is better than yours" log.critical("\(testString)") - let messageSucceeded = interceptStream.interceptedText?.trimmingCharacters(in: .whitespacesAndNewlines).hasSuffix(testString) + let messageSucceeded = interceptStream.interceptedText?.trimmingCharacters(in: .whitespacesAndNewlines) + .hasSuffix(testString) XCTAssertTrue(messageSucceeded ?? false) XCTAssertEqual(interceptStream.strings.count, 1) @@ -879,9 +986,14 @@ class LoggingTest: XCTestCase { let testString = "my message is better than yours" log.critical("\(testString)", source: source) - let pattern = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\+|-)\\d{4}\\s\(Logger.Level.critical)\\s\(label)\\s:\\s\\[\(source)\\]\\s\(testString)$" + let pattern = + "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\+|-)\\d{4}\\s\(Logger.Level.critical)\\s\(label)\\s:\\s\\[\(source)\\]\\s\(testString)$" - let messageSucceeded = interceptStream.interceptedText?.trimmingCharacters(in: .whitespacesAndNewlines).range(of: pattern, options: .regularExpression) != nil + let messageSucceeded = + interceptStream.interceptedText?.trimmingCharacters(in: .whitespacesAndNewlines).range( + of: pattern, + options: .regularExpression + ) != nil XCTAssertTrue(messageSucceeded) XCTAssertEqual(interceptStream.strings.count, 1) @@ -899,9 +1011,14 @@ class LoggingTest: XCTestCase { let testString = "my message is better than yours" log.critical("\(testString)", metadata: ["test": "test"], source: source) - let pattern = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\+|-)\\d{4}\\s\(Logger.Level.critical)\\s\(label)\\s:\\stest=test\\s\\[\(source)\\]\\s\(testString)$" + let pattern = + "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\+|-)\\d{4}\\s\(Logger.Level.critical)\\s\(label)\\s:\\stest=test\\s\\[\(source)\\]\\s\(testString)$" - let messageSucceeded = interceptStream.interceptedText?.trimmingCharacters(in: .whitespacesAndNewlines).range(of: pattern, options: .regularExpression) != nil + let messageSucceeded = + interceptStream.interceptedText?.trimmingCharacters(in: .whitespacesAndNewlines).range( + of: pattern, + options: .regularExpression + ) != nil XCTAssertTrue(messageSucceeded) XCTAssertEqual(interceptStream.strings.count, 1) @@ -931,15 +1048,19 @@ class LoggingTest: XCTestCase { func testStreamLogHandlerWritesIncludeMetadataProviderMetadata() { let interceptStream = InterceptStream() - LoggingSystem.bootstrapInternal({ _, metadataProvider in - StreamLogHandler(label: "test", stream: interceptStream, metadataProvider: metadataProvider) - }, metadataProvider: .exampleMetadataProvider) + LoggingSystem.bootstrapInternal( + { _, metadataProvider in + StreamLogHandler(label: "test", stream: interceptStream, metadataProvider: metadataProvider) + }, + metadataProvider: .exampleProvider + ) let log = Logger(label: "test") let testString = "my message is better than yours" log.critical("\(testString)") - let messageSucceeded = interceptStream.interceptedText?.trimmingCharacters(in: .whitespacesAndNewlines).hasSuffix(testString) + let messageSucceeded = interceptStream.interceptedText?.trimmingCharacters(in: .whitespacesAndNewlines) + .hasSuffix(testString) XCTAssertTrue(messageSucceeded ?? false) XCTAssertEqual(interceptStream.strings.count, 1) @@ -955,9 +1076,16 @@ class LoggingTest: XCTestCase { let testString = "hello\u{0} world" log.critical("\(testString)") + #if os(Windows) + let size = _read(readFD, readBuffer, 256) + #else let size = read(readFD, readBuffer, 256) + #endif - let output = String(decoding: UnsafeRawBufferPointer(start: UnsafeRawPointer(readBuffer), count: numericCast(size)), as: UTF8.self) + let output = String( + decoding: UnsafeRawBufferPointer(start: UnsafeRawPointer(readBuffer), count: numericCast(size)), + as: UTF8.self + ) let messageSucceeded = output.trimmingCharacters(in: .whitespacesAndNewlines).hasSuffix(testString) XCTAssertTrue(messageSucceeded) } @@ -970,11 +1098,20 @@ class LoggingTest: XCTestCase { LoggingSystem.bootstrapInternal { StreamLogHandler(label: $0, stream: logStream) } Logger(label: "test").critical("test") + #if os(Windows) + let size = _read(readFD, readBuffer, 256) + #else let size = read(readFD, readBuffer, 256) + #endif XCTAssertGreaterThan(size, -1, "expected flush") logStream.flush() + + #if os(Windows) + let size2 = _read(readFD, readBuffer, 256) + #else let size2 = read(readFD, readBuffer, 256) + #endif XCTAssertEqual(size2, -1, "expected no flush") } // default flushing @@ -983,11 +1120,20 @@ class LoggingTest: XCTestCase { LoggingSystem.bootstrapInternal { StreamLogHandler(label: $0, stream: logStream) } Logger(label: "test").critical("test") + #if os(Windows) + let size = _read(readFD, readBuffer, 256) + #else let size = read(readFD, readBuffer, 256) + #endif XCTAssertEqual(size, -1, "expected no flush") logStream.flush() + + #if os(Windows) + let size2 = _read(readFD, readBuffer, 256) + #else let size2 = read(readFD, readBuffer, 256) + #endif XCTAssertGreaterThan(size2, -1, "expected flush") } } @@ -999,14 +1145,15 @@ class LoggingTest: XCTestCase { let err = _pipe($0.baseAddress, 256, _O_BINARY) XCTAssertEqual(err, 0, "_pipe failed \(err)") } + let writeFD = _fdopen(fds[1], "w") #else fds.withUnsafeMutableBufferPointer { ptr in let err = pipe(ptr.baseAddress!) XCTAssertEqual(err, 0, "pipe failed \(err)") } + let writeFD = fdopen(fds[1], "w") #endif - let writeFD = fdopen(fds[1], "w") let writeBuffer = UnsafeMutablePointer.allocate(capacity: 256) defer { writeBuffer.deinitialize(count: 256) @@ -1038,13 +1185,19 @@ class LoggingTest: XCTestCase { // the actual test body(writeFD!, readFD, readBuffer) - fds.forEach { close($0) } + for fd in fds { + #if os(Windows) + _close(fd) + #else + close(fd) + #endif + } } func testOverloadingError() { struct Dummy: Error, LocalizedError { var errorDescription: String? { - return "errorDescription" + "errorDescription" } } // bootstrap with our test logging impl @@ -1060,7 +1213,7 @@ class LoggingTest: XCTestCase { func testCompileInitializeStandardStreamLogHandlersWithMetadataProviders() { // avoid "unreachable code" warnings - let dontExecute = Int.random(in: 100 ... 200) == 1 + let dontExecute = Int.random(in: 100...200) == 1 guard dontExecute else { return } @@ -1070,18 +1223,32 @@ class LoggingTest: XCTestCase { LoggingSystem.bootstrap { (label: String) in StreamLogHandler.standardError(label: label) } // with metadata handler, explicitly, public api - LoggingSystem.bootstrap({ label, metadataProvider in - StreamLogHandler.standardOutput(label: label, metadataProvider: metadataProvider) - }, metadataProvider: .exampleMetadataProvider) - LoggingSystem.bootstrap({ label, metadataProvider in - StreamLogHandler.standardError(label: label, metadataProvider: metadataProvider) - }, metadataProvider: .exampleMetadataProvider) + LoggingSystem.bootstrap( + { label, metadataProvider in + StreamLogHandler.standardOutput(label: label, metadataProvider: metadataProvider) + }, + metadataProvider: .exampleProvider + ) + LoggingSystem.bootstrap( + { label, metadataProvider in + StreamLogHandler.standardError(label: label, metadataProvider: metadataProvider) + }, + metadataProvider: .exampleProvider + ) // with metadata handler, still pretty - LoggingSystem.bootstrap({ (label: String, metadataProvider: Logger.MetadataProvider?) in StreamLogHandler.standardOutput(label: label, metadataProvider: metadataProvider) }, - metadataProvider: .exampleMetadataProvider) - LoggingSystem.bootstrap({ (label: String, metadataProvider: Logger.MetadataProvider?) in StreamLogHandler.standardError(label: label, metadataProvider: metadataProvider) }, - metadataProvider: .exampleMetadataProvider) + LoggingSystem.bootstrap( + { (label: String, metadataProvider: Logger.MetadataProvider?) in + StreamLogHandler.standardOutput(label: label, metadataProvider: metadataProvider) + }, + metadataProvider: .exampleProvider + ) + LoggingSystem.bootstrap( + { (label: String, metadataProvider: Logger.MetadataProvider?) in + StreamLogHandler.standardError(label: label, metadataProvider: metadataProvider) + }, + metadataProvider: .exampleProvider + ) } func testLoggerIsJustHoldingASinglePointer() { @@ -1100,15 +1267,19 @@ class LoggingTest: XCTestCase { } extension Logger { - public func error(error: any Error, - metadata: @autoclosure () -> Logger.Metadata? = nil, - file: String = #fileID, function: String = #function, line: UInt = #line) { + public func error( + error: any Error, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.error("\(error.localizedDescription)", metadata: metadata(), file: file, function: function, line: line) } } extension Logger.MetadataProvider { - static var exampleMetadataProvider: Self { + static var exampleProvider: Self { .init { ["example": .string("example-value")] } } diff --git a/Tests/LoggingTests/MDCTest.swift b/Tests/LoggingTests/MDCTest.swift index 029cd6aa..18c00050 100644 --- a/Tests/LoggingTests/MDCTest.swift +++ b/Tests/LoggingTests/MDCTest.swift @@ -12,9 +12,10 @@ // //===----------------------------------------------------------------------===// import Dispatch -@testable import Logging import XCTest +@testable import Logging + class MDCTest: XCTestCase { func test1() throws { // bootstrap with our test logger @@ -24,22 +25,22 @@ class MDCTest: XCTestCase { // run the program MDC.global["foo"] = "bar" let group = DispatchGroup() - for r in 5 ... 10 { + for r in 5...10 { group.enter() DispatchQueue(label: "mdc-test-queue-\(r)").async { - let add = Int.random(in: 10 ... 1000) - let remove = Int.random(in: 0 ... add - 1) - for i in 0 ... add { + let add = Int.random(in: 10...1000) + let remove = Int.random(in: 0...add - 1) + for i in 0...add { MDC.global["key-\(i)"] = "value-\(i)" } - for i in 0 ... remove { + for i in 0...remove { MDC.global["key-\(i)"] = nil } XCTAssertEqual(add - remove, MDC.global.metadata.count, "expected number of entries to match") - for i in remove + 1 ... add { + for i in remove + 1...add { XCTAssertNotNil(MDC.global["key-\(i)"], "expecting value for key-\(i)") } - for i in 0 ... remove { + for i in 0...remove { XCTAssertNil(MDC.global["key-\(i)"], "not expecting value for key-\(i)") } MDC.global.clear() diff --git a/Tests/LoggingTests/MetadataProviderTest.swift b/Tests/LoggingTests/MetadataProviderTest.swift index 0730816d..f6ba4252 100644 --- a/Tests/LoggingTests/MetadataProviderTest.swift +++ b/Tests/LoggingTests/MetadataProviderTest.swift @@ -11,9 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// -@testable import Logging + import XCTest +@testable import Logging + #if canImport(Darwin) import Darwin #elseif os(Windows) @@ -25,22 +27,30 @@ import Glibc final class MetadataProviderTest: XCTestCase { func testLoggingMergesOneOffMetadataWithProvidedMetadataFromExplicitlyPassed() throws { let logging = TestLogging() - LoggingSystem.bootstrapInternal({ logging.makeWithMetadataProvider(label: $0, metadataProvider: $1) }, - metadataProvider: .init { ["common": "initial"] - }) - - let logger = Logger(label: #function, metadataProvider: .init { - [ - "common": "provider", - "provider": "42", - ] - }) + LoggingSystem.bootstrapInternal( + { logging.makeWithMetadataProvider(label: $0, metadataProvider: $1) }, + metadataProvider: .init { + ["common": "initial"] + } + ) + + let logger = Logger( + label: #function, + metadataProvider: .init { + [ + "common": "provider", + "provider": "42", + ] + } + ) logger.log(level: .info, "test", metadata: ["one-off": "42", "common": "one-off"]) - logging.history.assertExist(level: .info, - message: "test", - metadata: ["common": "one-off", "one-off": "42", "provider": "42"]) + logging.history.assertExist( + level: .info, + message: "test", + metadata: ["common": "one-off", "one-off": "42", "provider": "42"] + ) } func testLogHandlerThatDidNotImplementProvidersButSomeoneAttemptsToSetOneOnIt() { @@ -51,7 +61,12 @@ final class MetadataProviderTest: XCTestCase { handler.metadataProvider = .simpleTestProvider - logging.history.assertExist(level: .warning, message: "Attempted to set metadataProvider on LogHandlerThatDidNotImplementMetadataProviders that did not implement support for them. Please contact the log handler maintainer to implement metadata provider support.", source: "Logging") + logging.history.assertExist( + level: .warning, + message: + "Attempted to set metadataProvider on LogHandlerThatDidNotImplementMetadataProviders that did not implement support for them. Please contact the log handler maintainer to implement metadata provider support.", + source: "Logging" + ) let countBefore = logging.history.entries.count handler.metadataProvider = .simpleTestProvider @@ -67,15 +82,20 @@ final class MetadataProviderTest: XCTestCase { handler.metadataProvider = .simpleTestProvider - logging.history.assertNotExist(level: .warning, message: "Attempted to set metadataProvider on LogHandlerThatDidImplementMetadataProviders that did not implement support for them. Please contact the log handler maintainer to implement metadata provider support.", source: "Logging") + logging.history.assertNotExist( + level: .warning, + message: + "Attempted to set metadataProvider on LogHandlerThatDidImplementMetadataProviders that did not implement support for them. Please contact the log handler maintainer to implement metadata provider support.", + source: "Logging" + ) #endif } } extension Logger.MetadataProvider { static var simpleTestProvider: Logger.MetadataProvider { - return Logger.MetadataProvider { - return ["test": "provided"] + Logger.MetadataProvider { + ["test": "provided"] } } } @@ -88,7 +108,7 @@ public struct LogHandlerThatDidNotImplementMetadataProviders: LogHandler { public subscript(metadataKey _: String) -> Logging.Logger.Metadata.Value? { get { - return nil + nil } set(newValue) { // ignore @@ -99,14 +119,24 @@ public struct LogHandlerThatDidNotImplementMetadataProviders: LogHandler { public var logLevel: Logging.Logger.Level = .trace - public func log(level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt) { - self.testLogging.make(label: "fake").log(level: level, message: message, metadata: metadata, source: source, file: file, function: function, line: line) + public func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { + self.testLogging.make(label: "fake").log( + level: level, + message: message, + metadata: metadata, + source: source, + file: file, + function: function, + line: line + ) } } @@ -118,7 +148,7 @@ public struct LogHandlerThatDidImplementMetadataProviders: LogHandler { public subscript(metadataKey _: String) -> Logging.Logger.Metadata.Value? { get { - return nil + nil } set(newValue) { // ignore @@ -131,13 +161,23 @@ public struct LogHandlerThatDidImplementMetadataProviders: LogHandler { public var metadataProvider: Logger.MetadataProvider? - public func log(level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt) { - self.testLogging.make(label: "fake").log(level: level, message: message, metadata: metadata, source: source, file: file, function: function, line: line) + public func log( + level: Logger.Level, + message: Logger.Message, + metadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { + self.testLogging.make(label: "fake").log( + level: level, + message: message, + metadata: metadata, + source: source, + file: file, + function: function, + line: line + ) } } diff --git a/Tests/LoggingTests/TestLogger.swift b/Tests/LoggingTests/TestLogger.swift index c8191268..9dfb9d31 100644 --- a/Tests/LoggingTests/TestLogger.swift +++ b/Tests/LoggingTests/TestLogger.swift @@ -11,9 +11,12 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// + import Foundation -@testable import Logging import XCTest + +@testable import Logging + #if os(Windows) import WinSDK #endif @@ -24,11 +27,11 @@ import Dispatch #endif internal struct TestLogging { - private let _config = Config() // shared among loggers - private let recorder = Recorder() // shared among loggers + private let _config = Config() // shared among loggers + private let recorder = Recorder() // shared among loggers func make(label: String) -> some LogHandler { - return TestLogHandler( + TestLogHandler( label: label, config: self.config, recorder: self.recorder, @@ -37,7 +40,7 @@ internal struct TestLogging { } func makeWithMetadataProvider(label: String, metadataProvider: Logger.MetadataProvider?) -> (some LogHandler) { - return TestLogHandler( + TestLogHandler( label: label, config: self.config, recorder: self.recorder, @@ -45,14 +48,14 @@ internal struct TestLogging { ) } - var config: Config { return self._config } - var history: some History { return self.recorder } + var config: Config { self._config } + var history: some History { self.recorder } } internal struct TestLogHandler: LogHandler { private let recorder: Recorder private let config: Config - private var logger: Logger // the actual logger + private var logger: Logger // the actual logger let label: String public var metadataProvider: Logger.MetadataProvider? @@ -75,11 +78,15 @@ internal struct TestLogHandler: LogHandler { self.metadataProvider = LoggingSystem.metadataProvider } - func log(level: Logger.Level, - message: Logger.Message, - metadata explicitMetadata: Logger.Metadata?, - source: String, - file: String, function: String, line: UInt) { + func log( + level: Logger.Level, + message: Logger.Message, + metadata explicitMetadata: Logger.Metadata?, + source: String, + file: String, + function: String, + line: UInt + ) { // baseline metadata, that was set on handler: var metadata = self._metadataSet ? self.metadata : MDC.global.metadata // contextual metadata, e.g. from task-locals: @@ -92,7 +99,15 @@ internal struct TestLogHandler: LogHandler { metadata.merge(explicitMetadata, uniquingKeysWith: { _, explicit in explicit }) } - self.logger.log(level: level, message, metadata: metadata, source: source, file: file, function: function, line: line) + self.logger.log( + level: level, + message, + metadata: metadata, + source: source, + file: file, + function: function, + line: line + ) self.recorder.record(level: level, metadata: metadata, message: message, source: source) } @@ -100,7 +115,7 @@ internal struct TestLogHandler: LogHandler { var logLevel: Logger.Level { get { // get from config unless set - return self._logLevel ?? self.config.get(key: self.label) + self._logLevel ?? self.config.get(key: self.label) } set { self._logLevel = newValue @@ -116,7 +131,7 @@ internal struct TestLogHandler: LogHandler { public var metadata: Logger.Metadata { get { - return self._metadata + self._metadata } set { self._metadata = newValue @@ -126,7 +141,7 @@ internal struct TestLogHandler: LogHandler { // TODO: would be nice to delegate to local copy of logger but StdoutLogger is a reference type. why? subscript(metadataKey metadataKey: Logger.Metadata.Key) -> Logger.Metadata.Value? { get { - return self._metadata[metadataKey] + self._metadata[metadataKey] } set { self._metadata[metadataKey] = newValue @@ -141,7 +156,7 @@ internal class Config { private var storage = [String: Logger.Level]() func get(key: String) -> Logger.Level { - return self.get(key) ?? self.get(Config.ALL) ?? Logger.Level.debug + self.get(key) ?? self.get(Config.ALL) ?? Logger.Level.debug } func get(_ key: String) -> Logger.Level? { @@ -165,13 +180,15 @@ internal class Recorder: History { private var _entries = [LogEntry]() func record(level: Logger.Level, metadata: Logger.Metadata?, message: Logger.Message, source: String) { - return self.lock.withLock { - self._entries.append(LogEntry(level: level, metadata: metadata, message: message.description, source: source)) + self.lock.withLock { + self._entries.append( + LogEntry(level: level, metadata: metadata, message: message.description, source: source) + ) } } var entries: [LogEntry] { - return self.lock.withLock { self._entries } + self.lock.withLock { self._entries } } } @@ -179,31 +196,31 @@ internal protocol History { var entries: [LogEntry] { get } } -internal extension History { +extension History { func atLevel(level: Logger.Level) -> [LogEntry] { - return self.entries.filter { entry in + self.entries.filter { entry in level == entry.level } } var trace: [LogEntry] { - return self.atLevel(level: .debug) + self.atLevel(level: .debug) } var debug: [LogEntry] { - return self.atLevel(level: .debug) + self.atLevel(level: .debug) } var info: [LogEntry] { - return self.atLevel(level: .info) + self.atLevel(level: .info) } var warning: [LogEntry] { - return self.atLevel(level: .warning) + self.atLevel(level: .warning) } var error: [LogEntry] { - return self.atLevel(level: .error) + self.atLevel(level: .error) } } @@ -215,34 +232,46 @@ internal struct LogEntry { } extension History { - func assertExist(level: Logger.Level, - message: String, - metadata: Logger.Metadata? = nil, - source: String? = nil, - file: StaticString = #filePath, - fileID: String = #fileID, - line: UInt = #line) { + func assertExist( + level: Logger.Level, + message: String, + metadata: Logger.Metadata? = nil, + source: String? = nil, + file: StaticString = #filePath, + fileID: String = #fileID, + line: UInt = #line + ) { let source = source ?? Logger.currentModule(fileID: "\(fileID)") let entry = self.find(level: level, message: message, metadata: metadata, source: source) - XCTAssertNotNil(entry, "entry not found: \(level), \(source), \(String(describing: metadata)), \(message)", - file: file, line: line) + XCTAssertNotNil( + entry, + "entry not found: \(level), \(source), \(String(describing: metadata)), \(message)", + file: file, + line: line + ) } - func assertNotExist(level: Logger.Level, - message: String, - metadata: Logger.Metadata? = nil, - source: String? = nil, - file: StaticString = #filePath, - fileID: String = #file, - line: UInt = #line) { + func assertNotExist( + level: Logger.Level, + message: String, + metadata: Logger.Metadata? = nil, + source: String? = nil, + file: StaticString = #filePath, + fileID: String = #file, + line: UInt = #line + ) { let source = source ?? Logger.currentModule(fileID: "\(fileID)") let entry = self.find(level: level, message: message, metadata: metadata, source: source) - XCTAssertNil(entry, "entry was found: \(level), \(source), \(String(describing: metadata)), \(message)", - file: file, line: line) + XCTAssertNil( + entry, + "entry was found: \(level), \(source), \(String(describing: metadata)), \(message)", + file: file, + line: line + ) } func find(level: Logger.Level, message: String, metadata: Logger.Metadata? = nil, source: String) -> LogEntry? { - return self.entries.first { entry in + self.entries.first { entry in if entry.level != level { return false } @@ -287,7 +316,7 @@ public class MDC { public subscript(metadataKey: String) -> Logger.Metadata.Value? { get { - return self.lock.withLock { + self.lock.withLock { self.storage[self.threadId]?[metadataKey] } } @@ -302,7 +331,7 @@ public class MDC { } public var metadata: Logger.Metadata { - return self.lock.withLock { + self.lock.withLock { self.storage[self.threadId] ?? [:] } } @@ -314,17 +343,25 @@ public class MDC { } public func with(metadata: Logger.Metadata, _ body: () throws -> Void) rethrows { - metadata.forEach { self[$0] = $1 } + for (key, value) in metadata { + self[key] = value + } defer { - metadata.keys.forEach { self[$0] = nil } + for (key, _) in metadata { + self[key] = nil + } } try body() } public func with(metadata: Logger.Metadata, _ body: () throws -> T) rethrows -> T { - metadata.forEach { self[$0] = $1 } + for (key, value) in metadata { + self[key] = value + } defer { - metadata.keys.forEach { self[$0] = nil } + for (key, _) in metadata { + self[key] = nil + } } return try body() } @@ -347,7 +384,7 @@ public class MDC { } } -internal extension NSLock { +extension NSLock { func withLock(_ body: () -> T) -> T { self.lock() defer { @@ -367,7 +404,7 @@ internal struct TestLibrary: Sendable { self.logger.info("TestLibrary::doSomething") } - public func doSomethingAsync(completion: @escaping @Sendable() -> Void) { + public func doSomethingAsync(completion: @escaping @Sendable () -> Void) { // libraries that use global loggers and async, need to make sure they propagate the // logging metadata when creating a new thread let metadata = MDC.global.metadata diff --git a/Tests/LoggingTests/TestSendable.swift b/Tests/LoggingTests/TestSendable.swift index 37da940b..5348dc56 100644 --- a/Tests/LoggingTests/TestSendable.swift +++ b/Tests/LoggingTests/TestSendable.swift @@ -11,9 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// -@testable import Logging + import XCTest +@testable import Logging + class SendableTest: XCTestCase { func testSendableLogger() async { let testLogging = TestLogging() diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 90861415..00000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG swift_version=5.0 -ARG ubuntu_version=bionic -ARG base_image=swift:$swift_version-$ubuntu_version -FROM $base_image -# needed to do again after FROM due to docker limitation -ARG swift_version -ARG ubuntu_version - -# set as UTF-8 -RUN apt-get update && apt-get install -y locales locales-all -ENV LC_ALL en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US.UTF-8 - -# dependencies -RUN apt-get update && apt-get install -y wget -RUN apt-get update && apt-get install -y lsof dnsutils netcat-openbsd net-tools curl jq # used by integration tests - -# ruby for scripts -RUN apt-get update && apt-get install -y ruby - -# tools -RUN mkdir -p $HOME/.tools -RUN echo 'export PATH="$HOME/.tools:$PATH"' >> $HOME/.profile - -# swiftformat (until part of the toolchain) - -ARG swiftformat_version=0.44.6 -RUN git clone --branch $swiftformat_version --depth 1 https://github.com/nicklockwood/SwiftFormat $HOME/.tools/swift-format -RUN cd $HOME/.tools/swift-format && swift build -c release -RUN ln -s $HOME/.tools/swift-format/.build/release/swiftformat $HOME/.tools/swiftformat diff --git a/docker/docker-compose.2004.58.yaml b/docker/docker-compose.2004.58.yaml deleted file mode 100644 index e0c78d32..00000000 --- a/docker/docker-compose.2004.58.yaml +++ /dev/null @@ -1,19 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-log:20.04-5.8 - build: - args: - ubuntu_version: "focal" - swift_version: "5.8" - - test: - image: swift-log:20.04-5.8 - environment: - - WARN_AS_ERROR_ARG=-Xswiftc -warnings-as-errors - #- SANITIZER_ARG=--sanitize=thread - - shell: - image: swift-log:20.04-5.8 diff --git a/docker/docker-compose.2204.510.yaml b/docker/docker-compose.2204.510.yaml deleted file mode 100644 index 07bd58d8..00000000 --- a/docker/docker-compose.2204.510.yaml +++ /dev/null @@ -1,20 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-log:22.04-5.10 - build: - args: - base_image: "swiftlang/swift:nightly-5.10-jammy" - - test: - image: swift-log:22.04-5.10 - environment: - - WARN_AS_ERROR_ARG=-Xswiftc -warnings-as-errors - #- SANITIZER_ARG=--sanitize=thread - # Generated test manifest uses existentials without 'any'. - #- EXISTENTIAL_ANY_ARG=-Xswiftc -enable-upcoming-feature -Xswiftc ExistentialAny - - shell: - image: swift-log:22.04-5.10 diff --git a/docker/docker-compose.2204.59.yaml b/docker/docker-compose.2204.59.yaml deleted file mode 100644 index 1cc67959..00000000 --- a/docker/docker-compose.2204.59.yaml +++ /dev/null @@ -1,20 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-log:22.04-5.9 - build: - args: - ubuntu_version: "jammy" - swift_version: "5.9" - - test: - image: swift-log:22.04-5.9 - environment: - - WARN_AS_ERROR_ARG=-Xswiftc -warnings-as-errors - - EXISTENTIAL_ANY_ARG=-Xswiftc -enable-upcoming-feature -Xswiftc ExistentialAny - #- SANITIZER_ARG=--sanitize=thread - - shell: - image: swift-log:22.04-5.9 diff --git a/docker/docker-compose.2204.main.yaml b/docker/docker-compose.2204.main.yaml deleted file mode 100644 index c3a1358d..00000000 --- a/docker/docker-compose.2204.main.yaml +++ /dev/null @@ -1,23 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-log:22.04-main - build: - args: - base_image: "swiftlang/swift:nightly-main-jammy" - - test: - image: swift-log:22.04-main - environment: - - WARN_AS_ERROR_ARG=-Xswiftc -warnings-as-errors - #- SANITIZER_ARG=--sanitize=thread - - IMPORT_CHECK_ARG=--explicit-target-dependency-import-check error - - EXPLICIT_SENDABLE_ARG=-Xswiftc -require-explicit-sendable - - STRICT_CONCURRENCY_ARG=-Xswiftc -strict-concurrency=complete - # Generated test manifest uses existentials without 'any'. - #- EXISTENTIAL_ANY_ARG=-Xswiftc -enable-upcoming-feature -Xswiftc ExistentialAny - - shell: - image: swift-log:22.04-main diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml deleted file mode 100644 index 502696cb..00000000 --- a/docker/docker-compose.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# this file is not designed to be run directly -# instead, use the docker-compose.. files -# eg docker-compose -f docker/docker-compose.yaml -f docker/docker-compose.1804.50.yaml run test -version: "3" - -services: - - runtime-setup: - image: swift-log:default - build: - context: . - dockerfile: Dockerfile - - common: &common - image: swift-log:default - depends_on: [runtime-setup] - volumes: - - ~/.ssh:/root/.ssh - - ..:/code:z - working_dir: /code - cap_drop: - - CAP_NET_RAW - - CAP_NET_BIND_SERVICE - - soundness: - <<: *common - command: /bin/bash -xcl "./scripts/soundness.sh" - - test: - <<: *common - command: /bin/bash -xcl "swift test $${WARN_AS_ERROR_ARG-} $${SANITIZER_ARG-} $${IMPORT_CHECK_ARG-} $${EXPLICIT_SENDABLE_ARG-} $${STRICT_CONCURRENCY_ARG-} $${EXISTENTIAL_ANY_ARG-}" - - # util - - shell: - <<: *common - entrypoint: /bin/bash -l diff --git a/scripts/check_no_api_breakages.sh b/scripts/check_no_api_breakages.sh deleted file mode 100755 index 7f0afbe6..00000000 --- a/scripts/check_no_api_breakages.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift Logging API open source project -## -## Copyright (c) 2019 Apple Inc. and the Swift Logging API project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of Swift Logging API project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) 2017-2020 Apple Inc. and the SwiftNIO project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of SwiftNIO project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -set -eu - -function usage() { - echo >&2 "Usage: $0 REPO-GITHUB-URL NEW-VERSION OLD-VERSIONS..." - echo >&2 - echo >&2 "This script requires a Swift 5.2+ toolchain." - echo >&2 - echo >&2 "Examples:" - echo >&2 - echo >&2 "Check between main and tag 2.1.1 of swift-nio:" - echo >&2 " $0 https://github.com/apple/swift-nio main 2.1.1" - echo >&2 - echo >&2 "Check between HEAD and commit 64cf63d7 using the provided toolchain:" - echo >&2 " xcrun --toolchain org.swift.5120190702a $0 ../some-local-repo HEAD 64cf63d7" -} - -if [[ $# -lt 3 ]]; then - usage - exit 1 -fi - -tmpdir=$(mktemp -d /tmp/.check-api_XXXXXX) -repo_url=$1 -new_tag=$2 -shift 2 - -repodir="$tmpdir/repo" -git clone "$repo_url" "$repodir" -git -C "$repodir" fetch -q origin '+refs/pull/*:refs/remotes/origin/pr/*' -cd "$repodir" -git checkout -q "$new_tag" - -for old_tag in "$@"; do - echo "Checking public API breakages from $old_tag to $new_tag" - - swift package diagnose-api-breaking-changes "$old_tag" -done - -echo done diff --git a/scripts/generate_contributors_list.sh b/scripts/generate_contributors_list.sh deleted file mode 100755 index ba8fdd2e..00000000 --- a/scripts/generate_contributors_list.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift Logging API open source project -## -## Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of Swift Logging API project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -set -eu -here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -contributors=$( cd "$here"/.. && git shortlog -es | cut -f2 | sed 's/^/- /' ) - -cat > "$here/../CONTRIBUTORS.txt" <<- EOF - For the purpose of tracking copyright, this is the list of individuals and - organizations who have contributed source code to the Swift Logging API. - - For employees of an organization/company where the copyright of work done - by employees of that company is held by the company itself, only the company - needs to be listed here. - - ## COPYRIGHT HOLDERS - - - Apple Inc. (all contributors with '@apple.com') - - ### Contributors - - $contributors - - **Updating this list** - - Please do not edit this file manually. It is generated using \`./scripts/generate_contributors_list.sh\`. If a name is misspelled or appearing multiple times: add an entry in \`./.mailmap\` -EOF diff --git a/scripts/preview_docc.sh b/scripts/preview_docc.sh deleted file mode 100755 index 84e1e97a..00000000 --- a/scripts/preview_docc.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift Logging API open source project -## -## Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of Swift Logging API project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -swift package --disable-sandbox preview-documentation --target Logging diff --git a/scripts/soundness.sh b/scripts/soundness.sh deleted file mode 100755 index 05c0e706..00000000 --- a/scripts/soundness.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift Logging API open source project -## -## Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of Swift Logging API project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of SwiftNIO project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -set -eu -here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -function replace_acceptable_years() { - # this needs to replace all acceptable forms with 'YEARS' - sed -e 's/20[12][789012]-20[12][89012]/YEARS/' -e 's/20[12][89012]/YEARS/' -} - -printf "=> Checking for unacceptable language... " -# This greps for unacceptable terminology. The square bracket[s] are so that -# "git grep" doesn't find the lines that greps :). -unacceptable_terms=( - -e blacklis[t] - -e whitelis[t] - -e slav[e] - -e sanit[y] -) -if git grep --color=never -i "${unacceptable_terms[@]}" > /dev/null; then - printf "\033[0;31mUnacceptable language found.\033[0m\n" - git grep -i "${unacceptable_terms[@]}" - exit 1 -fi -printf "\033[0;32mokay.\033[0m\n" - -printf "=> Checking format... " -FIRST_OUT="$(git status --porcelain)" -swiftformat . > /dev/null 2>&1 -SECOND_OUT="$(git status --porcelain)" -if [[ "$FIRST_OUT" != "$SECOND_OUT" ]]; then - printf "\033[0;31mformatting issues!\033[0m\n" - git --no-pager diff - exit 1 -else - printf "\033[0;32mokay.\033[0m\n" -fi - -printf "=> Checking license headers\n" -tmp=$(mktemp /tmp/.swift-log-soundness_XXXXXX) - -for language in swift-or-c bash dtrace; do - printf " * $language... " - declare -a matching_files - declare -a exceptions - expections=( ) - matching_files=( -name '*' ) - case "$language" in - swift-or-c) - exceptions=( -name c_nio_http_parser.c -o -name c_nio_http_parser.h -o -name cpp_magic.h -o -name Package.swift -o -name CNIOSHA1.h -o -name c_nio_sha1.c -o -name ifaddrs-android.c -o -name ifaddrs-android.h) - matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' ) - cat > "$tmp" <<"EOF" -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Logging API open source project -// -// Copyright (c) YEARS Apple Inc. and the Swift Logging API project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Logging API project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -EOF - ;; - bash) - matching_files=( -name '*.sh' ) - cat > "$tmp" <<"EOF" -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift Logging API open source project -## -## Copyright (c) YEARS Apple Inc. and the Swift Logging API project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of Swift Logging API project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## -EOF - ;; - dtrace) - matching_files=( -name '*.d' ) - cat > "$tmp" <<"EOF" -#!/usr/sbin/dtrace -q -s -/*===----------------------------------------------------------------------===* - * - * This source file is part of the Swift Logging API open source project - * - * Copyright (c) YEARS Apple Inc. and the Swift Logging API project authors - * Licensed under Apache License v2.0 - * - * See LICENSE.txt for license information - * See CONTRIBUTORS.txt for the list of Swift Logging API project authors - * - * SPDX-License-Identifier: Apache-2.0 - * - *===----------------------------------------------------------------------===*/ -EOF - ;; - *) - echo >&2 "ERROR: unknown language '$language'" - ;; - esac - - expected_lines=$(cat "$tmp" | wc -l) - expected_sha=$(cat "$tmp" | shasum) - - ( - cd "$here/.." - find . \ - \( \! -path './.build/*' -a \ - \( "${matching_files[@]}" \) -a \ - \( \! \( "${exceptions[@]}" \) \) \) | while read line; do - if [[ "$(cat "$line" | replace_acceptable_years | head -n $expected_lines | shasum)" != "$expected_sha" ]]; then - printf "\033[0;31mmissing headers in file '$line'!\033[0m\n" - diff -u <(cat "$line" | replace_acceptable_years | head -n $expected_lines) "$tmp" - exit 1 - fi - done - printf "\033[0;32mokay.\033[0m\n" - ) -done - -rm "$tmp"