CocoaLumberjack: an Objective-C logging framework for Apple platforms
A fast & simple, yet powerful & flexible logging framework for macOS, iOS, tvOS, watchOS and visionOS
At a glance
- What is it?
- CocoaLumberjack is a BSD-3-Clause logging framework for macOS, iOS, tvOS, watchOS and visionOS, installed through Swift Package Manager, CocoaPods, Carthage or manually. It gives you a DDLog facade plus pluggable loggers, and it still ships an Objective-C core.
- Who is it for?
- Adopt CocoaLumberjack if you ship an Objective-C or mixed codebase on Apple platforms and want a DDLog facade with DDFileLogger rotation and a swift-log backend. Do not adopt it if you want a pure Swift logging API with no Objective-C surface, or if you only need os_log calls, since DDOSLogger already forwards to os_log.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 10 days ago.
- What is it written in?
- Mainly Objective-C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What CocoaLumberjack replaces on Apple platforms
Apple's own logging facilities are split by era. NSLog is simple but slow and noisy, and os_log arrived later with a different API. A codebase that spans macOS, iOS, tvOS, watchOS and visionOS ends up with a mix of both, plus a homegrown wrapper that writes to a file when someone needs a support log. CocoaLumberjack's pitch is that you call one macro family, DDLogVerbose through DDLogError, and decide separately where those messages go.
The README describes it as a logging framework for macOS, iOS, tvOS, watchOS and visionOS, and the repository's topics list carthage, cocoapods, ios, log, logger, logging, lumberjack, macos, objective-c, swift, swift-package-manager, tvos, visionos and watchos. That topic list is the honest summary of the audience: teams already building for Apple's platforms, in Objective-C or Swift or both. The project is not a cross-platform logging library, and nothing in the README suggests it targets Android, server-side Swift on Linux, or the browser.
The primary language is Objective-C. That matters more than it sounds. The Swift API is a layer over an Objective-C core, which is why the README tells CocoaPods users to write import CocoaLumberjack rather than import CocoaLumberjackSwift, and why an existing Objective-C project can adopt it without a rewrite.
DDLog, loggers and the data flow
The architecture is a facade plus sinks. DDLog is the entry point. You attach one or more loggers to it with DDLog.add, and every DDLogInfo, DDLogWarn or DDLogError call is delivered to each attached logger. The README's Swift example attaches DDOSLogger.sharedInstance and a DDFileLogger to the same DDLog instance, so the same message reaches os_log and a rotating file at once.
Loggers are the extension point. DDOSLogger forwards to os_log and is what the README recommends for iOS 10 and later. DDTTYLogger and DDASLLogger are named for earlier OS versions. DDFileLogger writes to disk and owns rotation policy through rollingFrequency and logFileManager.maximumNumberOfLogFiles, which the README sets to 24 hours and 7 files. Those two properties are the whole retention story in the example: one day per file, seven files kept.
There is a second integration path. The project ships CocoaLumberjackSwiftLogBackend, a backend for apple/swift-log. You keep configuring loggers and formatters through DDLog, but you emit messages with swift-log's Logger after calling LoggingSystem.bootstrapWithCocoaLumberjack(). The README notes that custom formatters can read the swiftLogInfo property on DDLogMessage to recover the details of a message that arrived through swift-log. That property is the bridge between the two APIs, and it is the detail that makes the backend usable rather than a one-way adapter.
Installing CocoaLumberjack with Swift Package Manager or CocoaPods
The README lists four integration methods: Swift Package Manager, CocoaPods, Carthage, and manual installation. Swift Package Manager support exists as of CocoaLumberjack 3.6.0. Add the dependency to Package.swift, or add the package through Xcode.
.package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack", from: "3.9.0"),The README attaches a warning to this step: you may need to add both products, CocoaLumberjack and CocoaLumberjackSwift, to your target, because SPM sometimes fails to detect that CocoaLumberjackSwift depends on CocoaLumberjack. If your build cannot find the Swift symbols after adding the package, that sentence is the first thing to check.
For CocoaPods, the Swift subspec pulls in the Objective-C code plus the Swift layer, so one line is enough. The README's example sets a platform floor of iOS 11.0.
platform :ios, '11.0'
target 'SampleTarget' do
use_frameworks!
pod 'CocoaLumberjack/Swift'
endIf you are staying in Objective-C, the README shows the plain pod instead: pod 'CocoaLumberjack'. Carthage users add github "CocoaLumberjack/CocoaLumberjack" to a Cartfile. The manual route is documented separately in Documentation/GettingStarted.md under manual installation, which the README links rather than reproduces.
A first logging setup, and the Objective-C build error to expect
The smallest useful setup is one console logger plus one file logger, then a few calls. This is the README's own Swift example, and it is the shape most apps end up with: os_log for live debugging, a rotating file for logs you can pull off a device later.
DDLog.add(DDOSLogger.sharedInstance) // Uses os_log
let fileLogger: DDFileLogger = DDFileLogger() // File Logger
fileLogger.rollingFrequency = 60 * 60 * 24 // 24 hours
fileLogger.logFileManager.maximumNumberOfLogFiles = 7
DDLog.add(fileLogger)
DDLogInfo("Info")
DDLogWarn("Warn")
DDLogError("Error")After this runs you should see the same messages in the Xcode console through os_log and in the file logger's directory, capped at seven files. The README also lists DDLogVerbose and DDLogDebug alongside these, so the full set is Verbose, Debug, Info, Warn, Error.
There is a known integration snag for Objective-C projects. The README documents a build error reading Multiple methods named 'tag' found with mismatched result, parameter type or attributes. The fix is to define DD_LEGACY_MESSAGE_TAG as 0 before importing CocoaLumberjack, or to add -DDD_LEGACY_MESSAGE_TAG=0 to Other C Flags in the Xcode project. That is a macro-level switch, not a runtime option, so it belongs in build settings rather than in application code.
Where CocoaLumberjack is the wrong tool
If your app already targets iOS 10 or later and you only ever want console output, DDOSLogger is a thin wrapper over os_log, and you are adding a dependency to reach an API you already have. The framework earns its place when you need a second destination, most often a file, or when you want to swap destinations at runtime without touching call sites.
The file logger's configuration is coarse by design. In the README example, retention is a rolling frequency in seconds and a maximum file count. There is no documented size-based rotation trigger, no documented compression, and no documented upload or shipping path. If your requirement is "rotate at 5 MB and upload to a collector", the README does not describe that, and you should treat it as work you own.
Objective-C is the primary language, and the Swift API is a layer on top. A project that has banned Objective-C interop, or that wants a logging API that reads as idiomatic Swift throughout, will find the boundary visible in places like the CocoaPods import line. That is a real cost, not a cosmetic one.
The README's migration section for 3.x is also unhelpful: under Migrating to 3.x it says only To be determined. If you are coming from 2.x, the README will not walk you through it.
CocoaLumberjack against SwiftyBeaver, and against swift-log alone
SwiftyBeaver is the alternative that people search for alongside this project, and the difference is mostly language and scope. SwiftyBeaver is a Swift-native logging library. CocoaLumberjack's core is Objective-C with a Swift layer, and its logging macros are usable from both languages, which is why it survives in codebases that predate Swift. If your project is pure Swift and greenfield, SwiftyBeaver removes the interop boundary; if you have Objective-C files that also need to log, CocoaLumberjack is the one that does not force a second logging path.
Against apple/swift-log alone, the comparison is different, because CocoaLumberjack ships a swift-log backend rather than competing with it. The README's arrangement lets you keep DDLog for configuration and formatters while emitting through swift-log's Logger. The trade-off is indirection: messages pass through the backend, and formatters that need the original swift-log metadata read it from swiftLogInfo on DDLogMessage. If you want one API and nothing else, using swift-log without this backend is simpler. If you want file rotation from a swift-log-based app, the backend is the documented route.
Licence, releases and the cost of staying current
CocoaLumberjack is BSD-3-Clause. That is a permissive licence, and the repository carries a LICENSE file at the top level. This is not legal advice, and the practical implication depends on how you redistribute the framework, but a permissive licence of this kind is generally the reason a framework like this can be embedded in closed-source applications.
The release history is steady rather than dormant. The last push was on 2026-09-21, and the most recent release is 3.10.0 on the same date, following 3.9.1 on 2026-04-16 and 3.9.0 on 2025-07-22. The gap between 3.9.0 and 3.9.1 is roughly nine months, and 3.9.1 to 3.10.0 is about five. That is a maintenance cadence where you should expect to move versions occasionally, not continuously.
The upgrade cost is concentrated in two places. First, the README's 3.x migration notes are still marked To be determined, so a major-version jump has no published checklist. Second, the package manifests are split across Package.swift, [email protected], [email protected] and [email protected], which means Swift toolchain compatibility is handled per version. If you pin a toolchain, check which manifest applies to it before assuming a version bump is trivial.
Editorial conclusion
Adopt CocoaLumberjack if you ship an Objective-C or mixed codebase on Apple platforms and want a DDLog facade with DDFileLogger rotation and a swift-log backend. Do not adopt it if you want a pure Swift logging API with no Objective-C surface, or if you only need os_log calls, since DDOSLogger already forwards to os_log. Before you commit, verify the DD_LEGACY_MESSAGE_TAG=0 workaround against your own build and confirm which CocoaLumberjack products your target must declare in Package.swift, because the README warns that Swift Package Manager sometimes fails to detect that CocoaLumberjackSwift depends on CocoaLumberjack.
Frequently asked questions
What is CocoaLumberjack?
It is a logging framework for macOS, iOS, tvOS, watchOS and visionOS, written primarily in Objective-C with a Swift layer. You send messages through DDLog and attach loggers such as DDOSLogger and DDFileLogger to decide where they go.
What is logger used for?
In CocoaLumberjack a logger is a destination attached to DDLog, and a single message is delivered to every attached logger. The README's example attaches DDOSLogger for os_log output and a DDFileLogger for a rotating file at the same time.
What is code logging?
It is emitting messages from application code at severity levels, which CocoaLumberjack exposes as DDLogVerbose, DDLogDebug, DDLogInfo, DDLogWarn and DDLogError. Those calls go to the loggers you registered on DDLog.
What is an example of logging?
The README's Swift snippet is one: attach DDOSLogger.sharedInstance and a DDFileLogger to DDLog, then call DDLogInfo("Info"), DDLogWarn("Warn") and DDLogError("Error"). The same messages reach os_log and the rotating file.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/cocoalumberjack-cocoalumberjack)