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.

Example:
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 5
Console 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 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 
  • caseSensitive: Bool (optional, false on default): case-sensitive must be met 
  • required: Bool (optional, false on default): must be met if the destination has multiple filters 
  • minLevel: 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:

  1. you can directly create a new filter inside an addFilter() call
  2. the filter is case-sensitive. It logs messages containing the word "HTTP" but not "http"
  3. 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.swift
AFTER:
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.

Still need help? Contact Us Contact Us