apple/swift-log

Different log levels for different groups of loggers

JetForMe opened this issue · 4 comments

One of the powerful features of Java's log4j is that different logging levels can be set for large swaths of the code. This is accomplished with a couple of things:

  • Generally, each class has its own logger, named after the fully-qualified class name (Java has a hierarchical naming convention of pacakges and classes in them, e.g. myapp.app.model.entities.Employee
  • Log level can be specified at any level, e.g. myapp.app.model should log at TRACE, everything else at ERROR.
  • If a logger doesn't have a specific log level set, it uses its parent's level

These levels can be set at run time, making it easy to turn up or down the logging on parts of the code you need to focus on, without obscuring the output with too many messages from uninteresting code.

I don't see a way for swift-log to do that.

As I'm working to implement this functionality in my own LogHandler, I realize that the LogHandler's log method isn't provided the calling Logger’s data (like its label), making implementing this functionality inelegant at best. I now have to pass the label as metadata when creating the logger. I’m going to open a separate issue to add this.

One of the powerful features of Java's log4j is that different logging levels can be set for large swaths of the code. This is accomplished with a couple of things:

...

I don't see a way for swift-log to do that.

These are good points which I totally agree with. In my project I tried to start repeating it like:

At a main entry point

var logLevel = Logger.Level.debug // a common and default log level
// ... code setting a log level based on a passed program parameter
        
LoggingSystem.bootstrap{ label in
            var logHandler = StreamLogHandler.standardOutput(label: "com.github.vitalz.demo" + label)
            logHandler.logLevel = logLevel
            // TODO: MultiplexLogHandler([logHandler, fileLogHandler])
            return logHandler
}

in my classes

public final class Controller {
   static var logger: Logger { return Logger(label: "Controller") }
   let log = Controller.logger
   
   public func run() {
       log.debug("Controller running...")
   }    
}

I've planned to add a YAML-config parser which extracts a logger config and to update LoggingSystem.bootstrap that it will set a log level for a passed label comparing it to configured labels on YAML.

I got some of the behavior I wanted with this class. If you create loggers with dotted names (e.g. com.mycompany.myapp.section1.logger1), you can set thresholds with calls like this:

LogHandlerCoordinator.instance.set(rootThreshold: .debug)
LogHandlerCoordinator.instance.set(threshold: .warn, for: "com.mycompany.myapp.section1")

@JetForMe as you discovered already this would be a function of the log-backend and not the log-api (this library). for example, https://github.com/apple/swift-log/blob/main/Tests/LoggingTests/TestLogger.swift#L57 is a test log-backend that does a simplified version of this approach. of course, we would need a good log-backend that does this well. the log-backend that is included in swift-log is for development only and should not be used for real production applications