diff --git a/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.CanonicalLogLine.swift b/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.CanonicalLogLine.swift index 260e5d3..ce67c1f 100644 --- a/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.CanonicalLogLine.swift +++ b/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.CanonicalLogLine.swift @@ -1,7 +1,7 @@ import Foundation public extension Log.Record.FormatStyle { - /// Formats records as a Stripe-style canonical log line. + /// Formats records as a dense key-value line inspired by Stripe's canonical log lines. /// /// Inspired by Stripe's Canonical Log Lines: /// https://stripe.com/blog/canonical-log-lines @@ -40,7 +40,7 @@ public extension Log.Record.FormatStyle { // MARK: FormatStyle public extension FormatStyle where Self == Log.Record.FormatStyle.CanonicalLogLine { - /// A Stripe-style canonical log line record format. + /// A dense key-value record format inspired by Stripe's canonical log lines. static var canonicalLogLine: Self { Self() } @@ -49,7 +49,7 @@ public extension FormatStyle where Self == Log.Record.FormatStyle.CanonicalLogLi // MARK: Log.Record.Formatter public extension Log.Record.Formatter { - /// A type-erased Stripe-style canonical log line record formatter. + /// A type-erased dense key-value record formatter inspired by Stripe's canonical log lines. static var canonicalLogLine: Self { Self(.canonicalLogLine) } diff --git a/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.JSON.swift b/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.JSON.swift index 9b83b8c..5051e15 100644 --- a/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.JSON.swift +++ b/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.JSON.swift @@ -1,9 +1,9 @@ import Foundation public extension Log.Record.FormatStyle { - /// Formats records as a conventional structured JSON object. + /// Formats records as JSON that can be inspected with JSON tools. /// - /// The JSON output is designed for log export and ingestion, not as Broadcast's + /// The JSON output is designed for export and ingestion, not as Broadcast's /// internal `Codable` storage shape. Broadcast renders stable top-level logging /// fields first, then includes typed payload values under `payload`. /// @@ -43,7 +43,7 @@ public extension Log.Record.FormatStyle { // MARK: FormatStyle public extension FormatStyle where Self == Log.Record.FormatStyle.JSON { - /// A conventional structured JSON record format. + /// A structured JSON record format. static var json: Self { Self() } @@ -52,7 +52,7 @@ public extension FormatStyle where Self == Log.Record.FormatStyle.JSON { // MARK: Log.Record.Formatter public extension Log.Record.Formatter { - /// A type-erased conventional structured JSON record formatter. + /// A type-erased structured JSON record formatter. static var json: Self { Self(.json) } diff --git a/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.TokenOptimized.swift b/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.TokenOptimized.swift index 1df820c..3e54b6a 100644 --- a/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.TokenOptimized.swift +++ b/Sources/Broadcast/Components/FormatStyles/Log.Record.FormatStyle.TokenOptimized.swift @@ -1,11 +1,11 @@ import Foundation public extension Log.Record.FormatStyle { - /// Formats records as a compact token-optimized log line. + /// Formats records as compact lines for AI context windows. /// - /// The token-optimized format is designed for agents, where you have a limited context window, - /// for example in a prompt. This is ideal for large lists of logs because it keeps Broadcast's - /// fixed record envelope short, yet preserving application payload names: + /// Use this when you want to hand an agent a lot of runtime history without + /// spending tokens on repeated labels. Broadcast keeps the fixed record envelope + /// short while preserving your payload names: /// /// `t=42125 l=error s=Diagnostic c="Background Sync" m="Failed sync" p.retry_count=2 p.duration=1250ms` /// @@ -36,7 +36,7 @@ public extension Log.Record.FormatStyle { // MARK: FormatStyle public extension FormatStyle where Self == Log.Record.FormatStyle.TokenOptimized { - /// A compact record format optimized for token-sensitive text contexts. + /// A compact record format designed to optimize AI context windows. static var tokenOptimized: Self { Self() } @@ -45,7 +45,7 @@ public extension FormatStyle where Self == Log.Record.FormatStyle.TokenOptimized // MARK: Log.Record.Formatter public extension Log.Record.Formatter { - /// A type-erased compact record formatter optimized for token-sensitive text contexts. + /// A type-erased compact record formatter designed to optimize AI context windows. static var tokenOptimized: Self { Self(.tokenOptimized) } diff --git a/Sources/Broadcast/Components/Log.Payload.swift b/Sources/Broadcast/Components/Log.Payload.swift index 93478c2..a85311c 100644 --- a/Sources/Broadcast/Components/Log.Payload.swift +++ b/Sources/Broadcast/Components/Log.Payload.swift @@ -1,13 +1,14 @@ import Foundation public extension Log { - /// A typed key-value pair attached to a structured log. + /// Typed context attached to a structured log. /// - /// Payloads are where durable diagnostic context belongs: identifiers, counts, - /// decisions, timings, and errors. Prefer typed app-specific helper factories for - /// repeated keys, such as `Log.Payload.priority(_:)` or - /// `Log.Payload.dueDate(_:)`, so spelling, ordering, and value formatting stay - /// consistent. Avoid storing secrets, tokens, or sensitive user data in payloads. + /// Payloads are where most of a log's useful context lives: identifiers, counts, + /// dates, durations, outcomes, errors, and anything else that helps explain what + /// happened. Prefer app-specific helper factories for repeated keys, such as + /// `Log.Payload.priority(_:)` or `Log.Payload.dueDate(_:)`, so spelling, ordering, + /// and value formatting stay consistent. Avoid storing secrets, tokens, or + /// sensitive user data in payloads. struct Payload: Codable, Sendable, Equatable { /// The stable diagnostic key rendered before the payload value. public let key: String diff --git a/Sources/Broadcast/Components/Log.Record.swift b/Sources/Broadcast/Components/Log.Record.swift index 744fb85..27ff9ec 100644 --- a/Sources/Broadcast/Components/Log.Record.swift +++ b/Sources/Broadcast/Components/Log.Record.swift @@ -1,13 +1,12 @@ import Foundation public extension Log { - /// The semantic representation of a structured log before it is rendered. + /// A structured log before any destination renders it. /// - /// ``Log`` forwards structured calls to each ``LoggingDestination`` as semantic - /// components. Use ``Log/Record`` directly when you need to format a structured log - /// yourself, test exact structured components, or build a custom formatter. Most - /// app call-sites should use structured ``Log`` methods such as - /// ``Log/info(_:_:category:payload:)`` instead. + /// ``Log/Record`` keeps the level, timestamp, signal, category, message, and + /// payload separate so the same event can become readable support text, JSON, + /// canonical log lines, or token-optimized output for agents. Most app call-sites + /// should use structured ``Log`` methods such as ``Log/info(_:_:category:payload:)``. struct Record: Codable, Identifiable, Sendable, Equatable { /// A stable identifier for this record. public let id: UUID diff --git a/Sources/Broadcast/Log/Log+Combined.swift b/Sources/Broadcast/Log/Log+Combined.swift index 22d9135..2410c65 100644 --- a/Sources/Broadcast/Log/Log+Combined.swift +++ b/Sources/Broadcast/Log/Log+Combined.swift @@ -1,11 +1,11 @@ import Foundation public extension Log { - /// Returns a new logger that writes to this logger's destinations plus additional destinations. + /// Returns a new log that writes to this log's destinations plus additional destinations. /// - /// Use this to compose destination sets without mutating the original logger. This - /// is useful for adding console, buffered, remote, test, or feature-specific - /// destinations while preserving the existing logger configuration. + /// Use this when one package, feature, or workflow needs its own destination + /// without losing the app-wide logger. The returned ``Log`` keeps the same + /// `log.info` API, but each record also goes to the extra destinations. func combined(with destinations: [LoggingDestination]) -> Log { var allDestinations = self.destinations allDestinations.append(contentsOf: destinations) @@ -13,7 +13,7 @@ public extension Log { return Log(destinations: allDestinations) } - /// Returns a new logger that writes to this logger's destinations plus one additional destination. + /// Returns a new log that writes to this log's destinations plus one additional destination. func combined(with destination: LoggingDestination) -> Log { self.combined(with: [destination]) } diff --git a/Sources/Broadcast/Log/Log.swift b/Sources/Broadcast/Log/Log.swift index 1d5ad00..12a5d13 100644 --- a/Sources/Broadcast/Log/Log.swift +++ b/Sources/Broadcast/Log/Log.swift @@ -1,17 +1,16 @@ -/// A lightweight logging facade that fans each log call out to one or more destinations. +/// The API your app calls to write logs. /// -/// Create one shared ``Log`` near your app's composition root and inject it through -/// your app, package, or server dependencies. ``Log`` intentionally stays small: -/// destinations decide where output goes and how structured records are formatted, -/// while ``Log`` gives call-sites consistent plain and structured logging APIs. +/// A ``Log`` owns one or more destinations. Every `log.debug`, `log.info`, or +/// `log.error` call is sent to each destination, so your app gets one simple API +/// while destinations decide where records go and how they are formatted. public struct Log { /// The destinations that receive every log call. /// - /// Use multiple destinations when the same event should go to different places, - /// such as the system console and an in-memory support-log buffer. + /// Use multiple destinations when the same event should go to the console, an + /// in-memory support log, a persistent store, or a custom destination you build. public let destinations: [any LoggingDestination] - /// Creates a logger that writes to each destination in order. + /// Creates a log that writes to each destination in order. public init(destinations: [any LoggingDestination]) { self.destinations = destinations } @@ -83,22 +82,23 @@ public struct Log { } public extension Log { - /// Broadcast's shared in-memory logger for the current process. + /// Broadcast's shared in-memory support log for the current process. /// /// Prefer creating and injecting your own ``SessionLogger`` when you need explicit /// lifetime control, deterministic tests, or multiple independently exported buffers. static let sessionLogger = SessionLogger() - /// Broadcast's shared OSLog-backed console destination. + /// Broadcast's shared console destination. /// /// Prefer creating your own ``ConsoleLogger`` with your app's subsystem and category /// for production integrations. static let consoleLogger = ConsoleLogger(subsystem: "com.mergesort.broadcast", category: "logs") - /// A convenience logger that writes to Broadcast's default console and session destinations. + /// A convenience log that writes to Broadcast's default console and session destinations. /// - /// This is useful for quick integration or examples. Apps with dependency injection, - /// support-log export, or privacy-specific routing should construct their own ``Log``. + /// This is useful for quick integration or examples. Apps that need support-log + /// export, privacy-specific routing, or dependency injection should construct + /// their own ``Log``. static let `default` = Log( destinations: [ Log.consoleLogger, diff --git a/Sources/Broadcast/Loggers/BufferedLoggingDestination.swift b/Sources/Broadcast/Loggers/BufferedLoggingDestination.swift index 531ee7a..93f30de 100644 --- a/Sources/Broadcast/Loggers/BufferedLoggingDestination.swift +++ b/Sources/Broadcast/Loggers/BufferedLoggingDestination.swift @@ -1,18 +1,19 @@ -/// A logging destination that can export the text it has captured. +/// A destination that keeps records so they can be exported later. /// -/// This capability can be used for support flows, diagnostics exports, or tests that need to -/// inspect emitted logs. Destinations that only write elsewhere, such as the system -/// console, should conform to ``LoggingDestination`` directly. +/// Use a buffered destination for support screens, bug report attachments, AI +/// debugging exports, and tests that need to inspect what your app logged. +/// Destinations that only write elsewhere, such as the system console, should +/// conform to ``LoggingDestination`` directly. public protocol BufferedLoggingDestination: LoggingDestination { - /// Returns the destination's currently buffered records. + /// Returns the original records currently buffered by this destination. func records() -> [Log.Record] - /// Returns the destination's currently buffered logs as exportable text. + /// Returns the currently buffered records as readable export text. func logs() -> String } public extension BufferedLoggingDestination { - /// Returns the destination's currently buffered records as exportable text. + /// Returns the currently buffered records as readable export text. func logs() -> String { self.records() .map({ self.recordFormatter.format($0) }) diff --git a/Sources/Broadcast/Loggers/LoggingDestination.swift b/Sources/Broadcast/Loggers/LoggingDestination.swift index 12283fb..d3d1022 100644 --- a/Sources/Broadcast/Loggers/LoggingDestination.swift +++ b/Sources/Broadcast/Loggers/LoggingDestination.swift @@ -1,17 +1,16 @@ -/// A write-only logging sink. +/// A place where Broadcast sends log records. /// -/// ``Log/Record`` is Broadcast's canonical log event model. Destination -/// requirements accept records so custom destinations can inspect level, timestamp, -/// signal, category, message, and payload without re-parsing formatted strings. -/// Consumer call-sites should usually use the ergonomic ``Log`` APIs instead of -/// constructing records manually. +/// Custom destinations receive semantic ``Log/Record`` values, so they can inspect +/// level, timestamp, signal, category, message, and payload before formatting, +/// uploading, storing, or exporting them. App call-sites should usually use +/// ``Log`` APIs like `log.info` instead of calling destinations directly. public protocol LoggingDestination { /// Supplies dates for records created by this destination's convenience methods. /// /// Destinations can override this for deterministic tests or custom time sources. var dateProvider: Log.DateProvider { get } - /// The type-erased formatter this destination uses for log records. + /// The formatter this destination uses when it renders records. /// /// Custom destinations can override this with /// `Log.Record.Formatter(.customRecordStyle)` to change how records are rendered diff --git a/Sources/Broadcast/Loggers/MultiSessionLogger.swift b/Sources/Broadcast/Loggers/MultiSessionLogger.swift index 5a6eb8f..9ae2c97 100644 --- a/Sources/Broadcast/Loggers/MultiSessionLogger.swift +++ b/Sources/Broadcast/Loggers/MultiSessionLogger.swift @@ -2,12 +2,11 @@ import Boutique import Foundation import Synchronization -/// A persistent buffered destination for logs that should survive app relaunches. +/// A persistent destination for logs that should survive app relaunches. /// -/// Use ``MultiSessionLogger`` for support diagnostics where the most useful evidence -/// may have happened in a previous launch. The host app owns the Boutique `Store` -/// configuration so it can choose the right container, retention policy, and app -/// group behavior. +/// Use ``MultiSessionLogger`` when the useful evidence may have happened before +/// the current launch. The host app owns the Boutique `Store` configuration, so it +/// can choose the right storage location, app group, and retention policy. public final class MultiSessionLogger: BufferedLoggingDestination { public let dateProvider: Log.DateProvider private let recordStorage: Mutex<[Log.Record]> diff --git a/Sources/Broadcast/Loggers/SessionLogger.swift b/Sources/Broadcast/Loggers/SessionLogger.swift index 5f7981b..a0419be 100644 --- a/Sources/Broadcast/Loggers/SessionLogger.swift +++ b/Sources/Broadcast/Loggers/SessionLogger.swift @@ -1,17 +1,18 @@ import Foundation import Synchronization -/// An in-memory buffered destination for logs from the current process. +/// An in-memory destination for logs from the current launch. /// -/// Use ``SessionLogger`` when you need support-log export or test inspection for the -/// current launch only. It does not persist across app restarts; use -/// ``MultiSessionLogger`` when historical logs should survive relaunches. +/// Use ``SessionLogger`` for support-log export, bug report attachments, AI +/// debugging exports, or tests that need to inspect what your app logged during +/// this process. It does not persist across app restarts; use ``MultiSessionLogger`` +/// when historical logs should survive relaunches. public final class SessionLogger: BufferedLoggingDestination { public let dateProvider: Log.DateProvider private let storage = SessionLogStorage() private let timestampFormatStyle: Log.Timestamp.FormatStyle - /// Creates a session buffer with configurable time dependencies. + /// Creates a session buffer with configurable timestamp behavior. /// /// ``Log/DateProvider/default`` and the default ``Log/Timestamp/FormatStyle`` are /// suitable for production. Inject fixed values in tests when log output must be