Logging levels for Swift's server-side logging APIs and new os_log APIs

Thanks for raising those important points. Server-side logging APIs and os_log will remain as two different logging libraries. This post is aimed at using same names and similar semantics for log levels that will be common between the two APIs, to reduce confusion when porting code from one system to the other.

It wouldn't be desirable for an often-used level such as debug to have one meaning/behavior on the server-side API and a very different one in the os_log API. We would like to converge on some high-level recommendations for the log levels to reduce confusion and prevent such divergences.

Just to add further clarity, the following are some important differences between these two logging libraries:

  • There won't any dependency from server-side logging library to os_log or vice versa. os_log will not be implementing any protocol of server-side logging library such as LogHandler. They are two different logging libraries sharing some common names.
  • There will not be any common binary interface between the two logging APIs. In fact, the log calls such as info, debug, log etc. will have very different types in the two APIs.
  • The plan described here for improving os_log applies to the new os_log APIs. Only the names of the log calls will change.
  • The new os_log APIs will expose all functionality offered by os_log using Swift 5 extended string interpolation syntax as described in that thread. E.g. it will expose privacy qualifiers, custom formatting etc. The os log calls will be optimized using compile-time interpretation.

What this unification of log levels will enable

An existing os log call of the form os_log(.info, customLogger, "message: %lf", timeInterval) will now look like customLogger.info("message \(timeInterval)").

The type of customLogger has to be established at compile-time. This will enforce at compile-time whether info is an os_log call or a call to the server-side logging API.

If customLogger is defined as an os_log logger (by importing suitable libraries), when the program is compiled and executed, the string interpolation passed to the log call will be deconstructed into a format string and argument buffer and will be passed to the os logging system (see here for details on how this can be accomplished).

On the other hand, if customLogger is an instance of the type Logger of the server-side logging proposal, the string interpolation will get serialized to strings and the message will be logged through the backend used in the initialization of Logger.

What this unification of log levels will not enable

Log calls using features that are supported by only one of the two libraries will not compile with the other.

E.g. if a user uses customLogger.info("message \(timeInterval, is: .private)") indicating that the timeInterval is a private value, compiling the code on the server-side (by making customLogger an instance of Logger) will trigger a type error. (The syntax for specifying privacy in the os_log APIs is not fully finalized yet.)

Similarly, a log call of the form customLogger.info("message \(timeInterval)", metadata: myMetadata) will compile with the server-side logging library but not with os_log.

However, it is to be noted that sever-side logging APIs may evolve in the future to support formatting options for logged data, and to support privacy qualifiers. When such features are added, it presents an opportunity to unify those functionalities between the two logging libraries.

5 Likes