apple / apple/swift-log

Make non-emitted logs cheaper

Open
#377 9 comments 1 reaction 0 assignees View on GitHub
Dominant language
Swift
Stars
4k
Forks
344
Avg merge
1d 11h
Merged PRs (30d)
5

Description

Looking at some perf traces today, I stumbled upon the fact that swift-log appears to be failing at its goal of making non-issued logs approximately free. Consider this code:

```swift
public func whyThough(_ logger: Logger) {
logger.trace("Hello, world!")
}
```

In an ideal world, in release builds this code would execute almost no instructions when the log level is higher than trace: it would check the log level, and then return.

In practice what it does is:

- Call `swift_beginAccess`
- Call `outlined init with copy of Logging.LogHandler`, which includes calling through the VWT for the LogHandler to execute its copy constructor
- Call `swift_project_boxed_opaque_existential_1`
- Call `swift_destroy_boxed_opaque_existential_1`

This appears to all boil down to the fact that the `logLevel` is held on the `LogHandler`, not the `Logger`, so we have to touch the existential in order to load the log level.

In some cases this may not be excessive, but if anyone has inserted trace level logs in hot code paths the cost can spiral quite badly. We’re seeing it take up to 10% of the CPU time in some of our high packet rate benchmarks.

Now, we can resolve this issue by just deleting the trace logging, but I wanted to ask: do we think swift-log should be this expensive? Are we interested in making it faster? We could make it faster by moving the log level to the Logger, not the LogHandler, but conceptually that could break existing code if they were playing weird tricks, and it could cause things that used to fit safely into existentials to not fit. How do we feel?

Contributor guide

Open the contributing guide

Research direction

Start by examining how Logger accesses logLevel through LogHandler, then reproduce the whyThough example in a release build and inspect the disabled-trace instructions. Compare possible Logger and LogHandler changes against existential compatibility; done means non-emitted trace logs avoid the reported copying and existential operations without breaking existing handlers.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
observability-sre
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.