diff --git a/Sources/SKLogging/LoggingScope.swift b/Sources/SKLogging/LoggingScope.swift index 8e8b9de4b..25467ce54 100644 --- a/Sources/SKLogging/LoggingScope.swift +++ b/Sources/SKLogging/LoggingScope.swift @@ -16,7 +16,11 @@ import Foundation public final class LoggingScope { /// The name of the default logging subsystem if no task-local value is set. - fileprivate static let defaultSubsystem: ThreadSafeBox = .init(initialValue: nil) + private static let defaultSubsystem = ThreadSafeBox(initialValue: nil) + + /// Whether we have logged a fault that `subsystem` has been accessed without calling + /// `configureDefaultLoggingSubsystem` first. + private static let hasLoggedNoSubsystemConfiguredFault = AtomicBool(initialValue: false) /// The name of the current logging subsystem or `nil` if no logging scope is set. @TaskLocal fileprivate static var _subsystem: String? @@ -31,9 +35,16 @@ public final class LoggingScope { } else if let defaultSubsystem = defaultSubsystem.value { return defaultSubsystem } else { - Logger(subsystem: "org.swift.sklogging", category: "configuration") - .log(level: .fault, "default logging subsystem was not configured before first use") - return "org.swift" + if !hasLoggedNoSubsystemConfiguredFault.setAndGet(newValue: true) { + Logger(subsystem: "default", category: "sklogging") + .fault( + """ + Default logging subsystem was not configured before first use of SKLogging. \ + Ensure that configureDefaultLoggingSubsystem is called before the first log call. + """ + ) + } + return "default" } } @@ -42,8 +53,16 @@ public final class LoggingScope { return _scope ?? "default" } + /// Set the logging subsystem that is used task local subsystem is set using `withLoggingSubsystemAndScope`. + /// + /// Must be called before the first log call. public static func configureDefaultLoggingSubsystem(_ subsystem: String) { - LoggingScope.defaultSubsystem.withLock { $0 = subsystem } + LoggingScope.defaultSubsystem.withLock { defaultSubsystem in + if let defaultSubsystem, defaultSubsystem != subsystem { + logger.log("Changing default log subsystem from \(defaultSubsystem) to \(subsystem)") + } + defaultSubsystem = subsystem + } } } diff --git a/Sources/ToolsProtocolsCAtomics/include/ToolsProtocolsCAtomics.h b/Sources/ToolsProtocolsCAtomics/include/ToolsProtocolsCAtomics.h index 7e20a67c9..2629f0379 100644 --- a/Sources/ToolsProtocolsCAtomics/include/ToolsProtocolsCAtomics.h +++ b/Sources/ToolsProtocolsCAtomics/include/ToolsProtocolsCAtomics.h @@ -17,6 +17,7 @@ #include #include #include +#include typedef struct { _Atomic(uint32_t) value; @@ -32,6 +33,10 @@ static inline uint32_t atomic_uint32_get(CAtomicUInt32 *_Nonnull atomic) { return atomic->value; } +static inline uint32_t atomic_uint32_get_and_set(CAtomicUInt32 *_Nonnull atomic, uint32_t newValue) { + return __c11_atomic_exchange(&atomic->value, newValue, __ATOMIC_SEQ_CST); +} + static inline void atomic_uint32_set(CAtomicUInt32 *_Nonnull atomic, uint32_t newValue) { atomic->value = newValue; } diff --git a/Sources/ToolsProtocolsSwiftExtensions/Atomics.swift b/Sources/ToolsProtocolsSwiftExtensions/Atomics.swift index 4b8f9c275..302e6e7a5 100644 --- a/Sources/ToolsProtocolsSwiftExtensions/Atomics.swift +++ b/Sources/ToolsProtocolsSwiftExtensions/Atomics.swift @@ -32,6 +32,11 @@ import ToolsProtocolsCAtomics atomic_uint32_set(atomic, newValue ? 1 : 0) } } + + /// Sets the boolean to the new value and returns the previous value. + @_spi(SourceKitLSP) public func setAndGet(newValue: Bool) -> Bool { + return atomic_uint32_get_and_set(atomic, newValue ? 1 : 0) != 0 + } } @_spi(SourceKitLSP) public final class AtomicUInt8: Sendable {