Swiftybeaver filters are a powerful, unique feature to match the amount of logging output to your current needs by adjusting the minimum log level for a destination under certain log filter conditions. It’s a true productivity booster & time-saver!
With a single line of code you can enable or disable 1,000s of log messages in your application's source code or move log messages to a certain destination for further inspection. And you can even chain & route filters for the ultimate logging setup!
Focus & Direction
During development, filters are giving you focus just on the logs of the file or function you are currently working on and letting you ignore the rest of your project's logging activity,
During release, filters let you send log messages containing certain texts to a destination of your choice with routing, making it even easier to detect and react to errors or unusual application behavior.
Swiftybeaver filters are optional. Without filters or when no filter applies, SwiftyBeaver is using the optional destination property
.minLevel. It decides which levels are logged and which are not in ascending priority from
let console = ConsoleDestination() console.minLevel = .info ... // example calls log.verbose("not so important") // priority 1 log.debug("something to debug") // priority 2 log.info("a nice information") // priority 3 log.warning("oh no, that won’t be good") // priority 4 log.error("ouch, an error did occur!") // priority 5Console logging output:
INFO: a nice information WARNING: oh no, that won’t be good ERROR: ouch, an error did occur!
.debug message are not logged because their priority is lower than the set
console.minLevel = .info
Good to know: If no
.minLevel property was added to a destination then all levels are logged.
You can filter / group logs by
Path (in what source code file or folder the log call is), by
Function (in what source code function the log call is) and by
Message (what the log message is).
All SwiftyBeaver filters are singleton variables and have the same matching functions and arguments which makes them easy to learn, understand and use. Filters need to be added to a destination with
.addFilter() before they become active.
Firstly, the top-level instance of each filter is its type
Filters.Path: filter by path or filename
Filters.Function: filter by function name
Filters.Message: filter by log message
Secondly, all filter types have matching functions which decide if the filter is applied / met or not for a log message.
Filter Matching Functions:
.contains(args): does the filter contain the string?
.excludes(args): does the filter NOT contain the string?
.startsWith(args): does the filter start with the string?
.endsWith(args): does the filter end with the string?
.equals(args): does the filter equal the string?
Thirdly, all filter matching functions have the same arguments to more granular set the filter:
Filter Matching Function Arguments:
_: String(required): the string pattern
falseon default): case-sensitive must be met
falseon default): must be met if the destination has multiple filters
.verboseon default): the level the filter must at least have
If all that sounds a bit complicated, don’t worry, the following examples should make filters more clear and enjoyable 😉 🎉
1. Focus on a File
Often during development or debugging, you want to get more verbose logging of the source code file you are currently working on without constantly (un)commenting
Let’s take an example where you are are working on the next big
UnicornViewController.swift full of 🌈 and 🦄 and you want to see all
.error log messages in that file. All other log messages in other parts of your app are currently not so interesting and you just want to log errors.
Let's use this SwiftyBeaver logging setup:
let console = ConsoleDestination() console.minLevel = .error // global minLevel let filter1 = Filters.Path.contains("UnicornViewController", minLevel: .debug) console.addFilter(filter1)
2. Focus on a Function & File with Filter Chaining
Now let's drill a bit deeper into example 1 and just get all logs (including
.verbose) from the function
UnicornViewController.swift. To achieve that outcome we are chaining two filters.
To chain a filter with another filter you just need to add
require: true to the chained filters.
Good to know: if no
.minLevel is set for a filter then it is automatically
let console = ConsoleDestination() console.minLevel = .error // global minLevel let filter1 = Filters.Path.contains("UnicornViewController", required: true) let filter2 = Filters.Function.contains("viewDidLoad", required: true) console.addFilter(filter1) console.addFilter(filter2)
Here all logging calls from
UnicornViewController.swift:viewDidLoad() are logged but everything outside of that function needs to be of level
.error or above to be logged.
3. Log Certain Messages to an own Destination with Routing
Let's assume that you are making progress with your Unicorn app and you set your global
.error and all errors should go to Xcode's Console. But you are still somehow nervous about all networking calls so you want to be more verbose on all log messages containing the word "HTTP" and store them in a file so that you have a backup of it for your records.
For that very typical use case you are routing log containing a certain log message to their own, dedicated logging destination.
// console takes all errors let console = ConsoleDestination() console.minLevel = .error // all network related logs go to a file if you build a macOS app let file = FileDestination() file.logFileURL = URL(string: "file:///tmp/myNetworkLogs.log") file.addFilter(Filters.Message.contains("HTTP", caseSensitive: true, required: true))
In the code snippet you see 3 interesting things:
- you can directly create a new filter inside an
- the filter is case-sensitive. It logs messages containing the word "HTTP" but not "http"
required: truecauses the filter to just be applied for that condition which avoids that any other log message is routed to the file destination
4. Silence Files with Exclusion
Sometimes you do not want to have more logging of a certain part of your app but less. That is possible with an exclusion filter.
Let's assume that somehow your colleague added a lot of
log.info() calls in his part of your app which is all about horses. He has a
Normally you like 🐴 but hey, it is an app about 🦄 and 🌈 and these are definitely too many log messages for you at the moment! Here is the fix in a before-after setup:BEFORE:
let console = ConsoleDestination() console.minLevel = .info // global minLevel // ... resulting in a lot of noise from HorseViewController.swiftAFTER:
let console = ConsoleDestination() console.minLevel = .info // global minLevel console.addFilter(Filters.Path.excludes("Horse", required: true))
A single line of code silences all logging output from any file containing the word "Horse" - hooray! 🎉
Filters may appear complex, yes, even complicated at the first glance but once you dive it they really start to shine and you don’t want to miss them in your daily coding workflow anymore.