Logging is an important part of any application. It is a way to capture that certain events happened, the state of an object, what configuration parameters were loaded or the version of the software that is running. More importantly, if something goes wrong, logging can be used to help diagnose and debug the error.

There are several logging frameworks for JAVA out there. The JNIOR requires all of the classes to be resident in a single JAR file. You could try to merge the library jar into your JAR but it is likely that the logging library JAR is dependent on other classes as well. For that reason, INTEG provides logging classes for you to use.

INTEG provides two types on log files. One type is a backup file concept that renames the .log file to log.bak when the log file reaches the size limit. The other type of file is a rolling log. The rolling log will remove the oldest entries as new entries are added.

Instantiation and Method Availability

The static .getLogger() method is used to get the log. The FileLog classes cannot be instantiated. You need to call the .getLogger() method on the correct class to get the log file type that you want to use. The following snippet is an example of getting a log file of both types. There are additional lines to send text to the log files of differing levels.

        //
        // get a rolling log file
        Logger rollingLogger = RollingFileLog.getLogger("rolling.log");
        rollingLogger.info("rolling log file has been instantiated");
        rollingLogger.vital("woah, something vital occurred");
        rollingLogger.debug("something to help you debug");
        rollingLogger.warn("warning software developer! warning!");
        rollingLogger.error("uh oh! error!");

        //
        // get a bak log file
        Logger bakLogger = BakFileLog.getLogger("bak.log");
        bakLogger.info("backup log file has been instantiated");
        bakLogger.vital("woah, something vital occurred");
        bakLogger.debug("something to help you debug");
        bakLogger.warn("warning software developer! warning!");
        bakLogger.error("uh oh! error!");

Here is the output from the above code. Both files will generate the same output. The differences come when they reach their maximum file sizes. Each log method will generate a line starting with the date and time, comma, and the text. Certain methods will prepend 2 characters to help make those lines stand out.

11/19/19 15:24:31.272 EST, --------------------------------------------------
11/19/19 15:24:31.482 EST, rolling log file has been instantiated
11/19/19 15:24:31.503 EST, ## woah, something vital occurred
11/19/19 15:24:31.514 EST, ?? something to help you debug
11/19/19 15:24:31.529 EST,  * warning, warning! woop woop!
11/19/19 15:24:31.541 EST, ** uh oh! error!

Buffering and File Flushing

Log files are not immediately written to when the logging methods are called. A BufferedOutputStream is used to only write to the filesystem when a chunk of text is ready to be committed. This can cause a delay between when the event happens and when the event is ready to be viewed in the log file. To minimize the delay, a timer is used to force the logs to flush if they have had data waiting for five seconds.

Final Flush with Finalizer

Generally applications on the JNIOR run constantly while the JNIOR is powered up. But there are times that the application is ended gracefully. This happens when a user requests the unit to be rebooted. At this time the finalize methods are called and the log files are flushed.

You might want to override the finalize methods yourself to log when your application is shutting down.

Easily Change Output Target based On Logger Class Used

Another benefit to using the Logger class is that later on you can redirect the output without changing much code. You only need to change the initializer and all the logging calls remain the same. An example of development code and the associated initializers for released code is as follows.

        //
        // debugging file logger.  later we can just change it to SystemOutLog.getLogger()
        // or NullOutLog.getLogger()
        Logger debugLog = BakFileLog.getLogger("debug.log");

        // the following causes the output to go to the System.out output stream
        debugLog = SystemOutLog.getLogger();

        // the following causes the output to fall into the bit bucket
        debugLog = NullOutLog.getLogger();

        debugLog.info("This is some information that will go to a log during development.  "
                + "When we release the code we can assign the SystemOutLog.getLogger() and "
                + "the output will go to the System.out stream or we can assign the "
                + "NullOutLog.getLogger() and the output will go to the bit bucket");

Setting Log File Size

Each log file has a maximum size. By default the maximum size is 64 KB. The log file types will handle the maximum file size logic differently. A backup file log type will grow to the maximum and then rename itself to .bak and start over. This means you will always have the maximum to two times the maximum is log files. The rolling log file type will always try to maintain the maximum file size.

You can adjust the default size by calling the .setMaxSizeKB() method. Taking the debug log code from above, we can add the following…

    Logger debugLog = BakFileLog.getLogger("debug.log")
             .setMaxSizeKB(32);

Final Thoughts

While both log types generate log files, you might be wondering, why choose one over the other. For me, I prerfer the RollingFileLog. The reason is simple, the number of files on the JNIOR. The BakFileLog creates 2 files per log. filename.log and filename.log.bak. There is a brief moment in time where the RollingFileLog creates a filename.log.bak file. That is during the file rolling maintenance procedure.