From f18848104a33942d3e6ab1847245f363f836f8e3 Mon Sep 17 00:00:00 2001 From: nervenes Date: Fri, 22 Nov 2024 18:26:38 +0100 Subject: [PATCH 1/6] make logging optional and update docs --- README.md | 7 ++-- ... adopt ServiceLifecycle in applications.md | 25 ++++----------- Sources/ServiceLifecycle/ServiceGroup.swift | 10 ++++-- .../ServiceGroupConfiguration.swift | 32 ++++++++++++++----- 4 files changed, 41 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 4fb54ce..6767f0a 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ Furthermore, the group will setup signal listeners for the configured signals an on each service. ```swift +import ServiceLifecycle + actor FooService: Service { func run() async throws { print("FooService starting") @@ -78,13 +80,12 @@ struct Application { let serviceGroup = ServiceGroup( services: [service1, service2], - configuration: .init(gracefulShutdownSignals: [.sigterm]), - logger: logger + gracefulShutdownSignals: [.sigterm] ) + try await serviceGroup.run() } } - ``` ## Security diff --git a/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md b/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md index 0364216..5b3e93b 100644 --- a/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md +++ b/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md @@ -77,13 +77,7 @@ struct Application { let serviceGroup = ServiceGroup( // We are encoding the dependency hierarchy here by listing the fooService first - configuration: .init( - services: [ - .init(service: fooService), - .init(service: barService) - ], - logger: logger - ), + services: [fooService, barService] ) try await serviceGroup.run() @@ -148,11 +142,8 @@ struct Application { }) let serviceGroup = ServiceGroup( - configuration: .init( - services: [.init(service: streamingService)], - gracefulShutdownSignals: [.sigterm], - logger: logger - ) + services: [streamingService], + gracefulShutdownSignals: [.sigterm] ) try await serviceGroup.run() @@ -210,11 +201,8 @@ struct Application { }) let serviceGroup = ServiceGroup( - configuration: .init( - services: [.init(service: streamingService)], - gracefulShutdownSignals: [.sigterm], - logger: logger - ) + services: [streamingService], + gracefulShutdownSignals: [.sigterm] ) try await serviceGroup.run() @@ -266,8 +254,7 @@ struct Application { successTerminationBehavior: .shutdownGracefully, failureTerminationBehavior: .shutdownGracefully ) - ], - logger: logger + ] ), ) diff --git a/Sources/ServiceLifecycle/ServiceGroup.swift b/Sources/ServiceLifecycle/ServiceGroup.swift index 120983b..ee8be2b 100644 --- a/Sources/ServiceLifecycle/ServiceGroup.swift +++ b/Sources/ServiceLifecycle/ServiceGroup.swift @@ -76,19 +76,23 @@ public actor ServiceGroup: Sendable, Service { services: [any Service], gracefulShutdownSignals: [UnixSignal] = [], cancellationSignals: [UnixSignal] = [], - logger: Logger + logger: Logger? = nil ) { + if logger == nil { + LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } + } + let configuration = ServiceGroupConfiguration( services: services.map { ServiceGroupConfiguration.ServiceConfiguration(service: $0) }, gracefulShutdownSignals: gracefulShutdownSignals, cancellationSignals: cancellationSignals, - logger: logger + logger: logger ?? .init(label: "") ) self.init(configuration: configuration) } - @available(*, deprecated) + @available(*, deprecated, renamed: "init(services:gracefulShutdownSignals:cancellationSignals:logger:)") public init( services: [any Service], configuration: ServiceGroupConfiguration, diff --git a/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift b/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift index 30b21ff..c4d800d 100644 --- a/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift +++ b/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift @@ -172,10 +172,14 @@ public struct ServiceGroupConfiguration: Sendable { /// - logger: The group's logger. public init( services: [ServiceConfiguration], - logger: Logger + logger: Logger? = nil ) { + if logger == nil { + LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } + } + self.services = services - self.logger = logger + self.logger = logger ?? .init(label: "") } /// Initializes a new ``ServiceGroupConfiguration``. @@ -189,10 +193,14 @@ public struct ServiceGroupConfiguration: Sendable { services: [ServiceConfiguration], gracefulShutdownSignals: [UnixSignal] = [], cancellationSignals: [UnixSignal] = [], - logger: Logger + logger: Logger? = nil ) { + if logger == nil { + LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } + } + self.services = services - self.logger = logger + self.logger = logger ?? .init(label: "") self.gracefulShutdownSignals = gracefulShutdownSignals self.cancellationSignals = cancellationSignals } @@ -204,10 +212,14 @@ public struct ServiceGroupConfiguration: Sendable { /// - logger: The group's logger. public init( services: [Service], - logger: Logger + logger: Logger? = nil ) { + if logger == nil { + LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } + } + self.services = Array(services.map { ServiceConfiguration(service: $0) }) - self.logger = logger + self.logger = logger ?? .init(label: "") } /// Initializes a new ``ServiceGroupConfiguration``. @@ -221,10 +233,14 @@ public struct ServiceGroupConfiguration: Sendable { services: [Service], gracefulShutdownSignals: [UnixSignal] = [], cancellationSignals: [UnixSignal] = [], - logger: Logger + logger: Logger? = nil ) { + if logger == nil { + LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } + } + self.services = Array(services.map { ServiceConfiguration(service: $0) }) - self.logger = logger + self.logger = logger ?? .init(label: "") self.gracefulShutdownSignals = gracefulShutdownSignals self.cancellationSignals = cancellationSignals } From dbf03659d4bdd0de69ccbe6b65d03ee2c55992ec Mon Sep 17 00:00:00 2001 From: nervenes Date: Fri, 22 Nov 2024 18:56:01 +0100 Subject: [PATCH 2/6] make logger property an optional as well --- Sources/ServiceLifecycle/ServiceGroup.swift | 70 +++++++++---------- .../ServiceGroupConfiguration.swift | 26 ++----- 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/Sources/ServiceLifecycle/ServiceGroup.swift b/Sources/ServiceLifecycle/ServiceGroup.swift index ee8be2b..a1e9839 100644 --- a/Sources/ServiceLifecycle/ServiceGroup.swift +++ b/Sources/ServiceLifecycle/ServiceGroup.swift @@ -30,7 +30,7 @@ public actor ServiceGroup: Sendable, Service { } /// The logger. - private let logger: Logger + private let logger: Logger? /// The logging configuration. private let loggingConfiguration: ServiceGroupConfiguration.LoggingConfiguration /// The maximum amount of time that graceful shutdown is allowed to take. @@ -55,7 +55,7 @@ public actor ServiceGroup: Sendable, Service { Set(configuration.gracefulShutdownSignals).isDisjoint(with: configuration.cancellationSignals), "Overlapping graceful shutdown and cancellation signals" ) - precondition(configuration.logger.label != deprecatedLoggerLabel, "Please migrate to the new initializers") + precondition(configuration.logger?.label != deprecatedLoggerLabel, "Please migrate to the new initializers") self.state = .initial(services: configuration.services) self.gracefulShutdownSignals = configuration.gracefulShutdownSignals self.cancellationSignals = configuration.cancellationSignals @@ -78,15 +78,11 @@ public actor ServiceGroup: Sendable, Service { cancellationSignals: [UnixSignal] = [], logger: Logger? = nil ) { - if logger == nil { - LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } - } - let configuration = ServiceGroupConfiguration( services: services.map { ServiceGroupConfiguration.ServiceConfiguration(service: $0) }, gracefulShutdownSignals: gracefulShutdownSignals, cancellationSignals: cancellationSignals, - logger: logger ?? .init(label: "") + logger: logger ) self.init(configuration: configuration) @@ -208,7 +204,7 @@ public actor ServiceGroup: Sendable, Service { services: inout [ServiceGroupConfiguration.ServiceConfiguration], gracefulShutdownStream: AsyncStream ) async throws { - self.logger.debug( + self.logger?.debug( "Starting service lifecycle", metadata: [ self.loggingConfiguration.keys.gracefulShutdownSignalsKey: "\(self.gracefulShutdownSignals)", @@ -274,7 +270,7 @@ public actor ServiceGroup: Sendable, Service { gracefulShutdownManagers.reserveCapacity(services.count) for (index, serviceConfiguration) in services.enumerated() { - self.logger.debug( + self.logger?.debug( "Starting service", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(serviceConfiguration.service)" @@ -328,7 +324,7 @@ public actor ServiceGroup: Sendable, Service { switch service.successTerminationBehavior.behavior { case .cancelGroup: - self.logger.debug( + self.logger?.debug( "Service finished unexpectedly. Cancelling group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -341,7 +337,7 @@ public actor ServiceGroup: Sendable, Service { return .failure(ServiceGroupError.serviceFinishedUnexpectedly()) case .gracefullyShutdownGroup: - self.logger.debug( + self.logger?.debug( "Service finished. Gracefully shutting down group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -361,7 +357,7 @@ public actor ServiceGroup: Sendable, Service { } case .ignore: - self.logger.debug( + self.logger?.debug( "Service finished.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -370,7 +366,7 @@ public actor ServiceGroup: Sendable, Service { services[index] = nil if services.allSatisfy({ $0 == nil }) { - self.logger.debug( + self.logger?.debug( "All services finished." ) self.cancelGroupAndSpawnTimeoutIfNeeded( @@ -384,7 +380,7 @@ public actor ServiceGroup: Sendable, Service { case .serviceThrew(let service, let index, let serviceError): switch service.failureTerminationBehavior.behavior { case .cancelGroup: - self.logger.debug( + self.logger?.debug( "Service threw error. Cancelling group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -398,7 +394,7 @@ public actor ServiceGroup: Sendable, Service { return .failure(serviceError) case .gracefullyShutdownGroup: - self.logger.debug( + self.logger?.debug( "Service threw error. Shutting down group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -420,7 +416,7 @@ public actor ServiceGroup: Sendable, Service { } case .ignore: - self.logger.debug( + self.logger?.debug( "Service threw error.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -430,7 +426,7 @@ public actor ServiceGroup: Sendable, Service { services[index] = nil if services.allSatisfy({ $0 == nil }) { - self.logger.debug( + self.logger?.debug( "All services finished." ) @@ -445,7 +441,7 @@ public actor ServiceGroup: Sendable, Service { case .signalCaught(let unixSignal): if self.gracefulShutdownSignals.contains(unixSignal) { // Let's initiate graceful shutdown. - self.logger.debug( + self.logger?.debug( "Signal caught. Shutting down the group.", metadata: [ self.loggingConfiguration.keys.signalKey: "\(unixSignal)" @@ -464,7 +460,7 @@ public actor ServiceGroup: Sendable, Service { } } else { // Let's cancel the group. - self.logger.debug( + self.logger?.debug( "Signal caught. Cancelling the group.", metadata: [ self.loggingConfiguration.keys.signalKey: "\(unixSignal)" @@ -479,7 +475,7 @@ public actor ServiceGroup: Sendable, Service { case .gracefulShutdownCaught: // We got a manual or inherited graceful shutdown. Let's initiate graceful shutdown. - self.logger.debug("Graceful shutdown caught. Cascading shutdown to services") + self.logger?.debug("Graceful shutdown caught. Cascading shutdown to services") do { try await self.shutdownGracefully( @@ -496,7 +492,7 @@ public actor ServiceGroup: Sendable, Service { case .cancellationCaught: // We caught cancellation in our child task so we have to spawn // our cancellation timeout task if needed - self.logger.debug("Caught cancellation.") + self.logger?.debug("Caught cancellation.") self.cancelGroupAndSpawnTimeoutIfNeeded( group: &group, cancellationTimeoutTask: &cancellationTimeoutTask @@ -521,7 +517,7 @@ public actor ServiceGroup: Sendable, Service { return .success(()) } - self.logger.debug( + self.logger?.debug( "Service lifecycle ended" ) cancellationTimeoutTask?.cancel() @@ -562,12 +558,12 @@ public actor ServiceGroup: Sendable, Service { .enumerated().reversed() { guard let service = services[gracefulShutdownIndex] else { - self.logger.debug( + self.logger?.debug( "Service already finished. Skipping shutdown" ) continue gracefulShutdownLoop } - self.logger.debug( + self.logger?.debug( "Triggering graceful shutdown for service", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -587,7 +583,7 @@ public actor ServiceGroup: Sendable, Service { guard index == gracefulShutdownIndex else { // Another service exited unexpectedly - self.logger.debug( + self.logger?.debug( "Service finished unexpectedly during graceful shutdown. Cancelling all other services now", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -602,7 +598,7 @@ public actor ServiceGroup: Sendable, Service { } // The service that we signalled graceful shutdown did exit/ // We can continue to the next one. - self.logger.debug( + self.logger?.debug( "Service finished", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -614,7 +610,7 @@ public actor ServiceGroup: Sendable, Service { services[index] = nil switch service.failureTerminationBehavior.behavior { case .cancelGroup: - self.logger.debug( + self.logger?.debug( "Service threw error during graceful shutdown. Cancelling group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -632,7 +628,7 @@ public actor ServiceGroup: Sendable, Service { guard index == gracefulShutdownIndex else { // Another service threw while we were waiting for a shutdown // We have to continue the iterating the task group's result - self.logger.debug( + self.logger?.debug( "Another service than the service that we were shutting down threw. Continuing with the next one.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -643,7 +639,7 @@ public actor ServiceGroup: Sendable, Service { } // The service that we were shutting down right now threw. Since it's failure // behaviour is to shutdown the group we can continue - self.logger.debug( + self.logger?.debug( "The service that we were shutting down threw. Continuing with the next one.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -656,7 +652,7 @@ public actor ServiceGroup: Sendable, Service { guard index == gracefulShutdownIndex else { // Another service threw while we were waiting for a shutdown // We have to continue the iterating the task group's result - self.logger.debug( + self.logger?.debug( "Another service than the service that we were shutting down threw. Continuing with the next one.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -667,7 +663,7 @@ public actor ServiceGroup: Sendable, Service { } // The service that we were shutting down right now threw. Since it's failure // behaviour is to shutdown the group we can continue - self.logger.debug( + self.logger?.debug( "The service that we were shutting down threw. Continuing with the next one.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -680,7 +676,7 @@ public actor ServiceGroup: Sendable, Service { case .signalCaught(let signal): if self.cancellationSignals.contains(signal) { // We got signalled cancellation after graceful shutdown - self.logger.debug( + self.logger?.debug( "Signal caught. Cancelling the group.", metadata: [ self.loggingConfiguration.keys.signalKey: "\(signal)" @@ -696,7 +692,7 @@ public actor ServiceGroup: Sendable, Service { case .gracefulShutdownTimedOut: // Gracefully shutting down took longer than the user configured // so we have to escalate it now. - self.logger.debug( + self.logger?.debug( "Graceful shutdown took longer than allowed by the configuration. Cancelling the group now.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -710,7 +706,7 @@ public actor ServiceGroup: Sendable, Service { case .cancellationCaught: // We caught cancellation in our child task so we have to spawn // our cancellation timeout task if needed - self.logger.debug("Caught cancellation.") + self.logger?.debug("Caught cancellation.") self.cancelGroupAndSpawnTimeoutIfNeeded( group: &group, cancellationTimeoutTask: &cancellationTimeoutTask @@ -745,7 +741,7 @@ public actor ServiceGroup: Sendable, Service { ) { guard cancellationTimeoutTask == nil else { // We already have a cancellation timeout task running. - self.logger.debug( + self.logger?.debug( "Task cancellation timeout task already running." ) return @@ -760,7 +756,7 @@ public actor ServiceGroup: Sendable, Service { // from being cancelled. cancellationTimeoutTask = Task { do { - self.logger.debug( + self.logger?.debug( "Task cancellation timeout task started." ) try await Task.sleep( @@ -769,7 +765,7 @@ public actor ServiceGroup: Sendable, Service { attosecondsComponent: maximumCancellationDuration.attosecondsComponent ) ) - self.logger.debug( + self.logger?.debug( "Cancellation took longer than allowed by the configuration." ) fatalError("Cancellation took longer than allowed by the configuration.") diff --git a/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift b/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift index c4d800d..cc10328 100644 --- a/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift +++ b/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift @@ -107,7 +107,7 @@ public struct ServiceGroupConfiguration: Sendable { public var cancellationSignals = [UnixSignal]() /// The group's logger. - public var logger: Logger + public var logger: Logger? /// The group's logging configuration. public var logging = LoggingConfiguration() @@ -174,12 +174,8 @@ public struct ServiceGroupConfiguration: Sendable { services: [ServiceConfiguration], logger: Logger? = nil ) { - if logger == nil { - LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } - } - self.services = services - self.logger = logger ?? .init(label: "") + self.logger = logger } /// Initializes a new ``ServiceGroupConfiguration``. @@ -195,12 +191,8 @@ public struct ServiceGroupConfiguration: Sendable { cancellationSignals: [UnixSignal] = [], logger: Logger? = nil ) { - if logger == nil { - LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } - } - self.services = services - self.logger = logger ?? .init(label: "") + self.logger = logger self.gracefulShutdownSignals = gracefulShutdownSignals self.cancellationSignals = cancellationSignals } @@ -214,12 +206,8 @@ public struct ServiceGroupConfiguration: Sendable { services: [Service], logger: Logger? = nil ) { - if logger == nil { - LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } - } - self.services = Array(services.map { ServiceConfiguration(service: $0) }) - self.logger = logger ?? .init(label: "") + self.logger = logger } /// Initializes a new ``ServiceGroupConfiguration``. @@ -235,12 +223,8 @@ public struct ServiceGroupConfiguration: Sendable { cancellationSignals: [UnixSignal] = [], logger: Logger? = nil ) { - if logger == nil { - LoggingSystem.bootstrap { SwiftLogNoOpLogHandler($0) } - } - self.services = Array(services.map { ServiceConfiguration(service: $0) }) - self.logger = logger ?? .init(label: "") + self.logger = logger self.gracefulShutdownSignals = gracefulShutdownSignals self.cancellationSignals = cancellationSignals } From f219409522cb128bcf8e792f389ab914bea6b0af Mon Sep 17 00:00:00 2001 From: nervenes Date: Fri, 22 Nov 2024 18:56:57 +0100 Subject: [PATCH 3/6] include logger in the readme example --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6767f0a..413b592 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ on each service. ```swift import ServiceLifecycle +import Logging actor FooService: Service { func run() async throws { @@ -78,9 +79,12 @@ struct Application { let service1 = FooService() let service2 = FooService() + let logger = Logger(label: "Application") + let serviceGroup = ServiceGroup( services: [service1, service2], - gracefulShutdownSignals: [.sigterm] + gracefulShutdownSignals: [.sigterm], + logger: logger ) try await serviceGroup.run() From 85fcb6b02676f3b13026da30428ef05af79959e3 Mon Sep 17 00:00:00 2001 From: nervenes Date: Fri, 22 Nov 2024 19:03:26 +0100 Subject: [PATCH 4/6] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 413b592..6fdd306 100644 --- a/README.md +++ b/README.md @@ -75,12 +75,12 @@ actor FooService: Service { @main struct Application { + static let logger = Logger(label: "Application") + static func main() async throws { let service1 = FooService() let service2 = FooService() - let logger = Logger(label: "Application") - let serviceGroup = ServiceGroup( services: [service1, service2], gracefulShutdownSignals: [.sigterm], From 56887c146e2c624c4b2e6a0ef7dfa68c7b8094a4 Mon Sep 17 00:00:00 2001 From: nervenes Date: Sun, 1 Dec 2024 10:11:39 +0100 Subject: [PATCH 5/6] revert logger to non optional --- ... adopt ServiceLifecycle in applications.md | 34 ++++++++-- Sources/ServiceLifecycle/ServiceGroup.swift | 66 +++++++++---------- .../ServiceGroupConfiguration.swift | 10 +-- 3 files changed, 68 insertions(+), 42 deletions(-) diff --git a/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md b/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md index 5b3e93b..d7617e9 100644 --- a/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md +++ b/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md @@ -69,15 +69,23 @@ on services with a lower index. The following example shows how this can be applied to our `BarService`. ```swift +import ServiceLifecycle +import Logging + @main struct Application { + static let logger = Logger(label: "Application") + static func main() async throws { let fooService = FooServer() let barService = BarService(fooService: fooService) + + let serviceGroup = ServiceGroup( // We are encoding the dependency hierarchy here by listing the fooService first - services: [fooService, barService] + services: [fooService, barService], + logger: logger ) try await serviceGroup.run() @@ -109,6 +117,9 @@ A common example of this is for applications that implement streaming behaviours. ```swift +import ServiceLifecycle +import Logging + struct StreamingService: Service { struct RequestStream: AsyncSequence { ... } struct ResponseWriter { @@ -134,6 +145,8 @@ struct StreamingService: Service { @main struct Application { + static let logger = Logger(label: "Application") + static func main() async throws { let streamingService = StreamingService(streamHandler: { requestStream, responseWriter in for await request in requestStream { @@ -143,7 +156,8 @@ struct Application { let serviceGroup = ServiceGroup( services: [streamingService], - gracefulShutdownSignals: [.sigterm] + gracefulShutdownSignals: [.sigterm], + logger: logger ) try await serviceGroup.run() @@ -168,6 +182,9 @@ and what we want to do is stop the iteration. To do this we can use the `AsyncSequence`. The updated code looks like this: ```swift +import ServiceLifecycle +import Logging + struct StreamingService: Service { struct RequestStream: AsyncSequence { ... } struct ResponseWriter { @@ -193,6 +210,8 @@ struct StreamingService: Service { @main struct Application { + static let logger = Logger(label: "Application") + static func main() async throws { let streamingService = StreamingService(streamHandler: { requestStream, responseWriter in for await request in requestStream.cancelOnGracefulShutdown() { @@ -202,7 +221,8 @@ struct Application { let serviceGroup = ServiceGroup( services: [streamingService], - gracefulShutdownSignals: [.sigterm] + gracefulShutdownSignals: [.sigterm],, + logger: logger ) try await serviceGroup.run() @@ -239,8 +259,13 @@ sure your telemetry service is gracefully shutdown after your HTTP server unexpectedly threw from its `run()` method. This setup could look like this: ```swift +import ServiceLifecycle +import Logging + @main struct Application { + static let logger = Logger(label: "Application") + static func main() async throws { let telemetryService = TelemetryService() let httpServer = HTTPServer() @@ -254,7 +279,8 @@ struct Application { successTerminationBehavior: .shutdownGracefully, failureTerminationBehavior: .shutdownGracefully ) - ] + ], + logger: logger ), ) diff --git a/Sources/ServiceLifecycle/ServiceGroup.swift b/Sources/ServiceLifecycle/ServiceGroup.swift index a1e9839..853cc6d 100644 --- a/Sources/ServiceLifecycle/ServiceGroup.swift +++ b/Sources/ServiceLifecycle/ServiceGroup.swift @@ -30,7 +30,7 @@ public actor ServiceGroup: Sendable, Service { } /// The logger. - private let logger: Logger? + private let logger: Logger /// The logging configuration. private let loggingConfiguration: ServiceGroupConfiguration.LoggingConfiguration /// The maximum amount of time that graceful shutdown is allowed to take. @@ -55,7 +55,7 @@ public actor ServiceGroup: Sendable, Service { Set(configuration.gracefulShutdownSignals).isDisjoint(with: configuration.cancellationSignals), "Overlapping graceful shutdown and cancellation signals" ) - precondition(configuration.logger?.label != deprecatedLoggerLabel, "Please migrate to the new initializers") + precondition(configuration.logger.label != deprecatedLoggerLabel, "Please migrate to the new initializers") self.state = .initial(services: configuration.services) self.gracefulShutdownSignals = configuration.gracefulShutdownSignals self.cancellationSignals = configuration.cancellationSignals @@ -76,7 +76,7 @@ public actor ServiceGroup: Sendable, Service { services: [any Service], gracefulShutdownSignals: [UnixSignal] = [], cancellationSignals: [UnixSignal] = [], - logger: Logger? = nil + logger: Logger ) { let configuration = ServiceGroupConfiguration( services: services.map { ServiceGroupConfiguration.ServiceConfiguration(service: $0) }, @@ -204,7 +204,7 @@ public actor ServiceGroup: Sendable, Service { services: inout [ServiceGroupConfiguration.ServiceConfiguration], gracefulShutdownStream: AsyncStream ) async throws { - self.logger?.debug( + self.logger.debug( "Starting service lifecycle", metadata: [ self.loggingConfiguration.keys.gracefulShutdownSignalsKey: "\(self.gracefulShutdownSignals)", @@ -270,7 +270,7 @@ public actor ServiceGroup: Sendable, Service { gracefulShutdownManagers.reserveCapacity(services.count) for (index, serviceConfiguration) in services.enumerated() { - self.logger?.debug( + self.logger.debug( "Starting service", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(serviceConfiguration.service)" @@ -324,7 +324,7 @@ public actor ServiceGroup: Sendable, Service { switch service.successTerminationBehavior.behavior { case .cancelGroup: - self.logger?.debug( + self.logger.debug( "Service finished unexpectedly. Cancelling group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -337,7 +337,7 @@ public actor ServiceGroup: Sendable, Service { return .failure(ServiceGroupError.serviceFinishedUnexpectedly()) case .gracefullyShutdownGroup: - self.logger?.debug( + self.logger.debug( "Service finished. Gracefully shutting down group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -357,7 +357,7 @@ public actor ServiceGroup: Sendable, Service { } case .ignore: - self.logger?.debug( + self.logger.debug( "Service finished.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -366,7 +366,7 @@ public actor ServiceGroup: Sendable, Service { services[index] = nil if services.allSatisfy({ $0 == nil }) { - self.logger?.debug( + self.logger.debug( "All services finished." ) self.cancelGroupAndSpawnTimeoutIfNeeded( @@ -380,7 +380,7 @@ public actor ServiceGroup: Sendable, Service { case .serviceThrew(let service, let index, let serviceError): switch service.failureTerminationBehavior.behavior { case .cancelGroup: - self.logger?.debug( + self.logger.debug( "Service threw error. Cancelling group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -394,7 +394,7 @@ public actor ServiceGroup: Sendable, Service { return .failure(serviceError) case .gracefullyShutdownGroup: - self.logger?.debug( + self.logger.debug( "Service threw error. Shutting down group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -416,7 +416,7 @@ public actor ServiceGroup: Sendable, Service { } case .ignore: - self.logger?.debug( + self.logger.debug( "Service threw error.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -426,7 +426,7 @@ public actor ServiceGroup: Sendable, Service { services[index] = nil if services.allSatisfy({ $0 == nil }) { - self.logger?.debug( + self.logger.debug( "All services finished." ) @@ -441,7 +441,7 @@ public actor ServiceGroup: Sendable, Service { case .signalCaught(let unixSignal): if self.gracefulShutdownSignals.contains(unixSignal) { // Let's initiate graceful shutdown. - self.logger?.debug( + self.logger.debug( "Signal caught. Shutting down the group.", metadata: [ self.loggingConfiguration.keys.signalKey: "\(unixSignal)" @@ -460,7 +460,7 @@ public actor ServiceGroup: Sendable, Service { } } else { // Let's cancel the group. - self.logger?.debug( + self.logger.debug( "Signal caught. Cancelling the group.", metadata: [ self.loggingConfiguration.keys.signalKey: "\(unixSignal)" @@ -475,7 +475,7 @@ public actor ServiceGroup: Sendable, Service { case .gracefulShutdownCaught: // We got a manual or inherited graceful shutdown. Let's initiate graceful shutdown. - self.logger?.debug("Graceful shutdown caught. Cascading shutdown to services") + self.logger.debug("Graceful shutdown caught. Cascading shutdown to services") do { try await self.shutdownGracefully( @@ -492,7 +492,7 @@ public actor ServiceGroup: Sendable, Service { case .cancellationCaught: // We caught cancellation in our child task so we have to spawn // our cancellation timeout task if needed - self.logger?.debug("Caught cancellation.") + self.logger.debug("Caught cancellation.") self.cancelGroupAndSpawnTimeoutIfNeeded( group: &group, cancellationTimeoutTask: &cancellationTimeoutTask @@ -517,7 +517,7 @@ public actor ServiceGroup: Sendable, Service { return .success(()) } - self.logger?.debug( + self.logger.debug( "Service lifecycle ended" ) cancellationTimeoutTask?.cancel() @@ -558,12 +558,12 @@ public actor ServiceGroup: Sendable, Service { .enumerated().reversed() { guard let service = services[gracefulShutdownIndex] else { - self.logger?.debug( + self.logger.debug( "Service already finished. Skipping shutdown" ) continue gracefulShutdownLoop } - self.logger?.debug( + self.logger.debug( "Triggering graceful shutdown for service", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -583,7 +583,7 @@ public actor ServiceGroup: Sendable, Service { guard index == gracefulShutdownIndex else { // Another service exited unexpectedly - self.logger?.debug( + self.logger.debug( "Service finished unexpectedly during graceful shutdown. Cancelling all other services now", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -598,7 +598,7 @@ public actor ServiceGroup: Sendable, Service { } // The service that we signalled graceful shutdown did exit/ // We can continue to the next one. - self.logger?.debug( + self.logger.debug( "Service finished", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -610,7 +610,7 @@ public actor ServiceGroup: Sendable, Service { services[index] = nil switch service.failureTerminationBehavior.behavior { case .cancelGroup: - self.logger?.debug( + self.logger.debug( "Service threw error during graceful shutdown. Cancelling group.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -628,7 +628,7 @@ public actor ServiceGroup: Sendable, Service { guard index == gracefulShutdownIndex else { // Another service threw while we were waiting for a shutdown // We have to continue the iterating the task group's result - self.logger?.debug( + self.logger.debug( "Another service than the service that we were shutting down threw. Continuing with the next one.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -639,7 +639,7 @@ public actor ServiceGroup: Sendable, Service { } // The service that we were shutting down right now threw. Since it's failure // behaviour is to shutdown the group we can continue - self.logger?.debug( + self.logger.debug( "The service that we were shutting down threw. Continuing with the next one.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -652,7 +652,7 @@ public actor ServiceGroup: Sendable, Service { guard index == gracefulShutdownIndex else { // Another service threw while we were waiting for a shutdown // We have to continue the iterating the task group's result - self.logger?.debug( + self.logger.debug( "Another service than the service that we were shutting down threw. Continuing with the next one.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -663,7 +663,7 @@ public actor ServiceGroup: Sendable, Service { } // The service that we were shutting down right now threw. Since it's failure // behaviour is to shutdown the group we can continue - self.logger?.debug( + self.logger.debug( "The service that we were shutting down threw. Continuing with the next one.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)", @@ -676,7 +676,7 @@ public actor ServiceGroup: Sendable, Service { case .signalCaught(let signal): if self.cancellationSignals.contains(signal) { // We got signalled cancellation after graceful shutdown - self.logger?.debug( + self.logger.debug( "Signal caught. Cancelling the group.", metadata: [ self.loggingConfiguration.keys.signalKey: "\(signal)" @@ -692,7 +692,7 @@ public actor ServiceGroup: Sendable, Service { case .gracefulShutdownTimedOut: // Gracefully shutting down took longer than the user configured // so we have to escalate it now. - self.logger?.debug( + self.logger.debug( "Graceful shutdown took longer than allowed by the configuration. Cancelling the group now.", metadata: [ self.loggingConfiguration.keys.serviceKey: "\(service.service)" @@ -706,7 +706,7 @@ public actor ServiceGroup: Sendable, Service { case .cancellationCaught: // We caught cancellation in our child task so we have to spawn // our cancellation timeout task if needed - self.logger?.debug("Caught cancellation.") + self.logger.debug("Caught cancellation.") self.cancelGroupAndSpawnTimeoutIfNeeded( group: &group, cancellationTimeoutTask: &cancellationTimeoutTask @@ -741,7 +741,7 @@ public actor ServiceGroup: Sendable, Service { ) { guard cancellationTimeoutTask == nil else { // We already have a cancellation timeout task running. - self.logger?.debug( + self.logger.debug( "Task cancellation timeout task already running." ) return @@ -756,7 +756,7 @@ public actor ServiceGroup: Sendable, Service { // from being cancelled. cancellationTimeoutTask = Task { do { - self.logger?.debug( + self.logger.debug( "Task cancellation timeout task started." ) try await Task.sleep( @@ -765,7 +765,7 @@ public actor ServiceGroup: Sendable, Service { attosecondsComponent: maximumCancellationDuration.attosecondsComponent ) ) - self.logger?.debug( + self.logger.debug( "Cancellation took longer than allowed by the configuration." ) fatalError("Cancellation took longer than allowed by the configuration.") diff --git a/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift b/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift index cc10328..30b21ff 100644 --- a/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift +++ b/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift @@ -107,7 +107,7 @@ public struct ServiceGroupConfiguration: Sendable { public var cancellationSignals = [UnixSignal]() /// The group's logger. - public var logger: Logger? + public var logger: Logger /// The group's logging configuration. public var logging = LoggingConfiguration() @@ -172,7 +172,7 @@ public struct ServiceGroupConfiguration: Sendable { /// - logger: The group's logger. public init( services: [ServiceConfiguration], - logger: Logger? = nil + logger: Logger ) { self.services = services self.logger = logger @@ -189,7 +189,7 @@ public struct ServiceGroupConfiguration: Sendable { services: [ServiceConfiguration], gracefulShutdownSignals: [UnixSignal] = [], cancellationSignals: [UnixSignal] = [], - logger: Logger? = nil + logger: Logger ) { self.services = services self.logger = logger @@ -204,7 +204,7 @@ public struct ServiceGroupConfiguration: Sendable { /// - logger: The group's logger. public init( services: [Service], - logger: Logger? = nil + logger: Logger ) { self.services = Array(services.map { ServiceConfiguration(service: $0) }) self.logger = logger @@ -221,7 +221,7 @@ public struct ServiceGroupConfiguration: Sendable { services: [Service], gracefulShutdownSignals: [UnixSignal] = [], cancellationSignals: [UnixSignal] = [], - logger: Logger? = nil + logger: Logger ) { self.services = Array(services.map { ServiceConfiguration(service: $0) }) self.logger = logger From 363b5bfea2bcd5a4923447c9130b3ec6e23f58d7 Mon Sep 17 00:00:00 2001 From: nervenes Date: Sun, 1 Dec 2024 10:13:08 +0100 Subject: [PATCH 6/6] remove blank lines --- .../Docs.docc/How to adopt ServiceLifecycle in applications.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md b/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md index d7617e9..7f747e1 100644 --- a/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md +++ b/Sources/ServiceLifecycle/Docs.docc/How to adopt ServiceLifecycle in applications.md @@ -80,8 +80,6 @@ struct Application { let fooService = FooServer() let barService = BarService(fooService: fooService) - - let serviceGroup = ServiceGroup( // We are encoding the dependency hierarchy here by listing the fooService first services: [fooService, barService],