diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 998a0ebe..9f8d71ff 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,3 +8,11 @@ updates: dependencies: patterns: - "*" + - package-ecosystem: "swift" + directory: "/" + schedule: + interval: "daily" + groups: + dependencies: + patterns: + - "*" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7412c17..6e6a91c3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,13 +10,24 @@ permissions: jobs: unit-tests: - uses: vapor/ci/.github/workflows/run-unit-tests.yml@main - with: - with_windows: true - with_musl: true - secrets: inherit + permissions: + contents: read + uses: vapor/ci/.github/workflows/run-unit-tests.yml@main + with: + with_windows: true + with_musl: true + secrets: inherit + + submit-dependencies: + permissions: + contents: write + if: ${{ github.event_name == 'push' }} + uses: vapor/ci/.github/workflows/submit-deps.yml@main + secrets: inherit integration-check: + permissions: + contents: read runs-on: ubuntu-latest container: swift:noble steps: diff --git a/Package.swift b/Package.swift index 1eceb51c..052f5a3f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.8 +// swift-tools-version:6.1 import PackageDescription let package = Package( @@ -15,8 +15,8 @@ let package = Package( .library(name: "ConsoleKitCommands", targets: ["ConsoleKitCommands"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"), - .package(url: "https://github.com/apple/swift-nio.git", from: "2.62.0"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.11.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.97.0"), ], targets: [ .target( @@ -81,6 +81,10 @@ let package = Package( ) var swiftSettings: [SwiftSetting] { [ - .enableUpcomingFeature("ForwardTrailingClosures"), - .enableUpcomingFeature("ConciseMagicFile"), + .enableUpcomingFeature("ExistentialAny"), + //.enableUpcomingFeature("InternalImportsByDefault"), + .enableUpcomingFeature("MemberImportVisibility"), + .enableUpcomingFeature("InferIsolatedConformances"), + //.enableUpcomingFeature("NonisolatedNonsendingByDefault"), + .enableUpcomingFeature("ImmutableWeakCaptures"), ] } diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift deleted file mode 100644 index 752091e8..00000000 --- a/Package@swift-5.9.swift +++ /dev/null @@ -1,89 +0,0 @@ -// swift-tools-version:5.9 -import PackageDescription - -let package = Package( - name: "console-kit", - platforms: [ - .macOS(.v10_15), - .iOS(.v13), - .watchOS(.v6), - .tvOS(.v13), - ], - products: [ - .library(name: "ConsoleKit", targets: ["ConsoleKit"]), - .library(name: "ConsoleKitTerminal", targets: ["ConsoleKitTerminal"]), - .library(name: "ConsoleKitCommands", targets: ["ConsoleKitCommands"]), - ], - dependencies: [ - .package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"), - .package(url: "https://github.com/apple/swift-nio.git", from: "2.62.0"), - ], - targets: [ - .target( - name: "ConsoleKit", - dependencies: [ - .target(name: "ConsoleKitCommands"), - .target(name: "ConsoleKitTerminal"), - ], - swiftSettings: swiftSettings - ), - .target( - name: "ConsoleKitCommands", - dependencies: [ - .product(name: "Logging", package: "swift-log"), - .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), - .target(name: "ConsoleKitTerminal"), - ], - swiftSettings: swiftSettings - ), - .target( - name: "ConsoleKitTerminal", - dependencies: [ - .product(name: "Logging", package: "swift-log"), - .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), - ], - swiftSettings: swiftSettings - ), - .testTarget( - name: "ConsoleKitTests", - dependencies: [.target(name: "ConsoleKit")], - swiftSettings: swiftSettings - ), - .testTarget( - name: "AsyncConsoleKitTests", - dependencies: [.target(name: "ConsoleKit")], - swiftSettings: swiftSettings - ), - .testTarget( - name: "ConsoleKitPerformanceTests", - dependencies: [.target(name: "ConsoleKit")], - swiftSettings: swiftSettings - ), - .executableTarget( - name: "ConsoleKitExample", - dependencies: [.target(name: "ConsoleKit")], - swiftSettings: swiftSettings - ), - .executableTarget( - name: "ConsoleKitAsyncExample", - dependencies: [.target(name: "ConsoleKit")], - swiftSettings: swiftSettings - ), - .executableTarget( - name: "ConsoleLoggerExample", - dependencies: [ - .target(name: "ConsoleKit"), - .product(name: "Logging", package: "swift-log"), - ], - swiftSettings: swiftSettings - ), - ] -) - -var swiftSettings: [SwiftSetting] { [ - .enableUpcomingFeature("ExistentialAny"), - .enableUpcomingFeature("ForwardTrailingClosures"), - .enableUpcomingFeature("ConciseMagicFile"), - .enableUpcomingFeature("DisableOutwardActorInference"), - .enableExperimentalFeature("StrictConcurrency=complete"), -] } diff --git a/README.md b/README.md index 8d05807f..d4e3c19f 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,13 @@

