2

I am using CocoaLumberjack for a Swift project. I would like to implement custom log levels/flags, as I would like to use 6 rather than the default 5, and would prefer different names.

The documentation for doing this is not helpful. It is only a solution for Objective-C.

The fact that DDLogFlag is defined as NS_OPTIONS means I actually could simply ignore the pre-defined values here, create my own constants, and just write some wrapping code to convert from one to the other.

However, DDLogLevel is defined as NS_ENUM, which means Swift won't be very happy with me trying to instantiate something to say 0b111111, which isn't an existing value in the enum. If it were an NS_OPTIONS, like DDLogFlag, I could just ignore the pre-existing definitions from the library and use whatever valid UInt values I wanted to.

As far as I can tell, I just have to write some Objective-C code to define my own replacements for DDLogLevel, DDLogFlag, and write a custom function to pass this in to and access these properties on DDLogMessage. But this feels bad.

How can I use my own custom logging levels in Swift with CocoaLumberjack?

nhgrif
  • 61,578
  • 25
  • 134
  • 173

1 Answers1

1

This is indeed only possible in Objective-C right now - and there also only for the #define Log Macros. Even then I could imagine that the "modern" ObjC compiler will warn about the types that are passed to DDLogMessage.

The docs are indeed a bit outdated here and stem from a time where Objective-C was closer to C that it is to Swift nowadays... :-)

Nevertheless, in the end DDLogLevel and DDLogFlag are both stored as NSUInteger. Which means it can theoretically take any NSUInteger value (aka UInt in Swift).

To define your own levels, you would simply create an enum MyLogLevel: UInt { /*...*/ } and then write your own logging functions. Those functions can actually forward to the existing functions:

extension DDLogFlag {
   public static let fatal = DDLogFlag(rawValue: 0x0001)
   public static let failure = DDLogFlag(rawValue: 0x0010)
}

public enum MyLogLevel: UInt {
    case fatal = 0x0001
    case failure = 0x0011
}

extension MyLogLevel {
    public static var defaultLevel: MyLogLevel = .fatal
}

@inlinable
public func LogFatal(_ message: @autoclosure () -> Any,
                       level: MyLogLevel = .defaultLevel,
                       context: Int = 0,
                       file: StaticString = #file,
                       function: StaticString = #function,
                       line: UInt = #line,
                       tag: Any? = nil,
                       asynchronous async: Bool = asyncLoggingEnabled,
                       ddlog: DDLog = .sharedInstance) {
    _DDLogMessage(message(), level: unsafeBitCast(level, to: DDLogLevel.self), flag: .fatal, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
}

The unsafeBitCast here works, because in the end it's just an UInt and _DDLogMessage does not switch over the level, but instead does a bit mask check against the flag.


Disclaimer: I'm a CocoaLumberjack maintainer myself.


We don't recommend using a custom log level in Swift. There's not much benefit from it and logging frameworks like swift-log also use predefined log levels.

However, I personally could also imagine declaring DDLogLevel with NS_OPTIONS instead of NS_ENUM. The OSLog Swift overlay also uses an extensible OSLogType. If this is something you'd like to see, please open a PR so we can discuss it with the team. We need to be a bit careful with API compatibility, but like I said it's totally doable.

On a side-note: May I ask what you need custom levels for?

  • I will open a PR for `NS_ENUM` -> `NS_OPTIONS` for `DDLogLevel` and use that PR as a jumping off point for discussing my needs. Mechanically, this is the smallest possible change needed to allow me to more easily have my own custom log levels, but I do not know whether or not this is the best overall change for CocoaLumberjack that could accommodate my needs. GitHub PRs are a bit better location for back-and-forth discussion. – nhgrif Aug 27 '21 at 12:24