Share this page to your:
Mastodon

Most Arduino projects are one or two files and logging from those is not a big deal. Typically you have a few Serial.print statements that write information back to the serial monitor in the Arduino IDE. When you're done with them you might take them out of the code, or you might not bother. There aren't that many of them so it doesn't matter. Even so there are several Arduino libraries that implement the kind of logging features I'm used to from Java. They have levels so you can have log statements that specify if this is a debug message, an error message or just an information message. You can turn off all the debug messages and just leave the others active, and you can turn them back on if you suddenly find you need them.

But what they don't do is offer a way to have multiple loggers. For example in my current project I want to see debug messages for the display subsystem, but there are lots of debug messages generated from the clock. Can I turn off the messages from the clock and just see the display stuff? Nope. Not without editing the code and commenting out the messages I don't want to see. And even if I do that I might find I need to uncomment them tomorrow. It gets a bit tedious.

Now, in Java you have multiple logger objects, and each of those has its own debug level. I'm used to that and, on an Arduino project that now has 20 cpp files, I really feel the need of a better logging mechanism.

That's why I built ArduinoLogger.

At this point I need to digress a little an mention that I don't actually make much use of the Arduino IDE itself. I use an Eclipse based IDE called Sloeber which does all the things Arduino does but is a lot better when your file count grows. It also supports git which is much easier than going out to the command line for that. I tend to think in terms of C++ rather than ino files and I use lots of classes. I'm building code to run on a Teensy 3.2 which has plenty of memory so a bit of memory overhead for a logger is not a problem. On a smaller device you probably aren't working on such a large project, so you probably would not want, or even need, the overhead.

So let's see what it looks like.

The library is called ArdunioLogger and is in a zip file at https://madurasoftware.com/ArdinoLogger.zip. To install it download the zip and you can use the Arduino IDE to install it. Use Sketch>Include Library>Add .ZIP Library... to import the library.

After that you will find a sample in the IDE samples (File>Examples and look for ArduinoLogger) which is a good place to start. Or you can just unzip the file into ~/Arduino/libraries which is where the IDE unpacks it to. Once unpacked both IDEs can see the library and you can add it to any sketch or project you need it in.

To use it you need to import the Logger.h file and then create the Logger factory like this:

 LoggerFactory loggerFactory = LoggerFactory(true);

The next step is to configure it. This is what I use:

 loggerFactory.add("VM",LOG_LEVEL_INFOS);
 loggerFactory.add("AppRegistry",LOG_LEVEL_ERRORS);
 loggerFactory.add("TOUCH",LOG_LEVEL_ERRORS);
 loggerFactory.add("GESTURE",LOG_LEVEL_ERRORS);

This is telling the factory that we're going to have a logger called VM and we want its log level set to info. There are several levels: everything, debug, info, error. Setting the level to info means we will see info and error, but not debug. If we set it to debug we will see debug, info and error. And if we set it to error we will only see error messages. Now that we've configured the logger factory we can create the loggers like this:

 Logger *myLogger = loggerFactory.getLogger("VM");

And then we can use it to generate messages:

 myLogger->info("this is an info message");

This will give us a message that looks like :

 [VM][info]this is an info message

The message is an info message and we do have info messages configured to display. It tells us what logger generated the message and its type. This helps when finding odd messages you are trying to get rid of. If we decide we are seeing more info messages than we want from VM we can change its configuration to only show error messages and the above message will no longer appear. There are, of course, error() and debug() methods as well. All these methods actually take a format string at their first argument, and a variable list of arguments after that, much like printf. So we can say things like this

  int intVar = 100;
  myLogger->info("my total = %d",intVar);

This can do a lot:

  • %s replace with an string (char*)
  • %c replace with an character
  • %d replace with an integer value
  • %l replace with an long value
  • %x replace and convert integer value into hex
  • %X like x but combine with 0x123AB
  • %b replace and convert integer value into binary
  • %B like x but combine with 0b10100011
  • %t replace and convert boolean value into "t" or "f"
  • %T like t but convert into "true" or "false"

(This is code I copied from mrRobot62)

It is important to note that the string merging on this is done after the decision to print, so we don't merge the output string unless we know we are going to print a message. Even so it is sometimes worth knowing the status of the logger in your calling code so you can avoid wasting resources. For example:

  if (myLogger->isDebug()) {
          myLogger->debug("this is the result %d", someVeryComplexMethod());
  }

Remember the string merge is the only thing that gets suppressed. Evaluating the arguments is done anyway and when it is expensive you should avoid doing it.

The LoggerFactory will wait until a Serial monitor connection is made before proceeding, so you don't miss any messages. Once you are done with logging you want this suppressed, and you want all the logging suppressed too. Just change the LoggerFactory:

 LoggerFactory loggerFactory = LoggerFactory(false);

Now your program will behave as if logging was never there.

Previous Post Next Post