QuantiLogger 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+
- RxSwift 4.0+ (NOTE: QuantiLogger uses set of RxSwift libraries and takes advantage of its provided reactive functionality.)
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. You can install Carthage with Homebrew using the following command:
$ brew update
$ brew install carthage
To integrate QuantiLogger into your Xcode project using Carthage, specify it in your Cartfile
:
github "Qase/QuantiLogger" ~> 1.17
Run carthage update
to build the framework and drag the built QuantiLogger.framework
and its dependencies into your Xcode project.
CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:
$ gem install cocoapods
To integrate QuantiLogger into your Xcode project using CocoaPods, specify it in your Podfile:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '11.0'
use_frameworks!
target '<Your Target Name>' do
pod 'QuantiLogger', '~> 1.17'
end
Then, run the following command:
$ pod install
The Swift Package Manager is a tool for managing the distribution of Swift code. The Package Manager is included in Swift 3.0 and above.
In file Package.swift
in the dependencies parameter add the following:
.package(url: "https://github.com/Qase/QuantiLogger", from: "1.17")
so it should look something like this:
let package = Package(
name: "YOUR_APP_NAME",
dependencies: [
.package(url: "https://github.com/Qase/QuantiLogger", from: "1.17")
]
)
then run
$ swift build
and
$ swift package generate-xcodeproj
For updating use
$ swift package update
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 size of such batches and also a time interval between individual batches being sent.
Here is an example of log batch in JSON:
[{"severity": "VERBOSE",
"sessionName": "E598B4C1-2B08-4563-81C0-2A77E5CE0C3C",
"message":"/some/path/QuantiLoggerTests.swift - testWebLogger() - line 165: Test verbose",
"timestamp": 1529668897318.845},
{"severity": "INFO",
"sessionName":"E598B4C1-2B08-4563-81C0-2A77E5CE0C3C",
"message": "/some/path/QuantiLoggerTests.swift - testWebLogger() - line 166: Test system",
"timestamp":1529668897319.6549},
{"severity":"INFO",
"sessionName":"E598B4C1-2B08-4563-81C0-2A77E5CE0C3C",
"message":"/some/path/QuantiLoggerTests.swift - testWebLogger() - line 167: Test process",
"timestamp":1529668897319.6959
}]
Here is the set of properties a user can customize:
serverUrl
target server's urlsessionName
name of a session to log insizeOfBatch
size of a log batchtimeSpan
time interval between individual batches being sent
Target server that receives logs is independent on the WebLogger. Therefore a user can implement a customized server that displays logs in a prefered way. If one does not wish to implement a customized server, we also provide a simple server solution written in Node.js.
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: QuantiLogger.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 QLog(_:onLevel)
macro function. Such function can be used anywhere, where QuantiLogger
is imported.
Example of how to log:
QLog("This is the message to be logged.", onLevel: .info)
QuantiLogger
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 also provides LogFilesViaMailViewController
that has a pre-configured controller that, when presented, will offer the option to send all log files via mail. It is constructed using init(withRecipients:)
where withRecipients
is [String]
containing all mail recipients.
Example can be seen in a form of SendLogFilesViaMailViewController
within QuantiLoggerExample
which is a working example within the framework (clone the project to see).
The framework also provides FileLoggerTableViewDatasource
which is a pre-configured UITableViewDataSource
containing all log files merged, each log = a single UITableViewCell
.
Example can be seen in a form of LogListTableViewController
within QuantiLoggerExample
which is a working example within teh framework (clone the project to see).
QuantiLogger
is released under the MIT License.