Skip to content

Add SwiftLog LogHandler conformance to DiagnosticsLogger (#150) - #212

Open
elio-Wang wants to merge 5 commits into
AvdLee:mainfrom
elio-Wang:feature/swift-log-handler
Open

Add SwiftLog LogHandler conformance to DiagnosticsLogger (#150)#212
elio-Wang wants to merge 5 commits into
AvdLee:mainfrom
elio-Wang:feature/swift-log-handler

Conversation

@elio-Wang

Copy link
Copy Markdown
Contributor

What

Adds DiagnosticsLogger.SwiftLogHandler, a LogHandler conformance for apple/swift-log, so projects already using SwiftLog can bridge their logs into the Diagnostics report with a single bootstrap call:

try DiagnosticsLogger.setup()

LoggingSystem.bootstrap { label in

    DiagnosticsLogger.SwiftLogHandler(label: label)

} 

Resolves #150.

Changes

  • Package.swif — add apple/swift-log ≥ 1.6.0 dependency
  • DiagnosticsLogger.swift — make init() and standard public; add setup(_:) accepting a custom logger instance
  • SwiftLogHandler.swift (new) — LogHandler conformance; maps trace/debug/info/notice/warning → debug, error/critical → error
  • SwiftLogHandlerTests.swift (new) — tests for level mapping, metadata merging, and custom logger instances

Notes

  • @unchecked Sendable matches swift-log's own StreamLogHandler pattern — metadata / logLevel are set at bootstrap, not mutated across threads.
  • Diagnostics only has debug levels, so the mapping groups the lower SwiftLog levels into debug . Can be revisited if more levels are added later.

Allow DiagnosticsLogger to be used as a backing store for SwiftLog's
LoggingSystem by providing a DiagnosticsLogger.SwiftLogHandler type.
Projects already using SwiftLog can now seamlessly forward all log
messages into the Diagnostics report.

- Add swift-log as a package dependency
- Make DiagnosticsLogger.init() and .standard public
- Add setup(_:) overload accepting a custom logger instance
- Add DiagnosticsLogger.SwiftLogHandler nested LogHandler type
- Map SwiftLog levels to Diagnostics debug/error CSS classes
- Add unit tests for the SwiftLog integration

Resolves AvdLee#150
@elio-Wang
elio-Wang requested a review from AvdLee as a code owner July 27, 2026 02:55
@edorphy

edorphy commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

The latest version of SwiftLog added task local logger and some other improvements, not to mention all the chances in between. 1.6.0 is quite old. Any reason to not push the dependency to a more recent version?

Comment thread Sources/Logging/SwiftLogHandler.swift Outdated
/// | debug | debug |
/// | info | debug |
/// | notice | debug |
/// | warning | debug |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning might be more suitable as error. Though my next request if the library will be more diagnostic logger levels.

My rule of thumb, use warning for errors that are known, mitigated, and recoverable. They're still something to have observability into.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — I've updated the mapping so warning now routes to .error. The default is strict; developers can override via logLevel if they need a more lenient routing.

Regarding more granular diagnostic logger levels, I've put together a proposal in the comment below. Let me know if the direction works and I'll implement it.

Comment thread Sources/Logging/SwiftLogHandler.swift Outdated
public init(
label: String,
logger: DiagnosticsLogger = .standard,
logLevel: Logger.Level = .debug,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default should be the more strict level. If there were intermediate options I would have it be info. Debug logs typically don't belong in production.

Allow a developer to override but default on hardening the output.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Changes:

Default logLevel bumped from .debug to .info — debug/trace won't show in production by default
Added a levelMapping closure parameter so developers can override the default mapping if needed
The default mapping keeps trace/debug → Diagnostics debug, and everything else → error.

Comment thread Sources/Logging/SwiftLogHandler.swift Outdated
logger.log(
LogItem(.debug(message: fullMessage), file: file, function: function, line: line)
)
case .error, .critical:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AvdLee

Thoughts on enhancing the diagnostic logger api with more levels to facilitate more granularity?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to receive that, yeah! The better we can support it, the better!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to support all levels of SwiftLog, OpenTelemetry, or a subset of both? It would be expanding the usage of the diagnostic logger quite a bit.

My preference would be to match SwiftLog, or a subset of it since that is what support is being added for.

https://github.com/apple/swift-log/blob/main/Sources/Logging/Logger.swift#L1187

Or

https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord

https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber

https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go with matching SwiftLog's levels. It's the most natural fit since that's what we're bridging, and SwiftLog is already widely adopted in the Swift ecosystem. OpenTelemetry's 24-level SeverityNumber feels like overkill for a diagnostics log file.

For the mapping, I'd suggest:

SwiftLog Level Diagnostics Level
trace trace
debug debug
info info
notice info
warning warning
error error
critical error

This gives us 5 levels in DiagnosticsLogger (trace, debug, info, warning, error) — enough granularity for production diagnostics without being excessive. notice collapsing into info and critical into error keeps it pragmatic.

Happy to implement this if the direction looks right.
@edorphy @AvdLee


public init() { }

private static let logFileLocation: URL = FileManager.default.applicationSupportDirectory.appendingPathComponent("diagnostics_log.txt")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was looking at contributing to this issue, I wanted to also control the destination of the log file.

Maybe out of scope of this merge, but a tradeoff to giving a static default logger without being able to control the destination.

I would find it useful to inject the destination.

#195

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@edorphy Regarding #195 and the ability to inject a custom log file destination — I looked at the code and logFileLocation is currently a hardcoded static let. My approach would be:

Convert it to an instance property, injectable via init(logFileLocation:)
Fall back to the default path when not provided, keeping backward compatibility
This is a different scope from the SwiftLog bridge in this PR, so I can open a separate PR for it. Would you prefer to have it bundled here or kept as a standalone PR?

@elio-Wang

Copy link
Copy Markdown
Contributor Author

The latest version of SwiftLog added task local logger and some other improvements, not to mention all the chances in between. 1.6.0 is quite old. Any reason to not push the dependency to a more recent version?

No specific reason — I conservatively picked 1.6.0 as the minimum since that's the version I had locally when developing the handler. Happy to bump it.

Would you prefer pinning to the latest (≥ 1.14.0), or something in between like ≥ 1.6.0 with a note? Let me know and I'll push the update.

Address review feedback: 1.6.0 was outdated. No API changes needed —
the SwiftLogHandler implementation only uses stable LogHandler APIs
available since 1.6.0, so this is a minimum version bump only.
Per maintainer feedback, warning represents errors that are known,
mitigated, and recoverable — they warrant error-level observability
rather than being grouped with debug output. The default mapping now
favors strictness; developers can override via the logLevel parameter
if they prefer a more lenient routing.
- Default logLevel changed from .debug to .info so debug/trace logs
  are excluded from production output by default
- Add LevelMapping closure parameter to SwiftLogHandler.init,
  allowing developers to override how SwiftLog levels map to
  Diagnostics output (debug vs error)
swift-log 1.15.0 makes StreamLogHandler initializers public and
includes minor fixes. Keep the dependency current with the latest
release.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Conform DiagnosticLogger to SwiftLog LogHandler

3 participants