NOTE - No longer actively developed. The repository should be treated as a demo framework and not an actively maintained logging framework. It was originally created to try out some ideas with logging in Swift and creating a Swift framework. There are many other powerful logging frameworks available for Swift and so this repository won't be actively developed any further. As such, no support will be provided for this repository.
A powerful and extensible logging framework for Swift that is small and easy to use!
Features • Installation • Usage
You can be up and running with 1 line and then logging right away. Log statements are just as simple as using print
.
The framework contains a small set of powerful features, so you don't need to include a large amount of code you won't use into your project to use NanoLog.
The framework makes use of protocols for each layer in the logging process. This allows you to customise behaviour to your needs if you wish to. It comes bundled with the default console logging implementation.
Log messages at different severity levels, allowing to you see this in the console, but also deal with the levels differently if you want to. For example, only errors could be reported to your server in the production app. See LogSeverity.
Each severity level has a coloured emoji icon, allowing you to attach colour to the log messages in the Xcode console. These icons can be changed to your liking if you don't want to use the default ones. See LogSeverity.
The file, function (with arguments) and line number of the log call are included in the log output. This gives you context of where the logging is coming from. See PrettyLogFormat.
The log format is completely customisable, beyond the default implementation included. You can build up your preferred format from a set of different components, such as date/time, file/method/line, tag, message. See PrettyLogFormat.
The public API is fully documented, including code documentation and ability to generate Jazzy docs.
- Swift 4+
- Apple platforms:
- iOS 8.0+
- MacOS 10.10+
- tvOS 9.0+
- watchOS 2.0+
Carthage
You can use Carthage to integrate NanoLog into your Xcode project.
To do so, simply specify the dependency in your Cartfile
:
github "andrewlord1990/nanolog-swift" == 0.1
CocoaPods
You can use CocoaPods to integrate NanoLog into your Xcode project.
To do so, simply specify the dependency in your Podfile
:
pod 'NanoLog', '0.1'
Swift Package Manager
You can use Swift Package Manager to integrate NanoLog into your project.
To do so, simply specify the dependency in your Package.swift
:
.package(url: "https://github.com/andrewlord1990/nanolog-swift.git", .exact("0.1"))
If you prefer not to use any of the dependency managers, you can integrate NanoLog manually.
Embedded Framework
- Download the latest release from Releases
- Store the downloaded framework somewhere accessible
- Open your app project file in the Project Navigator
- Select your app target under the "Targets" heading in the sidebar
- Open the "General" tab
- Click on the
+
button under the "Embedded Binaries" section - Add the downloaded
NanoLog.framework
If you had already added another version of NanoLog, make sure to replace it with the new version.
Git Submodules
- Open up Terminal, or your preferred console application
cd
to the top-level of your project directory- If your project is not already a Git repository, run:
$ git init
- Add NanoLog as a Git submodule by running:
$ git submodule add https://github.com/andrewlord1990/nanolog-swift.git
$ git submodule update --init --recursive
-
Open the
NanoLog
folder, and drag theNanoLog.xcodeproj
into the Project Navigator of your Xcode project. -
Select the
NanoLog.xcodeproj
in the Project Navigator and verify the deployment target matches that of your application target. -
Next, open your app project file in the Project Navigator
-
Select your app target under the "Targets" heading in the sidebar
-
Open the "General" tab
-
Click on the
+
button under the "Embedded Binaries" section -
You will see two different
NanoLog.xcodeproj
folders each with two different versions of theNanoLog.framework
nested inside aProducts
folder.It does not matter which
Products
folder you choose from. -
Select the
NanoLog.framework
.
The
NanoLog.framework
is automatically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device.
NanoLog can be used primarily through a static API, however, if you would prefer, a non-static API is also available.
To setup logging, you will need to register a LoggingLane
. The easiest way to do this is using NanoLoggingLane
.
Preferably, you will want to enable logging as early as possible in the app's lifecycle. This minimises any logging that will occur before logging has been enabled. This can be done in the UIApplicationDelegate
initializer, or alternatively in the application(_:willFinishLaunchingWithOptions:)
function.
Using init
import NanoLog
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
init() {
super.init()
NanoLog.addDefaultConsoleLane()
}
}
Using application(_:willFinishLaunchingWithOptions:)
import NanoLog
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
willFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
NanoLog.addDefaultConsoleLane()
return true
}
}
This configuration will result in a logs being outputted to the Xcode console, using the default logging format.
For logging messages, there are methods available in NanoLog
that refer to the different severity levels. Each has a full-form and abbreviated form, depending on your preference, such as NanoLog.e
and NanoLog.error
.
func startLogging() {
NanoLog.error("A really important error")
NanoLog.w("Something you should probably look into")
anotherMethod(withString: "testing")
someMethod(withIntArg: 2)
}
func anotherMethod(withString string: String) {
NanoLog.info("Some useful information")
NanoLog.d("Some debugging data that contains the argument: \(string)")
}
func someMethod(withIntArg intArg: Int) {
NanoLog.verbose("Something verbose")
}
Often you will just want to use the default logging lane, which outputs to the console. Needless to say, particularly for iOS, watchOS, tvOS and MacOS, you won't want this logging enabled in production. A very simple way to achieve this is to not enable logging except for development builds of the app.
#if ENABLE_LOGGING
NanoLog.addDefaultConsoleLane()
#endif
For the DEBUG
symbol to work you must set it:
- Open your project file
- Select
Build Settings
- Got to
Swift Compiler - Custom Flags
- Add
-D ENABLE_LOGGING
to theOther Swift Flags
option.
To only enable logging for development builds, you will only want to set this for Debug
.
Beyond the default setup, you have the option of configuring whether particular messages are logged, where they are logged to and how they are logged.
Each layer of the logging process uses protocols, to allow you to use your own implementations if you wish. This includes:
LoggingLane
to have more specific control over how messages are logged.LogFormat
to customise the format with which messages are logged, described below.LogFilter
to choose which messages are logged, described below.LogPrinter
to specify the printing process for messages, described below.
When adding a NanoLoggingLane
instance, you can choose which filter, format and printer are used. A common use-case is to customise the logging format.
PrettyLogFormat
allows you to use a customisable logging format, which drives how messages are formatted before being sent to the console. This is as simple as providing an array of LogFormatComponent
.
let loggingFormat = PrettyLogFormat(withComponents: [
.date(withDateFormat: "HH:mm:ss.SSS"),
.separator(string: " | "),
.severity(withFormat: .label),
.separator(string: " | "),
.file(withExtension: false),
.separator(string: ":"),
.lineNumber,
.separator(string: " | "),
.message
])
let lane = NanoLoggingLane(format: loggingFormat)
NanoLog.addLoggingLane(lane)
The example is how the default logging format is specified internally to the framework. There are other options available to you, have a look at LogFormatComponent
for details.
You can also use your own implementation of the LogFormat
protocol. It is as simple as implementing the formattedMessage(from:withSeverity:withTag:forFile:forFunction:forLine)
function and returning a String
for output.
class CustomLogFormat: LogFormat {
public func formattedMessage(from message: @autoclosure () -> Any,
withSeverity severity: LogSeverity,
taggedWith tag: String,
calledAt callSite: LogCallSite) -> String {
return "\(severity.label): \(message)"
}
}
let lane = NanoLoggingLane(format: CustomLogFormat())
NanoLog.addLoggingLane(lane)
I would suggest trying out PrettyLogFormat
first, as it is easier to specify a list of components. Then, if what you want to do requires either extra components or something more complicated then consider your own LogFormat
implementation.
Another common use-case for customisation would be to filter which messages are logged. An example of this would be only logging messages above a particular severity.
let lane = NanoLoggingLane(filter: MinimumSeverityFilter(for: LogSeverity.debug))
NanoLog.addLoggingLane(lane)
You can write your own logging filters as well.
class ImportantFilter: LogFilter {
func isLoggable(at severity severity: LogSeverity, taggedWith tag: String) -> Bool {
return tag == "IMPORTANT"
}
}
let lane = NanoLoggingLane(filter: ImportantFilter())
NanoLog.addLoggingLane(lane)
The default LogPrinter
prints messages to the console. You can use your own LogPrinter
implementation to send messages elsewhere.
class MessageStore: LogPrinter {
var storedMessages = [String]()
public func printMessage(_ message: String) {
storedMessages.append(message)
}
}
let messageStore = MessageStore()
let lane = NanoLoggingLane(printer: messageStore)
NanoLog.addLoggingLane(lane)
Can add as many logging lanes as you wish, the example above stores the messages that are logged.
To add an extra LogSeverity
you can simply create a LogSeverity
instance and then use it via the NanoLog API.
let concernSeverity = LogSeverity(severity: 350, label: "CONCERN", icon: "⚪️")
NanoLog.message("A general concern", withSeverity: concernSeverity)
Using an extension to LogSeverity
you can make you new severity more discoverable.
extension LogSeverity {
static var concern: LogSeverity = {
LogSeverity(severity: 350, label: "CONCERN", icon: "⚪️")
}()
}
NanoLog.message("A general concern", withSeverity: .concern)
You can also add an extension to NanoLog
to access it through a named function. This allows you to use it just like any of the built-in severity levels.
extension NanoLog {
static func concern(_ message: @autoclosure () -> Any,
file: String = #file,
function: String = #function,
line: Int = #line) {
NanoLog.message(message, withSeverity: .concern, file: file, function: function, line: line)
}
}
NanoLog.concern("A general concern")
You can re-assign the built-in severity levels, which is useful if you want to change their label or icon for example.
LogSeverity.error = LogSeverity(severity: 500, label: "ALERT", icon: "🔺")
In the above example, any log calls at the 'error' severity will now use the new label and icon.
Andrew Lord @lordcodes