Filters
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.
No Filters
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 .verbose
to .error
.
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!
The .verbose
and .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.
Filter Syntax
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
Filter Types:
Filters.Path
: filter by path or filenameFilters.Function
: filter by function nameFilters.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 patterncaseSensitive: Bool
(optional,false
on default): case-sensitive must be metrequired: Bool
(optional,false
on default): must be met if the destination has multiple filtersminLevel: SwiftyBeaver.Level
(optional,.verbose
on default): the level the filter must at least have
Filter Examples
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 log.verbose
calls.
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 .debug
, .info
, .warning
and .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 viewDidLoad
in 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 .verbose
.
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 .minLevel
to .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
addFilter()
call - the filter is case-sensitive. It logs messages containing the word "HTTP" but not "http"
- the
required: true
causes 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 HorseViewController.swift
, HorseModel.swift
,etc.
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! ๐
Conclusion
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.