The Idea

I asked him what this mixin does, and he told me it's basically a wrapper for the @warn directive from Sass that checks whether or not the user is willing to print warnings from Neat in the console (based on a global variable).

So I thought to myself why stop there? and started playing with the idea that same night. My idea was to build a wrapper for both @warn and @error (from Sass 3.4) to help library and framework developers print different type of messages (info, debug, warn, error...) and keep track of all logs.

My current implementation provides:

5 levels of logging (DEBUG, INFO, WARN, ERROR and FATAL);

a minimum level at which the logger starts printing;

a history of all logs, that can be printed as CSS;

a friendly API with easy-to-use functions;

a helper to learn more about different levels of logging.

How Does it Work?

It turned out to be fairly straightforward. We need a global variable holding the whole configuration, and a mixin serving as a wrapper for our console printing directives.

Because we want our global configuration to be customisable (to some extent), we wrap its declaration in a mixin. Not only is this more convenient, but it's also nicer for the end user.

So we have a mixin, let's call it logger that is only intented to be called once, creating a global map holding our configuration. Then we have our wrapper, log, that accepts a logging level (for instance WARN or ERROR) and the message to log as arguments. That's pretty much it.

To make things more convenient to the developers, we will provide some shorthand functions to log different levels. For instance, instead of having to type:

The code above should be mostly self-explanatory, but I've added some comments to make everything clear. As you can see, this mixin doesn't do much except create a global variable. Not that bad, is it?

Before going any further, let's create a little helper function that makes it easy for us to get a value from this global map. Because you know, typing map-get($logger-configuration, ...) isn't remotely fun. What about logger-conf(...) instead?

Log Wrapper

Okay, let's move on to the actual log function which prints things in the console. Not only should it output the given messages in the user's console, but it should also update the history in order to keep track of what's being logged (which might or might not be useful).

That sounds difficult. Well worry not, it's going to be as smooth as butter. We already know this mixin should accept only two parameters: the logging level, and the message.

Now, we need to deal with updating the history. This is actually a little tougher, but once you get used to map manipulation, it becomes clearer.

Before looking at the code, let me explain how is the history working. Basically, it's a map where keys are the logging levels, and values are lists of logged messages. For instance, you could have something like:

That's it. One last thing we could do, but isn't really mandatory, is testing whether logger has been included before trying to use the global map. Not only do we prevent stupid mistakes, but we could also make the logger instantiation optional by doing it on the fly.

Adding Extras

We'll start with the first (and least useful) of both extra mixins, the helper. It's really a gadget at this point since all it does is print a CSS rule with logging levels as selectors, and explanations as values.

This is intented to give some help to developers when they don't really know which logging level they should use. It could have been written as a comment but I wanted to try this help printer thingie.

Example

// Instantiate a new logger with `INFO` as the minimum level for logging.
// If not included, it will automatically be done on first log.
@include logger("INFO");
// Logger help (optional, obviously)
@include logger-help;
// Log stuff
@include INFO("Hey, look at that.");
@include INFO("Bring in the unicorns!");
@include WARN("Dude, pay attention.");
// This one is not printed but still tracked in logs.
@include DEBUG("Debug and stuff.");
// Output history (optional) especially useful for debugging
@include logger-print-logs;

Final Thoughts

As you can see, the code is quite light in the end, plus most of its bulk is comments. I think it provides a nice clean API helping keeping track of what's being logged in any given project.

This is tool aimed at library and framework developers. If you happen to be one, please give this a try if you think it could be useful, and give me your feedback.