Logger is a super lightweight logging library for iOS development in Swift. It provides a few pre-built loggers including native os_log or a file logger. It also provides an interface for creating custom loggers.
- Swift 5.0+
- Xcode 10+
- iOS 11.0+
It is possible to log on different levels. Each logger can support different levels, meaning that it records only log messages on such levels. For example multiple loggers of the same type can be set to support different levels and thus separate logging on such level. Another use case is to further filter messages of specific type declared by its level.
Although it is definitely up to the developer in which way the levels will be used, there is a recommended way how to use them since each of them serves a different purpose.
info
for mostly UI related user actionsdebug
for debugging purposesverbose
for extended debug purposes, especially when there is a need for very specific messageswarn
for warningserror
for errorssystem
(for native os_log only) matches to os_log_fault -> sends a fault-level message to the logging systemprocess
(for native os_log only) matches to os_log_error -> sends an error-level message to the logging system
The simplest logger. Wraps print(_:separator:terminator:)
function from Swift Standard Library.
Wraps the native OSLog
to log messages on the system level.
Enables logging to a file. Each log file relates to a single day data. Another day, another log file is used. numberOfLogFiles
specifies the number of log files that are stored. In other words, how many days back (log files) should be kept. If the last log file is filled, the first one gets overriden using the simplest Round-robin strategy.
Enables logging via REST API to a target server. To reduce the traffic, logs are grouped into so-called batches when sent. A user can set the max size of such batches and also a max time interval between individual batches being sent.
The integrator is responsible for the creation of URLRequest
with the log batches & firing the request.
Target server that receives logs is independent on the WebLogger
. Thus the integrator is responsible for the implementation of a target server. The target server is to receive / parse / display the incoming log batches. If the does not wish to implement a customized server, we also provide a simple server solution written in Node.js.
Here is an example of log batch in JSON:
[
{"severity": "VERBOSE",
"sessionName": "E598B4C1-2B08-4563-81C0-2A77E5CE0C3C",
"message":"/some/path/LoggerTests.swift - testWebLogger() - line 165: Test verbose",
"timestamp": 1529668897318.845},
{"severity": "INFO",
"sessionName":"E598B4C1-2B08-4563-81C0-2A77E5CE0C3C",
"message": "/some/path/LoggerTests.swift - testWebLogger() - line 166: Test system",
"timestamp":1529668897319.6549},
{"severity":"INFO",
"sessionName":"E598B4C1-2B08-4563-81C0-2A77E5CE0C3C",
"message":"/some/path/LoggerTests.swift - testWebLogger() - line 167: Test process",
"timestamp":1529668897319.6959}
]
Here is the set of properties a user can customize:
sessionID
which can be used on a server to filter logs for a specific application instancebatchConfiguration
max batch size & time interval of batches
A special type of logger, that automatically logs all received UIApplication notifications, further called application callbacks. Here is a complete list of supported application callbacks:
UIApplicationWillTerminate
UIApplicationDidBecomeActive
UIApplicationWillResignActive
UIApplicationDidEnterBackground
UIApplicationDidFinishLaunching
UIApplicationWillEnterForeground
UIApplicationSignificantTimeChange
UIApplicationUserDidTakeScreenshot
UIApplicationDidChangeStatusBarFrame
UIApplicationDidReceiveMemoryWarning
UIApplicationWillChangeStatusBarFrame
UIApplicationDidChangeStatusBarOrientation
UIApplicationWillChangeStatusBarOrientation
UIApplicationProtectedDataDidBecomeAvailable
UIApplicationBackgroundRefreshStatusDidChange
UIApplicationProtectedDataWillBecomeUnavailable
The logger is integrated and set automatically, thus it logs all application callbacks on debug
level as is. By using setApplicationCallbackLogger(with: [ApplicationCallbackType]?)
on LogManager
a user can specific application callbacks to be logged (all of them are logged by default). If an empty array is passed, all application callbacks will be logged. If nil is passed, none of application callbacks will be logged. By using setApplicationCallbackLogger(onLevel: Level)
a user can set a specific level on which to log application callbacks (debug
is used by default). By using setApplicationCallbackLogger(with: [ApplicationCallbackType]?, onLevel: Level)
a user can set both, application callbacks and a level at the same time.
A special type of logger, that enables to log various meta information about the application and the host application. Here is a complete list of supported meta information:
identifier
application unique identifiercompiler
used compiler versionversion
CFBundleShortVersionString a.k.a. version of the applicationbuildNumber
CFBundleVersion a.k.a. build number of the applicationmodelType
model of the application's host devicecurrentOSVersion
current OS version of the application's host deviceupTime
boot time of the applicationlanguage
localization of the application's host device
The logger is accessible outside of the framework using logMetaInformation(_: [MetaInformationType])
method on LogManager
. It allows to explicitly list meta information that should be logged. If not specified explicitely, all the meta information will be logged.
There is a possibility of creating custom loggers just by implementing Logging
protocol. In the simplest form, the custom logger only needs to implement log(_:onLevel:)
and levels()
methods. Optionaly it can also implement configure()
method in case there is some configuration necessary before starting logging.
Here is an example of a custom logger that enables logging to Crashlytics:
import Fabric
import Crashlytics
class CrashLyticsLogger: Logger.Logging {
open func configure() {
Fabric.with([Crashlytics.self])
}
open func log(_ message: String, onLevel level: Level) {
CLSLogv("%@", getVaList(["[\(level.rawValue) \(Date().toFullDateTimeString())] \(message)"]))
}
open func levels() -> [Level] {
return [.verbose, .info, .debug, .warn, .error]
}
}
Logging initialization is done through LogManager
Singleton instance. The LogManager holds an array of all configured loggers and then manages the logging.
Here is an example of how to initialize the logger to use FileLogger
, ConsoleLogger
and previously created custom CrashLyticsLogger
:
let logManager = LogManager.shared
let fileLogger = FileLogger()
fileLogger.levels = [.warn, .error]
logManager.add(fileLogger)
let systemLogger = ConsoleLogger()
systemLogger.levels = [.verbose, .info, .debug, .warn, .error]
logManager.add(systemLogger)
let crashlyticsLogger = CrashLyticsLogger()
logManager.add(crashlyticsLogger)
Logging is done using a simple Log(_:onLevel)
macro function. Such function can be used anywhere, where Logger
is imported.
Example of how to log:
Log("This is the message to be logged.", onLevel: .info)
Logger
supports 3 different concurrency modes at the moment. The mode is set using loggingConcurrencyMode
property of LogManager
. Default value is asyncSerial
.
Logging task is dispatched synchronously on a custom serial queue, where all loggers perform their tasks serially one by one.
Logging task is dispatched asynchronously on a custom serial queue, where all loggers perform their tasks serially one by one.
Logging task is dispatched synchronously on a custom serial queue, where all loggers perform their tasks concurrently.
The framework provides SendMailView
that has a pre-configured SwiftUI View
that, when presented, will offer the option to send all log files via mail.
The framework also provides FileLoggerTableViewDatasource
which is a pre-configured UITableViewDataSource
containing all log files merged, each log = a single UITableViewCell
.
Logger
is released under the MIT License.