Log is a lightweight, flexible Swift logging framework that provides elegant and customizable logging capabilities for iOS, macOS, tvOS, watchOS, and visionOS applications. With support for multiple output destinations and custom formatting, Log makes debugging and monitoring your apps easier than ever.
✨ Multiple Output Destinations - Console, system log (os_log), and custom printers
🎨 Customizable Formatting - Prefix, timestamp, and custom formatters
📊 Log Levels - Fine-grained control over message severity
🔧 Extensible - Easy to create custom printers and formatters
📱 Cross-Platform - Works on iOS, macOS, tvOS, watchOS, and visionOS
⚡ Lightweight - Minimal footprint with zero dependencies
🎯 Type-Safe - Leverages Swift's type system for compile-time safety
| Platform | Minimum Version |
|---|---|
| iOS | 13.0+ |
| macOS | 10.15+ |
| tvOS | 13.0+ |
| watchOS | 7.0+ |
| visionOS | 1.0+ |
| Xcode | 15.3+ |
| Swift | 5.10+ |
Add the following dependency to your Package.swift:
dependencies:[.package(url:"https://github.com/space-code/log.git", from:"1.3.0")]Or add it through Xcode:
- File > Add Package Dependencies
- Enter package URL:
https://github.com/space-code/log.git - Select version requirements
import Log
// Create printers
letconsolePrinter=ConsolePrinter()letosPrinter=OSPrinter()
// Create logger
letlog=Logger(
printers:[consolePrinter, osPrinter],
logLevel:.all
)
// Start logging
log.info(message:"Application started")
log.debug(message:"Debug information")
log.error(message:"Something went wrong")Log provides two main printer implementations out of the box:
import Log
// Console printer for Xcode console output
letconsolePrinter=ConsolePrinter()
// OS printer for system console (Console.app)
letosPrinter=OSPrinter()
// Create logger with multiple printers
letlog=Logger(
printers:[consolePrinter, osPrinter],
logLevel:.all
)Log supports various severity levels for organizing your messages:
import Log
letlog=Logger(printers:[ConsolePrinter()], logLevel:.all)
// Different log levels
log.debug(message:"Debug information")
log.info(message:"General informational messages")
log.error(message:"Error messages")
log.fault(message:"Critical fault messages")Available Log Levels:
.debug- Debug information.info- General informational messages.error- Error messages.fault- Critical system faults.all- Log all messages
Filtering by Level:
// Only log errors
letlog=Logger(
printers:[ConsolePrinter()],
logLevel:.errors
)
log.debug(message:"This won't be printed")
log.error(message:"This will be printed")Printers determine where your log messages are output:
ConsolePrinter - Outputs to Xcode console:
letconsolePrinter=ConsolePrinter(
formatters:[PrefixLogFormatter(name:"MyApp"),TimestampLogFormatter(dateFormat:"HH:mm:ss")])OSPrinter - Outputs to system log (viewable in Console.app):
letosPrinter=OSPrinter(
formatters:[PrefixLogFormatter(name:"MyApp")])Using Multiple Printers:
// Log to both Xcode console and system log
letlog=Logger(
printers:[ConsolePrinter(formatters:[TimestampLogFormatter()]),OSPrinter(formatters:[PrefixLogFormatter(name:"MyApp")])],
logLevel:.all
)Formatters transform your log messages before they're printed. Log includes two built-in formatters:
| Formatter | Description |
|---|---|
| PrefixLogFormatter | Adds a specified prefix to messages |
| TimestampLogFormatter | Adds a timestamp based on date format |
Using Formatters:
import Log
// Prefix formatter
letprefixFormatter=PrefixLogFormatter(name:"NetworkLayer")letprinter=ConsolePrinter(formatters:[prefixFormatter])letlog=Logger(printers:[printer], logLevel:.all)
log.info(message:"Request started")
// Output: ℹ️ [NetworkLayer] => Request started
// Timestamp formatter
lettimestampFormatter=TimestampLogFormatter(dateFormat:"yyyy-MM-dd HH:mm:ss")letprinter2=ConsolePrinter(formatters:[timestampFormatter])letlog2=Logger(printers:[printer2], logLevel:.all)
log2.info(message:"User logged in")
// Output: ℹ️ 2025-12-22 15:56:42 User logged in
// Combining formatters
letcombinedPrinter=ConsolePrinter(formatters:[TimestampLogFormatter(dateFormat:"HH:mm:ss"),PrefixLogFormatter(name:"API")])letlog3=Logger(printers:[combinedPrinter], logLevel:.all)
log3.error(message:"Connection failed")
// Output: ⚠️ [API] => 15:56:42 Connection failedCreate custom formatters by conforming to the ILogFormatter protocol:
import Log
structEnvironmentFormatter:ILogFormatter{letenvironment:Stringfunc format(message:String, with logLevel:LogLevel)->String{return"[\(environment.uppercased())] \(message)"}}
// Usage
letformatter=EnvironmentFormatter(environment:"production")letprinter=ConsolePrinter(formatters:[formatter])letlog=Logger(printers:[printer], logLevel:.all)
log.info(message:"Server started")
// Output: [PRODUCTION] Server startedAdvanced Custom Formatter:
structContextualFormatter:ILogFormatter{letincludeThread:BoolletincludeFile:Boolfunc format(message:String, with logLevel:LogLevel)->String{varcomponents:[String]=[]if includeThread {
components.append("[Thread: \(Thread.current.threadName)]")}if includeFile {
components.append("[File: \(#file)]")}
components.append(message)return components.joined(separator:"")}}Custom Printer:
import Log
finalclassFilePrinter:IPrinter{letformatters:[ILogFormatter]letfileURL:URLinit(formatters:[ILogFormatter]=[], fileURL:URL){self.formatters = formatters
self.fileURL = fileURL
}func log(_ message:String, logLevel:Log.LogLevel){varformattedMessage= message
forformatterin formatters {
formattedMessage = formatter.format(
message: formattedMessage,
with: logLevel
)}iflet data =(formattedMessage +"\n").data(using:.utf8){
// Write to file
}}}import Log
classAppDelegate:UIResponder,UIApplicationDelegate{privateletlog=Logger(
printers:[ConsolePrinter(formatters:[TimestampLogFormatter(dateFormat:"HH:mm:ss.SSS"),PrefixLogFormatter(name:"MyApp")]),OSPrinter(formatters:[PrefixLogFormatter(name:"MyApp")])],
logLevel:.all
)func application(
_ application:UIApplication,
didFinishLaunchingWithOptions launchOptions:[UIApplication.LaunchOptionsKey:Any]?)->Bool{
log.info(message:"Application launched")setupApplication()returntrue}privatefunc setupApplication(){
log.debug(message:"Configuring application")
// Configuration code
log.info(message:"Application configured successfully")}}import Log
classNetworkManager{privateletlog=Logger(
printers:[ConsolePrinter(formatters:[TimestampLogFormatter(),PrefixLogFormatter(name:"Network")])],
logLevel:.debug
)func fetchData(from url:URL)asyncthrows->Data{
log.debug(message:"Requesting: \(url.absoluteString)")do{let(data, response)=tryawaitURLSession.shared.data(from: url)iflet httpResponse = response as?HTTPURLResponse{
log.info(message:"Response: \(httpResponse.statusCode)")}return data
}catch{
log.error(message:"Request failed: \(error.localizedDescription)")throw error
}}}import Log
classDatabaseManager{privateletlog=Logger(
printers:[ConsolePrinter(formatters:[PrefixLogFormatter(name:"Database")])],
logLevel:.all
)func saveRecord(_ record:Record)throws{
log.debug(message:"Attempting to save record: \(record.id)")do{try database.insert(record)
log.info(message:"Record saved successfully: \(record.id)")}catch{
log.error(message:"Failed to save record: \(error)")throw error
}}func deleteRecord(id:String)throws{
log.info(message:"Deleting record: \(id)")try database.delete(id)
log.info(message:"Record deleted: \(id)")}}import Log
classLoggerFactory{staticfunc createLogger()->Logger{#if DEBUGreturnLogger(
printers:[ConsolePrinter(formatters:[TimestampLogFormatter(dateFormat:"HH:mm:ss.SSS"),PrefixLogFormatter(name:"Debug")])],
logLevel:.all
)#elsereturnLogger(
printers:[OSPrinter(formatters:[PrefixLogFormatter(name:"Production")])],
logLevel:.error // Only log errors in production
)#endif}}
// Usage
letlog=LoggerFactory.createLogger()- 🐛 Found a bug?Open an issue
- 💡 Have a feature request?Open an issue
- ❓ Questions?Start a discussion
- 🤝 Want to contribute? Submit a pull request
We love contributions! Please feel free to help out with this project. If you see something that could be made better or want a new feature, open up an issue or send a Pull Request.
Bootstrap the development environment:
make bootstrapFor detailed contribution guidelines, see CONTRIBUTING.md.
Nikita Vasilev
- Email: nv3212@gmail.com
- GitHub: @ns-vasilev
Log is released under the MIT license. See LICENSE for details.
Made with ❤️ by space-code
