In this tutorial we'll build a "logger" mixin, which outputs a flexible, informative log as CSS when Sass is compiled.



Logging is the process of recording application actions and state to a secondary interface. - Code Project



The Idea

The other day, Neat maintainer Reda Lemedan and I were talking all things Sass and all of the sudden I saw an interesting mixin of his:

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 );

, , , and ); 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:

... we could have:

So you'll end up with an API looking like this:

We'll also add some mixins to bring in some cool extra features:

one printing information about each logging level as a reminder;

one printing all logs that have been registered in the current compilation.

Building the API

Logger Constructor

Let's start with the beginning, shall we? The logger constructor. This should accept a single parameter: the level at which the logger should start printing logs in the console.

This is quite a common pattern for logging systems. For instance:

If you only want to print errors ( ERROR and FATAL ), you'd write @include logger("ERROR") .

and ), you'd write . If you want to print everything, you'd go with @include logger("ALL") , which is basically the same as the lowest logging level ( DEBUG ).

, which is basically the same as the lowest logging level ( ). If you want to disable the logger altogether, you run @include logger("OFF") .

Note: you can find more information about logging levels in this StackOverflow thread or in the Apache logs documentation.

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:

Okay. Let's go.

It involves quite a few unfriendly lines, but when you explain each line individually, it all makes sense eventually.

We're done here, but we spoke about adding shorthand functions. Let's do that now before we forget:

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.

You use it like this:

...and it compiles as:

Nothing special. The other extra mixin is way more interesting. It uses the history to print all logs that have been registered during the compilation.

Again, simple use:

... which would output (based on our earlier example):

Example

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.

Feel free to grab the mixin from GitHub, or play with it directly on SassMeister.