Java Logging – Printing to Out vs Logging

javaloggingreporting

I'm writing a command line application in Java. That app will scan a list of remote resources, do several attempts to access those resources, report failed attempts, and if succesfully accesed, perform some maintenance or diagnosis action and report the outcomes of that action.

As you can imagine, there's a lot of error handling and error reporting. But reporting the errors is a feature. Let me explain: a report with the errors encountered when attempting to access to the remote resource is something the user expects from the app, since those failures are useful for the diagnosis. Such access failures are expected in many cases and the causes should be reported.

As you can see, there's a blurry line between what's an error and what's the expected output of the app, which as every app will have its own errors also.

I'm using the Java Logging API and I'm designing my classes in a way as to be able to add different loggers and swap loggers and/or handlers (to screen, to file, to database, etc), as well as different list of resources to scan (and different types of those resources).

The problem is that I'm tempted to call logger.log() everywhere I was previosly using System.put.println();

Right now I'm now in a position where there's a blury line between what should be printed with System.out.println() and what should be send to the Logger instance.

I believe I'm having a conceptual confusion.

So the questions are, given the fact that errors reporting is a part of the core functionality of the app and that, as any app, it has to handle and log its own errors:

  • What things should be printed with System.out.println()
  • What things should be forwarded to a Logger
  • What things should be both printed with System.out.println() and forwarded to a Logger
  • Should normal output be considered as being log messages of a low severity level or not at all?

EDIT:

Just to clarify, the app could attempt to download a file, but it's
not the file what the final user wants, but to see whether or not it
was accessible and if not, why. The resource could be a server, a
database, a service or a file. The user doesn't want the app to get
what the resource offers but to check availability and report the
cause. That somehow makes failed attempts code errors the output users
want to get from this app. Hence the thin line between logging or
printing.

Best Answer

The fact that you are writing a console application doesn't change good UI practices.

  • Your business logic should be distinct from your UI. The business logic performs the operations and indicates what might have gone wrong. The UI decided how that should be displayed.

  • If there is information that is useful for tracking the execution of the application after something has happened, it should be written to a log file. This will primarily happen in your business logic classes. The fact that something is written to a log file does not inherently mean that the UI does not need to display it. Things that are important enough to display to the user is likely important enough to log, but it does not need to be in the same form.

    What goes in the log should be more detailed: What exactly went wrong? What were the inputs when that happened? You can also give more fine grained details about what is currently executing. To the user, it might be a single task, but to the code it could be a four step process. The nice thing about logging is the different levels let you have very detailed log messages in your code, but allowing some of them to be omitted from the log file.

  • Don't write code that is statically configured to only work one way. A command line application will obviously be dealing with standard out a lot. However, that doesn't mean your classes must have System.out.println() everywhere. You classes could write to an OutputStream. By initializing your UI classes with the OutputStreams/InputStreams that they should work with, it will make it much easier to test the code. Being able to use a ByteArrayOutputStream in an automated test is a great way to check if content is being output correctly.

    An example of what I am suggesting:

    public class A {
      private final OutputStream _out;
    
      public A(OutputStream out) {
        _out = out;
      }
    
      public void Run() {
        _out.println("Write to UI here");
      }
    }
    

    It is true that System.out is an OutputStream, but there are many classes that implement that interface. If you hard code your classes to use System.out, that is the only way the code can be used. If you pass in the output stream to write to, now you have the option to write to files, network sockets, and other things.

Related Topic