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

taking a step back from the concrete dilemma, interested to understand from @ravikandhadai the original intent in going after syslog levels. to rehash, the original proposal used more java/python like levels and we switched to syslog levels per feedback. my question is, was the intent to adopt the level language (ie names) or also behavior (ie numeric values)

3 Likes

I have summarized the reasons for proposing syslog levels. This is based on the discussions we had before along with @Ben_Cohen and @moiseev .

  • It seemed it is in the good interest of the language that we have a somewhat consistent logging API for device and servers. On the devices, os_log is an existing Apple-specific, widely-used logging system. os_log uses different logging levels: debug, info, default (which is like notice), error and fault. The original server-side logging APIs used different levels: trace, debug, ... error. (While they shared many levels, they did not even agree on the lowermost and the upper most levels.) Furthermore, os_log has very concrete meaning for each logging levels and a specification of what log messages can be purged under resource pressure and so on. At that point, the server side logging APIs left the interpretation of the levels completely to the backend. We thought it would be better if some "guidelines/recommendations" can be provided on what logging at a level can and cannot do. This would simplify mapping the log levels in the interface to the log levels of the os_log and potentially to any other backend in the future that may not have the same hierarchy of levels as that exposed by the logging APIs. We wanted to outline some semantics for levels like what are debug messages? Should they be stored/made available in release software or only meant for development? Can logging levels have side-effects like terminating the program etc. Can logging at a some levels be expensive in terms of performance (this is the case of the fault level of os_log as it captures stack shot and other information that will help in debugging a critical error)? Therefore, this post starts off with some guidelines for these, which were based on syslog as well as os_log. (A note: syslog RFC itself does not provide a detailed definition either, but provides a small phrase for each level.) Since the logging levels proposed in the server-side logging API and os_log were so close to an existing syslog RFC (and also because of point 2 mentioned below), we thought it would be better to adopt the levels of syslog along with their semantics as it is an already existing standard.

  • Sever-side logging API at that point had raw numerical representation. @Ben_Cohen suggested that since the raw numbers are something that cannot be changed once the APIs are fixed, it better to align it with the existing syslog standard that already defines numbers for the levels. Now that @johannesweiss proposes to remove the number for the server-side logging API, this may not be a consideration any more. Btw, we were never intending to support raw numbers in the new os_log APIs. So it is not that syslog numbers are essential for unifying with os_log. The concern was that since raw numbers for logging levels were already standardized by an RFC, should we invent new numbers or would it be better to align with the standard?

4 Likes

thanks you for rehashing all this, it’s very helpful

adopting a consistent approach across os_log and server-side is a very good idea imo, as is leaning on an rfc or some other open standard

that said, i am torn if the rfc above is the right choice for us. on one hand, the levels it includes mostly fit the bill, but on the other, there are issues with it. first, we are not fully implementing the rfc, we are only adopting the level names and their semantic description, iow the rfc covers much bigger ground than what we need and we are adopting a fairly small part of it, which may not fit our context. as you point out, os_log does not plan to use the numeric priorities and the server-side finds them counter intuitive. finally, some of the feedback we received on this thread and previous ones pushed back against the suggested levels, since they are different from other ecosystems

imo, each of the items on its own may not be a good enough reason to reject syslog levels, but when putting them together there seems to be enough friction to at least consider alternative options

1 Like

Is there a preferred place for discussion on this? The [Feedback] thread linked originally is locked.

It seems the project is less trying to have logging between syslog and os_log behave and look the same, but rather have a pluggable logging framework.

If that is the case, I would have a lot more feedback than just logging levels.

This thread is the preferred one if it's regarding the log levels. Otherwise, you can use the Server -> Development forum or a GitHub issue/PR to swift-log.

I'm not sure where you got this impression from but that's really not what swift-log is trying to achieve. Swift-log is a logging API package, it's a pluggable API that allows users to plug in any compatible logging backend solution they would like.

However, the API contains the names of the log levels and therefore we need to settle this before tagging the first swift-log version. Because os_log is the primary way of logging on iOS, we believe that swift-log and os_log should have an API that is as aligned as possible. Therefore, we're trying to unify the logging level between os_log and swift-log. This thread started by proposing to use the syslog logging levels for both swift-log as well as os_log. Neither os_log, nor swift-log is aiming to look/feel like syslog.

3 Likes

imo, each of the items on its own may not be a good enough reason to reject syslog levels, but when putting them together there seems to be enough friction to at least consider alternative options

I think it would be helpful to list down the suggestions in this thread and understand whether they favor outright rejection of syslog levels proposed above or some tweaks to it. There also seems to be a good number of opinions (and likes) in favor of the proposed levels. I have tried to summarize below the concerns raised in this thread so far to better understand where we are:

Numerical representation: There seems to be an agreement on not exposing a numerical representation for levels (in the Logger APIs) and only support comparisons. There also seems to be no strong opinion against having an explicit method like isMoreServe for comparison. (Also, if I understand correctly, this will not prevent backends i.e, LogHandlers from using such representation for levels if they need to be configured externally e.g. from command line.)

Log levels: It looks like there is agreement on having the levels: debug, info, notice, warning, error and critical with the proposed semantics.

Adding a trace level that is less severe than debug. This should be fine as mentioned earlier by @Ben_Cohen

Concerns about Alert and Emergency levels There seems to be a couple of suggestions here (mainly from @dhoepfl):

(a) that these levels are redundant and Critical may suffice.
(b) renaming Alert to Fatal and Emergency to Panic.

