This is a lightweight library for logging debug messages in your Arduino code and then controlling where the messages go.
This library is a combination of separate code cobbled together that originally came from Alexander Brevig (CascadeDebugger) and David A. Mellis (CascadePrinter). But I have heavily modified and extended the code over time, and I am releasing it as a separate, new library. But credit goes to them for the original work.
If you have any questions or bugs, please open a new issue on github.
Like almost all developers, there comes a time where you just need to insert debug messages that get printed out during the execution of the code. This is especially true with Arduino code. Most of the time Serial is used directly to print. But it's no surprise that there are many libraries out there that extend from Serial and add a little more power. ArduinoLogging is no exception.
ArduinoLogging provides the following features:
- Defaults to printing messages to the Serial port at program start. But this can be easily changed to print messages to other destinations, such as auxilary serial ports or even UDP.
- Allows for "cascading" print calls to make print calls more compact.
- Supports "message levels" to be associated with print calls, and messages will only be printed if the associated level is enabled.
- Any object that supports the
Print
interface, which includes allStreams
, can be used to print the debug messages. Simply reference it in aPrintWrapper
instance and set it for use. This works for simple destinations like auxilary serial ports whether hardware or software based. - While any object supporting
Print
can be used, some object types also require some sort of open and close mechanism before and after printing to them. This is also supported inPrintWrapper
, allowing subclasses to implement open and close methods that are called at the correct time when printing debug messages. This works for more complex destinations like a remote UDP port. - Once a new
PrintWrapper
is applied, all subsequent messages will be printed using it. - Though these classes have been written to support debugging, the base
class,
CascadePrinter
, can be used separatly to implement other situations where one needs to print to different destinations. - All debug messages can be disabled and not printed by simply disabling
all message level in the
DebugMsgs
object (which is its default state).
To use Arduino Logging, simply include the file DebugMsgs.h
, and then use
the DebugMsgs
global object to print messages.
#include <DebugMsgs.h>
void setup() {
Serial.begin(9600); // Initialization of the Serial port must be performed before usage.
DebugMsgs.enableLevel(DEBUG);
DebugMsgs.debug().("Some ").print("message ").print(" to be").println("printed!");
}
void loop() {
DebugMsgs.debug().print("The current millis is ").println(millis());
delay(1000);
}
PrinterWrapper
is the primary class that performs the printing to the
Print
reference that it wraps.
- In addition to the print related methods, three other methods are
open()
,close()
, andrequiresOpenClose()
. The default implementations effectively do nothing and only need to be overridden by subclasses if thePrint
type requires some kind of open and close when printing to it. - If a subclass returns true for
requiresOpenClose()
then theopen()
method will be called with the first of anyprint
orprintln
method called. Theclose()
method will be called at the end of the firstprintln
method called. In this way the messages will be sent to the destination in chunks decided by theprint
andprintln
calls, typically line by line. - All initialization of the
Print
reference wrapped byPrintWrapper
is expected to be done outside of the class. No initialization is performed byPrintWrapper
. For example, theSerialPrintWrapper
expects theSerial
port to be initialized with a call to thebegin(int baud)
method before messages are printed. TheUDPPrintWrapper
expects theUDP
instance to have been initialized before any messages are printed. - Examples of
PrintWrapper
subclasses are SerialPrintWrapper, NullPrintWrapper, and UDPPrintWrapper.
CascadeDebugger is the class used for the DebugMsgs global.
- It has all the methods related to enabling and disabling the message
levels as well as the message methods, like
debug()
. The message methods control whether the followingprint
andprintln
methods will print their contents. If the associated message level is not enabled, then nothing will be printed. - All message printing can be disabled by calling the
disable()
method (which is the default at program start). SerialPrintWrapper
is the default at program start, and will print messages to the Serial port. A differentPrintWrapper
subclass can be set at any point during the program execution. All subsequent messages will be printed by the newPrintWrapper
instance.
CascadePrinter
is the base class for CascadeDebugger
.
- It has all of the
print
andprintln
methods used for printing the message contents. This set of methods exactly matches the methods provided by thePrint
interface. These versions return a reference to the currentCascadePrinter
so that methods can be appended for cascaded printing. - In addition it also supports variable insertion and formatting methods, printf and printfln.
- It has all of the methods for handling the open and close calls
of the
PrintWrapper
class.
Four different print methods are supported for output on
CascadePrinter
and CascadeDebugger
.
-
print(value) - This will print the given value to the output. It does not print a new line. Any subsequent printing will be started directly at the end of the value previouly printed.
-
println(value) - This is the same as the print() method, but it will also print a new line. Any subsequent printing will begin on the next line.
-
printf(format string, values...) - This will print the given format string with the given values inserted into the format string. It supports all of the normal data types and data formatting supported by C/C++. It does not print a new line. Any subsequent printing will be started directly at the end of the value previouly printed.
-
printfln(format string, values...) - This is the same as the printf() method, but it will also print a new line. Any subsequent printing will begin on the next line.
While all message output can be disabled by disabling all of the message levels, the logging code calls still exist within the compiled code, and messages with inserted values are still being evaluated at runtime. All logging related code can be removed at compile time from any program by utilizing proveded LOG macros to output logging messages. The macros will expand to appropriate logging code in normal usage. But with the setting of a single flag the macros will expand to nothing, and not included in the compiled code.
-
LOG_ENABLE(MsgLevel)/LOG_DISABLE(MsgLevel) - Enables or disables messages printed at a given message level. The values of ALL and NONE can be used to enable or disable all message levels.
-
LOG_DEBUG(msg, values...)
LOG_WARNING(msg, values...)
LOG_ERROR(msg, values...)
LOG_NOTIFICATION(msg, values...)
LOG_STATUS(message, values...) - These macros will print the given message, with any given values inserted, at the appropriate message level. It will print the message and then a new line. A subsequent message will appear on the next line. -
LOG(msg, values...) - This macro will print the given message, with any given values inserted, regardless of any enabled message levels. No timestamp or message level will be printed at the beginning of the line. It will print the message and then a new line. A subsequent message will appear on the next line.
-
INCLUDE_LOGGING - This is the flag that can be used to disable and remove all LOG macros. By default it will be set to "true" and all macros will function. But if it is set to "false" BEFORE the include for
DebugMsgs.h
, then all LOG macros will evaluate to nothing, and all logging code will be removed from the code.
Below is a very simple example:
// uncomment to remove all logging code
//#define INCLUDE_LOGGING false
#include <DebugMsgs.h>
void setup() {
Serial.begin(9600); // Initialization of the Serial port must be performed before usage.
LOG_ENABLE(DEBUG);
LOG_DEBUG("Some message to be logged");
}
void loop() {
LOG_DEBUG("The current millis is %d", millis());
delay(1000);
}