Benno Kress
Benno Kress

Reputation: 2809

How can I change the source location of an OSLog Message?

I'm working on a company project that requires me to log to a specific server, but I want to log to the console simultaneously. I use a custom logging class that I can feed a log message, a log level, a category and the source location where the log message was generated. Depending on where the app runs, it will log to the console (development), a development backend (test) or the production backend (production).

With Xcode 15 I'm finding the console output of OSLog beautiful and want to use it instead of the print statement. Now I want to also utilize the functionality of OSLog as much as I can and one thing is bothering me: right clicking a log entry in the console and using "Jump to source" always shows the line inside the class where I determine if we log to OSLog or to one of the backends.

Reading through Apple's documentation and looking at the available methods I can't find a way to override the default source location used by OSLog/Xcode to determine the source of a log message. Is there a way to modify it?

Upvotes: 6

Views: 437

Answers (2)

Kasra Babaei
Kasra Babaei

Reputation: 307

You may be able to do this by inlining your wrapper around os.Logger. However, @inlinable doesn't work in Debug builds and @inline(__always) when the SWIFT_OPTIMIZATION_LEVEL is set to -O doesn't force the compiler to inline the implementation.

A workaround is using @_transparent (note that it's an underscored attribute; so, use it with care). Also, you'll need to put your wrapper in another module; otherwise, the call site will show up as ../<compiler-generated> since the implementation has become part of the binary interface of the module.

A very simple implementation will look like this:

import os

@frozen
public enum App {
  public static let log: Logger = {
      Logger()
  }()

  struct Logger: Sendable {
    @_transparent
    public func debug(
      _ message: String,
      fileID: StaticString = #fileID,
      filePath: StaticString = #file,
      function: StaticString = #function,
      line: UInt = #line
    ) {
      let formattedMessage = "Do some stuff... \(message)"
      let logger = os.Logger(subsystem: "can use fileID", category: "placeholder")
      logger.debug("\(function):#\(line): \(formattedMessage)")
    }
  }
}

If the implementation of the wrapper changes, the caller needs to be recompiled. The enum is annotated with @frozen; so, it's promising no changes to its callers.

Upvotes: -1

Claus J&#248;rgensen
Claus J&#248;rgensen

Reputation: 26351

You can't. Apple implemented OSLog on a compiler/toolchain level, so the option to pass in file and line info is unavailable.

This unfortunately means you'll have to use OSLog directly, not wrapped, in order to get the Xcode integration working.

Upvotes: 2

Related Questions