These levels are not important for unification with os_log. If there is no foreseeable need for these levels, it should be okay to omit them.

2 Likes

the way i read the solutions proposed in this thread for numerical representation is to be workarounds to the fact syslog priorities are counter intuitive as logging levels, or iow an attempt to force/make syslog priorities work out of collaborative spirit, not technical reasons. afaik, the preferred technical solution for logging api in that aspect is to keep numeric values that are naturally
ordered, allowing both serialization and numeric comparison

1 Like

That's true for date objects, too (“before” is < vs. “less long from now” is < vs. “day of year” is <, …), still it is one of the examples for Comparable. It would feel natural for me that it compares severity (not numerical level), like I think that DEFCON 1 > DEFCON 5.

RFC 5424 clearly states that the codes are “not normative”. syslog combines facility and severity into one value, using 0 as code of highest level for both results in the easy to spot PRI 0 instead of PRI 7 as “most important message”.

I wouldn’t expect the equal here …

.error.isMoreSevere(than: .error) is clearly

  • true
  • false

It should be even more: Comparable requires < (and == via Equatable) but provides all <, <=, !=, ==, >, and >=. Why should the API provide >/isMoreSevere(than:) but nothing else? What's right: 42 > value vs. value < 42?

1 Like

Sorry if I missed this elsewhere, but what are the advantages of sharing a common numerical representation? Does this need to stored, or can this just be a requirement of the protocol that one can produce a syslog-compatible number in a computed property?

If we're more interested in a common set of log level names than the numerical representation, then can making a syslog-compatible numerical value just be done in a protocol extension?

Are we more interested in a common notion of comparison of levels in order of severity?

2 Likes

Having a numerical representation for log levels is not driven by the need to unify with os_log but was a part of the original server-side logging API. There is no specific requirement that it must be compatible with syslog numbers. In fact, some people are finding it counterintuitive. I don't know if there are any advantage in generating syslog numbers, but I do see a difference in opinion here as to whether 0 should mean highest severity (as in syslog) or should it mean lowest severity. (In fact, I even found that XCGLogger adopts exactly syslog levels but inverts the numerical values.)

Does this need to stored, or can this just be a requirement of the protocol that one can produce a syslog-compatible number in a computed property? If we're more interested in a common set of log level names than the numerical representation, then can making a syslog-compatible numerical value just be done in a protocol extension? Are we more interested in a common notion of comparison of levels in order of severity?

My reading is that server-side logging APIs must allow comparison of levels. I don't see a strong opinion one way or other that raw numbers for levels must be frozen in the APIs. Letting them be defined in the extensions as needed seems reasonable to me. Coming back to comparison of levels, it does seem to me that there is some convergence of opinions that the comparison in the order of severity is natural i.e., .trace < .debug < .info < ... < .critical.

A note about the log levels:
As an aside, I would like to note that we don't have much freedom in choosing a different set of log levels (as this suggestion came up before). We need 7 canonical levels to expose all levels of os_log and the original server-side logging proposal: trace, debug, info, notice, warning, error and critical. The level trace is desired by many people. Levels debug and info are universal. info and notice distinction does exist in os_log and if we don't expose it, it would be a problem for adopting the APIs on the device side. warning and error distinction was a part of the original server-side logging proposal and also seems useful. error is a universal level, and critical is needed as it is essential to have a level that does expensive logging for very serious errors.

So is it fair to say we can expunge any numerical representation of the log level from the design so long as we have a way to compare levels?

6 Likes

My +1 for this.

To put down something concrete, we can say Logger.Level is Comparable in ascending severity, but we remove the Int raw value. We add a convenience extension for people who want to serialize a level in a syslog-compatible way:

extension Logger.Level {
  var syslogLevel: Int {
    switch self {
    case debug: return 7
    ...
    }
  }
}

and we proceed to bikeshed the name syslogLevel? :slight_smile:

3 Likes

@Michael_Ilseman and @ravikandhadai if i read the last few comments from you correctly, i think we are converging towards:

  1. agreeing that the shared aspect between os-log and server-logging-api is the log level names. current suggestion includes: trace , debug , info , notice , warning , error and critical

  2. the server-api can choose whatever numeric representation it sees fit to satisfy its serialization and comparison needs, likely using an extension. os-log does not care about it

imo, this brings us very close to a resolution

I mostly agree. I would just add that in case numerical representation for levels becomes a part of the core API (i.e, as a rawValue of log levels) in the server-side logging API, a stretch goal is to support it on the device side for source compatibility. But, there are more stake holders for that to be possible, and there is a possibility that it may just be a feature supported on the server-side and we may not have portability of log calls that use numerical representation for levels.

1 Like

(replying to both in one). Awesome, are we all happy with:

  1. Log level names:

    trace , debug , info , notice , warning , error, and critical

  2. (at least for the server-side) we order them

     trace < debug < info < notice < warning < error < critical
  1. Let's get started without exposing any numerical codes at all. That fits better for the client-side and if the server-side finds a pressing need we can just add them in a minor version with a property.

If that doesn't work for anybody, please shout quickly, I plan to implement that very soon in swift-log.

12 Likes

Thanks a lot for chiming in @ravikandhadai and @Michael_Ilseman!

I'm very happy with all of the above; The names (glad that trace made it after all, yay!), severity order, as well as not exposing any numbers in the core api itself all sound great :+1:

4 Likes

done: change log levels to be more natural by weissi · Pull Request #35 · apple/swift-log · GitHub

5 Likes