The practice of logging is very simple. If you do not want to spend too much time experimenting with different logging solutions and you do not have some special requirement, then simply go with slf4j, add the JAR to the dependency list as a compile dependency, and start using logging in the source code.
Since logging is not instance-specific, and loggers implement thread safety, the log objects that we usually use are stored in a static field, and since they are used as long as the class is used, the program running the field is also final. For example using the slf4j façade we can get a logger with the following command:
private static final Logger log =
LoggerFactory.getLogger(MastermindHandler.class);
To get the logger, the logger factory is used, which just creates the logger or returns the one that is already available.
To create a log item the methods trace, debug, info, warn, and error create a message with the respective level as the name implies. For example, consider the following line:
log.debug("Adding new guess {} to the game", newGuess);
It creates a debug message. Slf4j has support for formatting using the {} literal inside strings. This way, there is no need to append the string from small parts, and in case the actual log item is not sent to the log destination, the formatting will not perform. If we use String concatenation in any form to pass a string as an argument, then the formatting will happen even if debug logging is not desired as per the example.
The logging methods also have a version that gets only two arguments: a String message and Throwable. In this case, the logging framework will take care of the output of the exception and the stack trace along with it. If you log something in exception handling code, log the exception and let the logger format it.