- - - - ConsoleKit - +ConsoleKit

Documentation Team Chat MIT License -Continuous Integration - -Swift 5.8+ +Continuous Integration +Code Coverage +Swift 6.1+


+Utilities for interacting with a terminal and the commandline in a Swift application. diff --git a/Sources/ConsoleKit/Docs.docc/images/vapor-consolekit-logo.svg b/Sources/ConsoleKit/Docs.docc/images/vapor-consolekit-logo.svg index f3b1e796..0135210e 100644 --- a/Sources/ConsoleKit/Docs.docc/images/vapor-consolekit-logo.svg +++ b/Sources/ConsoleKit/Docs.docc/images/vapor-consolekit-logo.svg @@ -1,21 +1,25 @@ - - - + + + - - - + + + - - - + + + - - + + diff --git a/Sources/ConsoleKit/Docs.docc/theme-settings.json b/Sources/ConsoleKit/Docs.docc/theme-settings.json index 9d414567..d7b909a4 100644 --- a/Sources/ConsoleKit/Docs.docc/theme-settings.json +++ b/Sources/ConsoleKit/Docs.docc/theme-settings.json @@ -1,18 +1,21 @@ { "theme": { - "aside": { "border-radius": "16px", "border-style": "double", "border-width": "3px" }, + "aside": { "border-radius": "16px", "border-width": "3px", "border-style": "double" }, "border-radius": "0", "button": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" }, "code": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" }, "color": { "consolekit": "#392048", - "documentation-intro-fill": "radial-gradient(circle at top, var(--color-consolekit) 30%, #000 100%)", + "documentation-intro-fill": "radial-gradient(circle at top, var(--color-consolekit) 30%, #000 100%)", "documentation-intro-accent": "var(--color-consolekit)", + "hero-eyebrow": "white", + "documentation-intro-figure": "white", + "hero-title": "white", "logo-base": { "dark": "#fff", "light": "#000" }, "logo-shape": { "dark": "#000", "light": "#fff" }, "fill": { "dark": "#000", "light": "#fff" } }, - "icons": { "technology": "/consolekit/images/vapor-consolekit-logo.svg" } + "icons": { "technology": "/consolekit/images/ConsoleKit/vapor-consolekit-logo.svg" } }, "features": { "quickNavigation": { "enable": true }, diff --git a/Sources/ConsoleKitCommands/Async/GenerateAsyncAutocompleteCommand.swift b/Sources/ConsoleKitCommands/Async/GenerateAsyncAutocompleteCommand.swift index cd57e935..cfec9b37 100644 --- a/Sources/ConsoleKitCommands/Async/GenerateAsyncAutocompleteCommand.swift +++ b/Sources/ConsoleKitCommands/Async/GenerateAsyncAutocompleteCommand.swift @@ -1,3 +1,4 @@ +import ConsoleKitTerminal import Foundation struct GenerateAsyncAutocompleteCommand: AsyncCommand { diff --git a/Sources/ConsoleKitCommands/Completion/Completion.swift b/Sources/ConsoleKitCommands/Completion/Completion.swift index e03b1d1c..b2e30524 100644 --- a/Sources/ConsoleKitCommands/Completion/Completion.swift +++ b/Sources/ConsoleKitCommands/Completion/Completion.swift @@ -1,3 +1,5 @@ +import Foundation + /// Shell completion implementations. public enum Shell: String, LosslessStringConvertible, CaseIterable, Sendable { case bash diff --git a/Sources/ConsoleKitCommands/Completion/GenerateAutocompleteCommand.swift b/Sources/ConsoleKitCommands/Completion/GenerateAutocompleteCommand.swift index d837a807..f5ccbb2c 100644 --- a/Sources/ConsoleKitCommands/Completion/GenerateAutocompleteCommand.swift +++ b/Sources/ConsoleKitCommands/Completion/GenerateAutocompleteCommand.swift @@ -1,3 +1,4 @@ +import ConsoleKitTerminal import Foundation struct GenerateAutocompleteCommand: Command { diff --git a/Sources/ConsoleKitCommands/Docs.docc/images/vapor-consolekit-logo.svg b/Sources/ConsoleKitCommands/Docs.docc/images/vapor-consolekit-logo.svg index f3b1e796..0135210e 100644 --- a/Sources/ConsoleKitCommands/Docs.docc/images/vapor-consolekit-logo.svg +++ b/Sources/ConsoleKitCommands/Docs.docc/images/vapor-consolekit-logo.svg @@ -1,21 +1,25 @@ - - - + + + - - - + + + - - - + + + - - + + diff --git a/Sources/ConsoleKitCommands/Docs.docc/theme-settings.json b/Sources/ConsoleKitCommands/Docs.docc/theme-settings.json index 9d414567..7e6dcffa 100644 --- a/Sources/ConsoleKitCommands/Docs.docc/theme-settings.json +++ b/Sources/ConsoleKitCommands/Docs.docc/theme-settings.json @@ -1,18 +1,21 @@ { "theme": { - "aside": { "border-radius": "16px", "border-style": "double", "border-width": "3px" }, + "aside": { "border-radius": "16px", "border-width": "3px", "border-style": "double" }, "border-radius": "0", "button": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" }, "code": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" }, "color": { "consolekit": "#392048", - "documentation-intro-fill": "radial-gradient(circle at top, var(--color-consolekit) 30%, #000 100%)", + "documentation-intro-fill": "radial-gradient(circle at top, var(--color-consolekit) 30%, #000 100%)", "documentation-intro-accent": "var(--color-consolekit)", + "hero-eyebrow": "white", + "documentation-intro-figure": "white", + "hero-title": "white", "logo-base": { "dark": "#fff", "light": "#000" }, "logo-shape": { "dark": "#000", "light": "#fff" }, "fill": { "dark": "#000", "light": "#fff" } }, - "icons": { "technology": "/consolekit/images/vapor-consolekit-logo.svg" } + "icons": { "technology": "/consolekit/images/ConsoleKitCommands/vapor-consolekit-logo.svg" } }, "features": { "quickNavigation": { "enable": true }, diff --git a/Sources/ConsoleKitTerminal/Docs.docc/images/vapor-consolekit-logo.svg b/Sources/ConsoleKitTerminal/Docs.docc/images/vapor-consolekit-logo.svg index f3b1e796..0135210e 100644 --- a/Sources/ConsoleKitTerminal/Docs.docc/images/vapor-consolekit-logo.svg +++ b/Sources/ConsoleKitTerminal/Docs.docc/images/vapor-consolekit-logo.svg @@ -1,21 +1,25 @@ - - - + + + - - - + + + - - - + + + - - + + diff --git a/Sources/ConsoleKitTerminal/Docs.docc/theme-settings.json b/Sources/ConsoleKitTerminal/Docs.docc/theme-settings.json index 9d414567..8e716bd3 100644 --- a/Sources/ConsoleKitTerminal/Docs.docc/theme-settings.json +++ b/Sources/ConsoleKitTerminal/Docs.docc/theme-settings.json @@ -1,18 +1,21 @@ { "theme": { - "aside": { "border-radius": "16px", "border-style": "double", "border-width": "3px" }, + "aside": { "border-radius": "16px", "border-width": "3px", "border-style": "double" }, "border-radius": "0", "button": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" }, "code": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" }, "color": { "consolekit": "#392048", - "documentation-intro-fill": "radial-gradient(circle at top, var(--color-consolekit) 30%, #000 100%)", + "documentation-intro-fill": "radial-gradient(circle at top, var(--color-consolekit) 30%, #000 100%)", "documentation-intro-accent": "var(--color-consolekit)", + "hero-eyebrow": "white", + "documentation-intro-figure": "white", + "hero-title": "white", "logo-base": { "dark": "#fff", "light": "#000" }, "logo-shape": { "dark": "#000", "light": "#fff" }, "fill": { "dark": "#000", "light": "#fff" } }, - "icons": { "technology": "/consolekit/images/vapor-consolekit-logo.svg" } + "icons": { "technology": "/consolekit/images/ConsoleKitTerminal/vapor-consolekit-logo.svg" } }, "features": { "quickNavigation": { "enable": true }, diff --git a/Sources/ConsoleKitTerminal/Terminal/ANSI.swift b/Sources/ConsoleKitTerminal/Terminal/ANSI.swift index 532fe966..35b6ef77 100644 --- a/Sources/ConsoleKitTerminal/Terminal/ANSI.swift +++ b/Sources/ConsoleKitTerminal/Terminal/ANSI.swift @@ -1,6 +1,15 @@ -import Foundation -#if canImport(Android) -import Android +#if canImport(Darwin) +import Darwin.C +#elseif canImport(Glibc) +@preconcurrency import Glibc +#elseif canImport(Musl) +@preconcurrency import Musl +#elseif canImport(Android) +@preconcurrency import Android +#elseif os(WASI) +import WASILibc +#elseif os(Windows) +import CRT #endif /// Terminal ANSI commands @@ -51,8 +60,12 @@ extension Terminal { // fdopen() on stdout is fast; also the returned file MUST NOT be fclose()d // This avoids concurrency complaints due to accessing global `stdout`. + #if os(Windows) + fflush(_fdopen(_fileno(stdout), "w+")) + #else fflush(fdopen(STDOUT_FILENO, "w+")) - + #endif + } } diff --git a/Sources/ConsoleKitTerminal/Terminal/Console.swift b/Sources/ConsoleKitTerminal/Terminal/Console.swift index 89e5e182..f9612448 100644 --- a/Sources/ConsoleKitTerminal/Terminal/Console.swift +++ b/Sources/ConsoleKitTerminal/Terminal/Console.swift @@ -1,6 +1,15 @@ -import Foundation -#if canImport(Android) -import Android +#if canImport(Darwin) +import Darwin.C +#elseif canImport(Glibc) +@preconcurrency import Glibc +#elseif canImport(Musl) +@preconcurrency import Musl +#elseif canImport(Android) +@preconcurrency import Android +#elseif os(WASI) +import WASILibc +#elseif os(Windows) +import CRT #endif /// Protocol for powering styled Console I/O. @@ -91,6 +100,8 @@ extension Console { #if Xcode // Xcode output does not support ANSI commands return false + #elseif os(Windows) + return _isatty(_fileno(stdout)) > 0 #else // If STDOUT is not an interactive terminal then omit ANSI commands return isatty(STDOUT_FILENO) > 0 diff --git a/Sources/ConsoleKitTerminal/Terminal/Terminal.swift b/Sources/ConsoleKitTerminal/Terminal/Terminal.swift index ba05514e..3ca15cb7 100644 --- a/Sources/ConsoleKitTerminal/Terminal/Terminal.swift +++ b/Sources/ConsoleKitTerminal/Terminal/Terminal.swift @@ -1,11 +1,18 @@ -import Foundation -import NIOConcurrencyHelpers -#if canImport(Android) -import Android -#endif -#if os(Windows) +#if canImport(Darwin) +import Darwin.C +#elseif canImport(Glibc) +@preconcurrency import Glibc +#elseif canImport(Musl) +@preconcurrency import Musl +#elseif canImport(Android) +@preconcurrency import Android +#elseif os(WASI) +import WASILibc +#elseif os(Windows) +import CRT import WinSDK #endif +import NIOConcurrencyHelpers /// Generic console that uses a mixture of Swift standard /// library and Foundation code to fulfill protocol requirements. diff --git a/Sources/ConsoleKitTerminal/Utilities/ConsoleLogger.swift b/Sources/ConsoleKitTerminal/Utilities/ConsoleLogger.swift index c2f6db19..e0e0a027 100644 --- a/Sources/ConsoleKitTerminal/Utilities/ConsoleLogger.swift +++ b/Sources/ConsoleKitTerminal/Utilities/ConsoleLogger.swift @@ -1,136 +1,78 @@ import Logging -/// A `LoggerFragment` which implements the default logger message format. -public func defaultLoggerFragment() -> some LoggerFragment { +/// The complete explicit type of the ``defaultLoggerFragment()``. Unfortunately, we have to +/// spell it out instead of just letting it be `some LoggerFragment` in order to define the +/// ``ConsoleLogger`` alias properly. +public typealias DefaultLoggerFragmentType = AndFragment, AndFragment, SeparatorFragment>>, SeparatorFragment>, IfMaxLevelFragment>> + +/// A ``LoggerFragment`` which implements the default logger message format. +public func defaultLoggerFragment() -> DefaultLoggerFragmentType { LabelFragment().maxLevel(.trace) .and(LevelFragment().separated(" ").and(MessageFragment().separated(" "))) .and(MetadataFragment().separated(" ")) .and(SourceLocationFragment().separated(" ").maxLevel(.debug)) } -/// A `LoggerFragment` which implements the default logger message format with a timestamp at the front. +/// A ``LoggerFragment`` which implements the default logger message format with a timestamp at the front. public func timestampDefaultLoggerFragment( timestampSource: some TimestampSource = SystemTimestampSource() ) -> some LoggerFragment { TimestampFragment(timestampSource).and(defaultLoggerFragment().separated(" ")) } -/// Outputs logs to a `Console` via a `LoggerFragment` pipeline. +/// Outputs logs to a ``Console`` via a ``LoggerFragment`` pipeline. public struct ConsoleFragmentLogger: LogHandler, Sendable { + /// The log handler's label. public let label: String - - /// See `LogHandler.metadata`. + + // See `LogHandler.metadata`. public var metadata: Logger.Metadata - /// See `LogHandler.metadataProvider`. + // See `LogHandler.metadataProvider`. public var metadataProvider: Logger.MetadataProvider? - /// See `LogHandler.logLevel`. + // See `LogHandler.logLevel`. public var logLevel: Logger.Level - /// The conosle that the messages will get logged to. + /// The console to which messages will be logged. public let console: any Console - /// The `LoggerFragment` this logger outputs through. - public var fragment: T - - /// Creates a new `ConsoleLogger` instance. + private var _fragment: T + + /// The ``LoggerFragment`` this logger outputs through. + public var fragment: T { + get { self._fragment } + @available(*, deprecated, message: "Setting ConsoleFragmentLogger's fragment after creation is deprecated.") + set { self._fragment = newValue } + } + + /// Creates a new ``ConsoleFragmentLogger`` instance. /// /// - Parameters: - /// - fragment: The `LoggerFragment` this logger outputs through. - /// - label: Unique identifier for this logger. + /// - fragment: The ``LoggerFragment`` this handler outputs through. + /// - label: Unique identifier for this handler. /// - console: The console to log the messages to. - /// - level: The minimum level of message that the logger will output. This defaults to `.debug`, the lowest level. - /// - metadata: Extra metadata to log with the message. This defaults to an empty dictionary. - public init(fragment: T, label: String, console: any Console, level: Logger.Level = .debug, metadata: Logger.Metadata = [:]) { - self.fragment = fragment + /// - level: The minimum level of message that the handler will output. Defaults to `.debug`. + /// - metadata: Extra metadata to log with the message. Defaults to an empty dictionary. + public init(fragment: T = defaultLoggerFragment(), label: String, console: any Console, level: Logger.Level = .debug, metadata: Logger.Metadata = [:]) { + self._fragment = fragment self.label = label self.metadata = metadata self.logLevel = level self.console = console } - public init(fragment: T, label: String, console: any Console, level: Logger.Level = .debug, metadata: Logger.Metadata = [:], metadataProvider: Logger.MetadataProvider?) { - self.fragment = fragment - self.label = label - self.metadata = metadata - self.logLevel = level - self.console = console - self.metadataProvider = metadataProvider - } - - /// See `LogHandler[metadataKey:]`. - /// - /// This just acts as a getter/setter for the `.metadata` property. - public subscript(metadataKey key: String) -> Logger.Metadata.Value? { - get { return self.metadata[key] } - set { self.metadata[key] = newValue } - } - - /// See `LogHandler.log(level:message:metadata:source:file:function:line:)`. - public func log( - level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt - ) { - var output = FragmentOutput() - var record = LogRecord( - level: level, - message: message, - metadata: metadata, - source: source, - file: file, - function: function, - line: line, - label: self.label, - loggerLevel: self.logLevel, - loggerMetadata: self.metadata, - metadataProvider: self.metadataProvider - ) - - self.fragment.write(&record, to: &output) - - self.console.output(output.text) - } -} - -/// Outputs logs to a `Console`. -public struct ConsoleLogger: LogHandler, Sendable { - public let label: String - - /// See `LogHandler.metadata`. - public var metadata: Logger.Metadata - - /// See `LogHandler.metadataProvider`. - public var metadataProvider: Logger.MetadataProvider? - - /// See `LogHandler.logLevel`. - public var logLevel: Logger.Level - - /// The conosle that the messages will get logged to. - public let console: any Console - - public var fragment: some LoggerFragment = defaultLoggerFragment() - - /// Creates a new `ConsoleLogger` instance. + /// Creates a new ``ConsoleFragmentLogger`` instance. /// /// - Parameters: - /// - label: Unique identifier for this logger. + /// - fragment: The ``LoggerFragment`` this handler outputs through. + /// - label: Unique identifier for this handler. /// - console: The console to log the messages to. - /// - level: The minimum level of message that the logger will output. This defaults to `.debug`, the lowest level. - /// - metadata: Extra metadata to log with the message. This defaults to an empty dictionary. - public init(label: String, console: any Console, level: Logger.Level = .debug, metadata: Logger.Metadata = [:]) { - self.label = label - self.metadata = metadata - self.logLevel = level - self.console = console - } - - public init(label: String, console: any Console, level: Logger.Level = .debug, metadata: Logger.Metadata = [:], metadataProvider: Logger.MetadataProvider?) { + /// - level: The minimum level of message that the handler will output. Defaults to `.debug`. + /// - metadata: Extra metadata to log with the message. Defaults to an empty dictionary. + /// - metadataProvider: A metadata provider to associate with this handler. + public init(fragment: T = defaultLoggerFragment(), label: String, console: any Console, level: Logger.Level = .debug, metadata: Logger.Metadata = [:], metadataProvider: Logger.MetadataProvider?) { + self._fragment = fragment self.label = label self.metadata = metadata self.logLevel = level @@ -138,50 +80,43 @@ public struct ConsoleLogger: LogHandler, Sendable { self.metadataProvider = metadataProvider } - /// See `LogHandler[metadataKey:]`. - /// - /// This just acts as a getter/setter for the `.metadata` property. + // See `LogHandler.subscript(metadataKey:)`. public subscript(metadataKey key: String) -> Logger.Metadata.Value? { get { return self.metadata[key] } set { self.metadata[key] = newValue } } - - /// See `LogHandler.log(level:message:metadata:source:file:function:line:)`. - public func log( - level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt - ) { + + // See `LogHandler.log(event:)`. + public func log(event: LogEvent) { var output = FragmentOutput() - var record = LogRecord( - level: level, - message: message, - metadata: metadata, - source: source, - file: file, - function: function, - line: line, + level: event.level, + message: event.message, + metadata: event.metadata, + source: event.source, + file: event.file, + function: event.function, + line: event.line, label: self.label, loggerLevel: self.logLevel, loggerMetadata: self.metadata, metadataProvider: self.metadataProvider ) - + self.fragment.write(&record, to: &output) - self.console.output(output.text) } } +/// Outputs logs to a ``Console`` using the ``defaultLoggerFragment()``. +public typealias ConsoleLogger = ConsoleFragmentLogger + extension LoggingSystem { - /// Bootstraps a `ConsoleLogger` to the `LoggingSystem`, so that logger will be used in `Logger.init(label:)`. + /// Bootstraps a ``ConsoleLogger`` to the `LoggingSystem` as the default log handler. /// - /// LoggingSystem.boostrap(console: console) + /// ```swift + /// LoggingSystem.boostrap(console: console) + /// ``` /// /// - Parameters: /// - console: The console the logger will log the messages to. @@ -195,9 +130,11 @@ extension LoggingSystem { self.bootstrap(console: console, level: level, metadata: metadata, metadataProvider: nil) } - /// Bootstraps a `ConsoleLogger` to the `LoggingSystem`, so that logger will be used in `Logger.init(label:)`. + /// Bootstraps a ``ConsoleLogger`` to the `LoggingSystem` as the default log handler. /// - /// LoggingSystem.boostrap(console: console) + /// ```swift + /// LoggingSystem.boostrap(console: console) + /// ``` /// /// - Parameters: /// - console: The console the logger will log the messages to. @@ -210,14 +147,16 @@ extension LoggingSystem { metadata: Logger.Metadata = [:], metadataProvider: Logger.MetadataProvider? = nil ) { - self.bootstrap({ (label, metadataProvider) in + self.bootstrap({ label, metadataProvider in return ConsoleLogger(label: label, console: console, level: level, metadata: metadata, metadataProvider: metadataProvider) }, metadataProvider: metadataProvider) } - /// Bootstraps a `ConsoleFragmentLogger` to the `LoggingSystem`, so that logger will be used in `Logger.init(label:)`. + /// Bootstraps a ``ConsoleFragmentLogger`` to the `LoggingSystem` as the default log handler. /// - /// LoggingSystem.boostrap(console: console) + /// ```swift + /// LoggingSystem.boostrap(fragment: timestampDefaultLoggerFragment()) + /// ``` /// /// - Parameters: /// - fragment: The logger fragment which will be used to build the logged messages. @@ -232,8 +171,8 @@ extension LoggingSystem { metadata: Logger.Metadata = [:], metadataProvider: Logger.MetadataProvider? = nil ) { - self.bootstrap({ (label, metadataProvider) in - return ConsoleFragmentLogger(fragment: fragment, label: label, console: console, level: level, metadata: metadata, metadataProvider: metadataProvider) + self.bootstrap({ label, metadataProvider in + ConsoleFragmentLogger(fragment: fragment, label: label, console: console, level: level, metadata: metadata, metadataProvider: metadataProvider) }, metadataProvider: metadataProvider) } } @@ -249,16 +188,9 @@ extension Logger.Level { case .critical: return ConsoleStyle(color: .brightRed) } } - + + @available(*, deprecated, renamed: "rawValue", message: "Use `Logger.Level.rawValue` instead") public var name: String { - switch self { - case .trace: return "TRACE" - case .debug: return "DEBUG" - case .info: return "INFO" - case .notice: return "NOTICE" - case .warning: return "WARNING" - case .error: return "ERROR" - case .critical: return "CRITICAL" - } + self.rawValue.uppercased() } } diff --git a/Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift b/Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift index 4b6ce9ab..3daa729b 100644 --- a/Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift +++ b/Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift @@ -2,6 +2,8 @@ import Logging import Foundation /// Information about a specific log message, including information from the logger the message was logged to. +/// +/// > Note: This type is obsoleted by the new `LogEvent` type in `swift-log`. public struct LogRecord { public init(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata? = nil, source: String, file: String, function: String, line: UInt, label: String, loggerLevel: Logger.Level, loggerMetadata: Logger.Metadata, metadataProvider: Logger.MetadataProvider? = nil) { self.level = level @@ -188,7 +190,7 @@ public struct LevelFragment: LoggerFragment { public init() { } public func write(_ record: inout LogRecord, to output: inout FragmentOutput) { - output += "[ \(record.level.name) ]".consoleText(record.level.style) + output += "[ \(record.level.rawValue.uppercased()) ]".consoleText(record.level.style) output.needsSeparator = true } } diff --git a/Tests/AsyncConsoleKitTests/AsyncUtilities.swift b/Tests/AsyncConsoleKitTests/AsyncUtilities.swift index 44d6eaa0..3770c57b 100644 --- a/Tests/AsyncConsoleKitTests/AsyncUtilities.swift +++ b/Tests/AsyncConsoleKitTests/AsyncUtilities.swift @@ -2,8 +2,6 @@ import ConsoleKit import XCTest import NIOConcurrencyHelpers -extension String: Error {} - final class TestGroup: AsyncCommandGroup { struct Signature: CommandSignature { @Flag(name: "version", help: "Prints the version") diff --git a/Tests/ConsoleKitTests/LoggingTests.swift b/Tests/ConsoleKitTests/LoggingTests.swift index 420de1f4..c38e45f6 100644 --- a/Tests/ConsoleKitTests/LoggingTests.swift +++ b/Tests/ConsoleKitTests/LoggingTests.swift @@ -134,7 +134,7 @@ final class ConsoleLoggerTests: XCTestCase { // Remove the timezone, since there doesn't appear to be a good way to mock it with strftime. while logged.removeFirst() != " " { } - XCTAssertEqual(logged, "[ \(Logger.Level.info.name) ] logged (ConsoleKitTests/LoggingTests.swift:1)\n") + XCTAssertEqual(logged, "[ \(Logger.Level.info.rawValue.uppercased()) ] logged (ConsoleKitTests/LoggingTests.swift:1)\n") } func testSourceFragment() { @@ -150,12 +150,12 @@ final class ConsoleLoggerTests: XCTestCase { logger.info("logged", line: 1) - XCTAssertEqual(console.testOutputQueue.first, "ConsoleKitTests [ \(Logger.Level.info.name) ] logged (ConsoleKitTests/LoggingTests.swift:1)\n") + XCTAssertEqual(console.testOutputQueue.first, "ConsoleKitTests [ \(Logger.Level.info.rawValue.uppercased()) ] logged (ConsoleKitTests/LoggingTests.swift:1)\n") } } private func XCTAssertLog(_ console: TestConsole, _ level: Logger.Level, _ message: String, file: StaticString = #filePath, line: UInt = #line) { - XCTAssertEqual(console.testOutputQueue.first ?? "", "[ \(level.name) ] \(message)\n", file: file, line: line) + XCTAssertEqual(console.testOutputQueue.first ?? "", "[ \(level.rawValue.uppercased()) ] \(message)\n", file: file, line: line) } enum TraceNamespace { diff --git a/Tests/ConsoleKitTests/Utilities.swift b/Tests/ConsoleKitTests/Utilities.swift index df5b86ea..65aeb8ad 100644 --- a/Tests/ConsoleKitTests/Utilities.swift +++ b/Tests/ConsoleKitTests/Utilities.swift @@ -2,8 +2,6 @@ import ConsoleKit import XCTest import NIOConcurrencyHelpers -extension String: Error {} - final class TestGroup: CommandGroup { struct Signature: CommandSignature { @Flag(name: "version", help: "Prints the version")