For some time I've been going on about how I dislike the way that logging frameworks work. My last post outlined what I wanted in a framework. Today I've pushed up v0.2 of my new framework: Story Teller.
So what is Story Teller and how does it work. Lets start with some code from a legacy logging framework:
... logInfo(@"System is using database URL %@", dbUrl); ... logDebug(@"Currently processing user %@", user.name); ... logError(@"Something's gone wrong with %@", account.id); ...
There are several problems with this sort of logging. The first issue is that the logging is based on an abstract concept of Severity. Something that rarely has any useful connection to the log message. Making it difficult to sometimes get effective logging. Especially if the developers having been less than diligent in the first place.
Secondly to get details on a user, you will have to turn on all Debug level logging. Then you will get inundated with all Debug level logging from the entire code base, regardless of whether it's relevant to users or not.
Finally, assuming that the code that produces the Error does not have access to the user object, no way to establish a link between the Error log and Debug log statements. All you could do was hope that the two statement's output was close in the log and not intertwined with other user's logging.
All this means that a criteria such as "Find the problem with Fred's accounts" can be a real nightmare with a traditional logging framework.
So with Story Teller I decided to adopt the Apple mantra: 'Think Different!' and started from the view point that logging is not about dumping everything into the console on the offchance that the developer can pick out the nuggets. But to actively target the desired information.
Story Teller to the rescue
So here is the above logging Story Teller Style!
... STLog(@"db", @"System is using database URL %@", dbUrl); ... STLog(user, @"Currently processing user %@", user.name); STStartScope(user); ... STLog(account, @"Something's gone wrong with %@", account.id); ...
At first glance not very different. Which is the point. A logging framework should not be hard to use. Notice we have dropped the abstract concept of severity and replaced it with a Key argument on each call. Story Teller lets you use anything you like as a key to a logging statement. So here we use a fixed string (
@"db"), a user object and an account object.
We also start a scope for the user object. Scopes tell Story Teller that even though the account statement does not mention user, it should be logged if user is logged. As long as the scope is in effect, any logging statements will be regarded as also being logged under user. Even if they are nested in a called method. Scopes follow normal variable scopes.
So now we can be confident that we can establish a link between the two statements.
Logging with Story Teller
So now we know we can key our logging intelligently, and make associations to higher level data structures. How do we specify what to log?
You basically have three options:
- A StoryTellerConfig.json file stored in your app.
- Command line arguments (Usually via a XCode Scheme).
- Programmatically using a
STStartLogging(...);statement. I'll use this method here.
Now lets activate some logs;
This activates logging of anything using the
@"db" key. Pretty straight forward and using hard coded key like this we can even setup logs similar to other frameworks.
STStartLogging(@"[User].name == Fred");
Now this is interesting! Here we us a simple expression to activate any logging where the key is a User class and has a name property with a value of 'Fred'. Try doing that with those other frameworks.
The net result is logs where you will see a huge difference between Story Teller and other frameworks. Story Tellers log will susinct and contain ONLY the logging for Fred. No longer will you have to troll through hundreds, if not thousands of lines of irrelevant logging to find what you want. Woot!
Story Teller has quite a range of criteria you can apply to you logging. Classes and protocols, nil checks and numbers are some examples. So go check it out. Hopefully you will find it interesting.