CleanroomLogger
CleanroomLogger provides an extensible Swift-based logging API that is simple, lightweight and performant
Install / Use
/learn @emaloney/CleanroomLoggerREADME

CleanroomLogger
CleanroomLogger provides an extensible Swift-based logging API that is simple, lightweight and performant.
The API provided by CleanroomLogger is designed to be readily understood by anyone familiar with packages such as CocoaLumberjack and log4j.
CleanroomLogger is part of the Cleanroom Project from Gilt Tech.
Swift compatibility
This is the master branch. It uses Swift 4.1 and requires Xcode 9.3 to compile.
Current status
Branch|Build status
--------|------------------------
master|
Contents
- Key Benefits of CleanroomLogger
- Adding CleanroomLogger to your project
- Using CleanroomLogger
- CleanroomLogger In Depth
- Design Philosophy
- Architectural Overview
Key Benefits of CleanroomLogger
▶︎ Built for speed
You don’t have to choose between smooth scrolling and collecting meaningful log information. CleanroomLogger does very little work on the calling thread, so it can get back to business ASAP.
▶ A modern logging engine with first-class legacy support
CleanroomLogger takes advantage of Apple’s new Unified Logging System (aka “OSLog” or “os_log”) when running on iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0 or higher.
On systems where OSLog isn’t available, CleanroomLogger gracefully falls back to other standard output mechanisms, automatically.
▶ 100% documented
Good documentation is critical to the usefulness of any open-source framework. In addition to the extensive high-level documentation you’ll find below, the CleanroomLogger API itself is 100% documented.
▶ Organize and filter messages by severity
Messages are assigned one of five severity levels: the most severe is error, followed by warning, info, debug and verbose, the least severe. Knowing a message’s severity lets you perform additional filtering; for example, to minimize the overhead of logging in App Store binaries, you could choose to log only warnings and errors in release builds.
▶ Color-coded log messages
Quickly spot problems at runtime in the Xcode console, where log messages are color coded by severity:
◽️ Verbose messages are tagged with a small gray square — easy to ignore
◾️ Debug messages have a black square; easier to spot, but still de-emphasized
🔷 Info messages add a splash of color in the form of a blue diamond
🔶 Warnings are highlighted with a fire-orange diamond
❌ Error messages stand out with a big red X — hard to miss!
▶ UNIX-friendly
Support for standard UNIX output streams is built-in. Use StandardOutputLogRecorder and StandardErrorLogRecorder to direct output to stdout and stderr, respectively.
Or, use the StandardStreamsLogRecorder to send verbose, debug and info messages to stdout while warnings and errors go to stderr.
▶ Automatic handling of OS_ACTIVITY_MODE
When Xcode 8 was introduced, the console pane got a lot more chatty. This was due to the replacement of the ASL facility with OSLog. To silence the extra chatter, developers discovered that setting the OS_ACTIVITY_MODE environment variable to “disable” would revert to the old logging behavior. It turns out that this silences OSLog altogether, so no output is sent to the console pane. CleanroomLogger notices when the setting is present, and echoes messages to stdout or stderr in addition to logging them through the os_log() function.
▶ See where your code is logging
If you’re just using print() or NSLog() everywhere, it can sometimes be difficult to figure out what code is responsible for which log messages. By default, CleanroomLogger outputs the source file and line responsible for issuing each log message, so you can go straight to the source:
🔶 AppleTart.framework didn’t load due to running on iOS 8 (AppleTartShim.swift:19)
◾️ Uploaded tapstream batch (TapstreamTracker.swift:166)
◽️ Presenting AccountNavigationController from SaleListingController (BackstopDeepLinkNavigator.swift:174)
🔷 Successfully navigated to .account for URL: gilt://account (DeepLinkConsoleOutput.swift:104)
❌ Unrecognized URL: CountrySelector (GiltOnTheGoDeepLinkRouter.swift:100)
▶ Rotating log files
CleanroomLogger provides simple file-based logging support as well as a self-pruning rotating log directory implementation.
▶ Super-simple execution tracing
Developers often use logging to perform tracing. Rather than writing lots of different log messages to figure out what your program is doing at runtime, just sprinkle your source with Log.debug?.trace() and Log.verbose?.trace() calls, and you’ll see exactly what lines your code hits, when, and on what thread, as well as the signature of the executing function:
2017-01-05 13:46:16.681 -05:00 | 0001AEC4 ◾️ —> StoreDataTransaction.swift:42 - executeTransaction()
2017-01-05 13:46:16.683 -05:00 | 00071095 ◾️ —> LegacyStoresDeepLinking.swift:210 - viewControllerForRouter(_:destination:)
2017-01-05 13:46:16.683 -05:00 | 0001AEC4 ◽️ —> StoreDataTransaction.swift:97 - executeTransaction(completion:)
2017-01-05 13:46:16.684 -05:00 | 00071095 ◾️ —> ContainerViewController.swift:132 - setContentViewController(_:animated:completion:)
2017-01-05 13:46:16.684 -05:00 | 00071095 ◾️ —> DefaultBackstopDeepLinkNavigator.swift:53 - navigate(to:via:using:viewController:displayOptions:completion:)
2017-01-05 13:46:16.687 -05:00 | 00071095 ◽️ —> ViewControllerBase.swift:79 - viewWillAppear
▶ Useful built-in formatters
CleanroomLogger ships with two general-purpose log formatters: the ReadableLogFormatter is handy for human consumption, while the ParsableLogFormatter is useful for machine processing. Both can be customized via the initializer.
A formatter constructed using ReadableLogFormatter() yields log output that looks like:
2017-01-06 02:06:53.679 -05:00 | Debug | 001BEF88 | DeepLinkRouterImpl.swift:132 - displayOptions(for:via:displaying:)
2017-01-06 02:06:53.682 -05:00 | Verbose | 001BEF88 | UIWindowViewControllerExtension.swift:133 - rootTabBarController: nil
2017-01-06 02:06:53.683 -05:00 | Info | 001BEF88 | DeepLinkConsoleOutput.swift:104 - Successfully navigated to storeSale for URL: gilt://sale/women/winter-skin-rescue
2017-01-06 02:07:01.761 -05:00 | Error | 001BEF88 | Checkout.swift:302 - The user transaction failed
2017-01-06 02:07:02.397 -05:00 | Warning | 001BEF88 | MemoryCache.swift:233 - Caching is temporarily disabled due to a recent memory warning
When the same log messages are handled by a formatter constructed using ParsableLogFormatter(), the timestamp is output in UNIX format, tab is used as the field delimiter, and the severity is indicated numerically:
1483686413.67946 2 001BEF88 DeepLinkRouterImpl.swift:132 - displayOptions(for:via:displaying:)
1483686413.68170 1 001BEF88 UIWindowViewControllerExtension.swift:133 - rootTabBarController: nil
1483686413.68342 3 001BEF88 DeepLinkConsoleOutput.swift:104 - Successfully navigated to storeSale for URL: gilt://sale/women/winter-skin-rescue
1483686421.76101 5 001BEF88 Checkout.swift:302 - The user transaction failed
1483686422.39651 4 001BEF88 MemoryCache.swift:233 - Caching is temporarily disabled due to a recent memory warning
▶ Easy mix-and-match formatting
If the built-in formatters don’t fit the bill, you can use the FieldBasedLogFormatter to assemble just about any kind of log format possible.
Let’s say you wanted a log formatter with the timestamp in ISO 8601 date format, a tab character, the source file and line number of the call site, followed by the severity as an uppercase stri
