Getting Started with CoralLog for Level Logging

In this article we present CoralLog, a powerful, ultra-low-latency and asynchronous logging library with a very intuitive and non-intrusive API. First we show you how you can use CoralLog for level logging, in other words, to log status messages from your application using the log levels available such as Debug, Trace, Error, etc. Then we show in a follow up article how you can use CoralLog for event sourcing, in other words, to write any kind of message (i.e. data) to a file without adding latency or variance to the critical thread doing the logging. If you are interested in the article with the performance numbers of CoralLog you can check here.

Level Logging

Let’s start with a CoralLog Hello World:

// this static import is all you need to start using CoralLog:
import static com.coralblocks.corallog.Log.*;

// then, anywhere in your code you can write:
Warn.log("Hello World!");
Info.log("Hello Again!");

When you execute the code above you see in the console:

13:54:21.322715-WARN Hello World!
13:54:21.322816-INFO Hello Again!

By default, CoralLog logs to the console (i.e. standard output) but you can easily change it to log to a file instead, as below:

// anywhere in your code, you can write:
// (or pass -DlogFile=true in the JVM command-line)
Log.setFile(true);

// now everything goes by default to a file:
Error.log("Hello World!");
Info.log("Hello Again!");

Now if you look in your current directory, you will find a file named corallog-levels.log with the contents:

LOG OPENED - 16/05/2018 05:02:42.237
05:02:42.238762-ERROR Hello World!
05:02:42.238762-INFO Hello Again!

To change the name of the file, you can do:

// (or pass -DlogLevelLoggerFilename=all-level-logging.log in the JVM command-line)
Log.setLevelLoggerFilename("all-level-logging.log");

To change the directory in where you want the log file, you can do:

// anytime, anywhere in you code, you can write:
// (or pass -DlogDir=/Users/coralblocks/ in the JVM command-line)
Log.setDir("/Users/coralblocks/");
	
// now the log below goes to /Users/coralblocks/corallog-levels.log
Error.log("Hello World!");

Logging is easy and you can choose among the flavors below:

// CoralLog varargs make it convenient to build your
// messages without the varargs array allocation overhead:
Warn.log("This is a log message!", "user=", "foo", "age=", 21);

Outputs:

14:40:41.589516 This is a log message! user=foo age=21

NOTE: With CoralLogs, you can use up to 16 objects in a varargs method call without producing any garbage, in other words, without causing the JVM to allocate a new object array to be passed to the method call.

When using varargs, the CoralLog default rule is to separate each object with a space character. The exception is when you have a string ending in ‘=’. Instead of “user= foo” it does not use the space and prints “user=foo”. If you don’t like this trick you can call the method below:

// do not suppress the space in between object after an '=':
// default is true, to NOT have the space after an '=' sign
// (or pass -DlogNoSpaceAfterEqualSign=false in the JVM command-line)
Log.setNoSpaceAfterEqualSign(false);

// and you can also suppress the space completely with:
// default is false, to have the space between objects
// (or pass -DlogNoSpaceBetweenObjects=true in the JVM command-line)
Log.setNoSpaceBetweenObjects(true);

If you prefer, you can also use placeholders when constructing the log message, as below:

Warn.log("This is a log message! user={} age={}", "foo", 21);

Of course you can also take control of the whole message by preparing your own StringBuilder, CharSequence, ByteBuffer or byte[] and logging it as below:

StringBuilder sb = new StringBuilder(1024); // cache and re-use this instance

sb.setLength(0);
sb.append("This is a log message!");
sb.append(" user=").append("foo");
sb.append(" age=").append(21);

Warn.log(sb);

Avoiding Autoboxing

One of the most important features of CoralLog is its zero garbage creation, in other words, you can log as much as you want without producing any (i.e. zero) garbage. The goal is to eliminate any GC overhead in the system. If you pass any primitive value (i.e. integer, long, boolean, etc.) to a log call, autoboxing will occur and garbage will be created under the hood by the JVM. CoralLog provides an easy way to escape autoboxing, as you can see below:

// use the to_sb method to translate a primitive into a StringBuilder:
Warn.log("This is a log message!", "user=", "foo", "age=", to_sb(21));

By using the method to_sb, you can translate on-the-fly a primitive value into a StringBuilder so no autoboxing occurs. You should know that whenever you write a primitive value to a StringBuilder no garbage is created, so another way to escape autoboxing is to write the whole message to a StringBuilder as the previous example showed:

StringBuilder sb = new StringBuilder(1024); // cache and re-use this instance

sb.setLength(0);
sb.append("This is a log message!");
sb.append(" user=").append("foo");
sb.append(" age=").append(21); // no garbage is created when you write 21

Warn.log(sb);

The difference is that the to_sb method only writes the part of the message (i.e. the primitive values) that would cause autoboxing to a StringBuilder, instead of the whole message.

Log Levels and Colors

When logging to the console (i.e. standard output), you can use colors to separate among the log levels. Linux/Unix terminals usually support this great feature, so to turn it on you can do:

// call that anytime, anywhere in your code:
// (default is false because some terminals (win) do not support colors)
// (or pass -DlogColors=true in the JVM command-line)
Log.setColors(true);

Screen Shot 2014-07-14 at 3.34.52 PM

If you prefer, you can also change these colors with the code below:

// change the Warn level to green:
// (or you can pass -DlogWarnColor=32 in the JVM command-line)
Warn.setColor(32);

The available colors are:

30 - Black
31 - Red
32 - Green
33 - Yellow
34 - Blue
35 - Purple
36 - Cyan
37 - White

Enabling/Disabling Log Levels

CoralLog log levels, in order, are: Debug, Trace, Info, Event, Warn, Error, Alert, Critical and Emergency. To log everything on and above the Trace level, you can do:

Trace.log("Can you see me?");

// log Trace and anything above Trace:
// (default is Info)
// (you can also pass -logLevel=trace in the JVM command-line)
Log.setLevel(Trace);

Trace.log("Now you can!");

In addition to setting the log level of the whole application, you can enable and disable individual logs, as the example below shows:

Warn.enable(true); // no matter what the level is, it will be logged

Warn.enable(false); // not matter what the level is, it will NOT be logged

Warn.enable(null); // go by the current log level as usual...

Although the log method will not log anything if the level is not enabled, you can use the conditional below to avoid having to build a message and making the log call:

if (Trace.isEnabled()) {
    Trace.log("Tracing something!");
}