Java Logging – The Ultimate, Easy Guide

On first glance, logging looks like an exceedingly simple problem to solve. However, it is one of these problems which unfortunately become more and more complex the longer one looks at it.

I think because of this, there are many frameworks in Java to support logging (since everyone seems to have thought they have found a solution) with many of them being less than optimal, especially under load.

In effect, for someone who wants to start with logging in Java, there is an overwhelming, confusing and often contradictory wealth of resources available. In this guide, I will provide an introduction to Java logging in three simple steps: First, to choose the right framework. Second, to get your first log printed out onto the screen. And, third, to explore more advanced logging topics. So, without further ado, here the steps to get you started with Java logging:

Framework

The first question to sort out when considering logging for Java is to decide which logging framework to use. Unfortunately, there are quite a few to choose from.

The standard Java logging seems to be very unpopular. Further, it seems that Log4j and Logback both have architectural disadvantages to Log4j 2. In specific in respect to the performance impact which logging has on the host app. Loggly ran some tests on the different logging frameworks and the theoretical advantages of Log4j 2 also seem to be reflected in cold, hard data.

Thus, I think the prudent choice is to go with log4j2 in any but exceptional circumstances.

How To Get Started

The official documentation for Log4j 2 is not very approachable. Simply speaking, you only need to do two things to get ready for logging with Log4j 2.

The first is to add the following Maven dependency:

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.10.0</version>
</dependency>

The second is to create the file src/main/resources/log4j2.properties in your project with the following content:

status = error
name = PropertiesConfig

filters = threshold

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug

appenders = console

appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

rootLogger.level = debug
rootLogger.appenderRefs = stdout
rootLogger.appenderRef.stdout.ref = STDOUT

(Note, you may also provide the configuration in XML format. In that case, simply create file named log4j2.xml in src/main/resources)

Now you are ready to start logging!

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class OutputLog { 
  public static void main(String[] args) { 
    Logger logger = LogManager.getLogger(); 
    logger.error("Hi!"); 
  } 
}

Master Class

The real power of using a logging framework is realised by modifying the properties file created earlier.

You can, for instance, configure it to log into a file and rotate this log file automatically (so it doesn’t just keep on growing and growing). The following presents a properties file to enable this:


status = error
name = PropertiesConfig

property.filename = ./logs/log.txt

filters = threshold

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug

appenders = rolling

appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ${filename}
appender.rolling.filePattern = ./logs/log-backup-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=10MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 20

loggers = rolling

logger.rolling.name = file
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile

#rootLogger.level = debug
#rootLogger.appenderRefs = stdout
rootLogger.appenderRef.stdout.ref = RollingFile

This configuration will result in a log file being written into the logs/ folder. If the application is run multiple times, previous log files will be packed into gzipped files:

output

For even more sophisticated logging, you would want to set up a Graylog server and then send the logs there. This can be achieved using the logstash-gelf library. Add the following Maven dependency:

<dependency>
<groupId>biz.paluch.logging</groupId>
<artifactId>logstash-gelf</artifactId>
<version>1.11.1</version>
</dependency>

And then provide a log4j.xml configuration file like the following (replace yourserver.com with your Graylog server):


<Configuration>
<Appenders>
<Gelf name="gelf" host="udp:yourserver.com" port="51401" version="1.1" extractStackTrace="true"
filterStackTrace="true" mdcProfiling="true" includeFullMdc="true" maximumMessageSize="8192"
ignoreExceptions="true">
<Field name="timestamp" pattern="%d{dd MMM yyyy HH:mm:ss,SSS}" />
<Field name="level" pattern="%level" />
<Field name="simpleClassName" pattern="%C{1}" />
<Field name="className" pattern="%C" />
<Field name="server" pattern="%host" />
<Field name="server.fqdn" pattern="%host{fqdn}" />

<DynamicMdcFields regex="mdc.*" />
<DynamicMdcFields regex="(mdc|MDC)fields" />
</Gelf>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="gelf" />
</Root>
</Loggers>
</Configuration>

Then create a new GELF UDP input in Graylog (& don’t forget to open the firewall for udp port 51401) and you are ready to receive messages!

message

Finally, I personally find the logging frameworks with all their dependencies and insistence on configuration files exactly where they expected them a bit intrusive. Thus, I developed delight-simple-log – this very simply project can be used as a dependency in your reusable component; and then linked with Log4j 2 in the main package for an app. That way, the Log4j dependencies will only be present in one of your modules.

 

 

Tutorial: Upload from Java, Download from JavaScript

This tutorial.describes how a simple unit of data, the text ‘Hello, Java!’ can be uploaded from a Java application and retrieved with a JavaScript application. 

Upload from Java

This section lists all steps required to create a small application that store the text ‘Hello, World’ in the Appjangle cloud database.

Step 1: Download Client Libraries

First download the latest version of the Appjangle client libraries from this page.
Chose the ‘Appjangle Client with Nextweb API’. The Nextweb API is more declarative in nature than the more low-level onedb API and therefore allows in most cases to write more concise code.
If you use Maven in your project, you can also use the provided Maven dependencies to link the library to your project.

Step 2: Link Client Libraries

Create a new project in your favorite IDE. This example will make use of eclipse.
Copy the downloaded .jar file into a your project directory (e.g. ‘lib’) and add the library to the classpath/ build path.

Step 3: Write App to Upload Data

Create a new class with the name ‘Upload’. Add a Java main method to the class as follows:
import io.nextweb.Query;
import io.nextweb.Session;
import io.nextweb.jre.Nextweb;

public class Upload {

    public static void main(String[] args) {
        Session session = Nextweb.createSession();

        Query hello = session.seed().append("Hello, Java!");

        System.out.println("Created:\n"+hello.get());

        session.close().get();
    }

}
Running this application should result in an output such as the following:
Created:
node("http://slicnet.com/seed1/seed1/2/1/2/h/sd/Hello__1", 
  class java.lang.String)
You can now access the created node using the reported URI. For instance, by opening http://slicnet.com/seed1/seed1/2/1/2/h/sd/Hello__1 
in a web browser.

Download from JavaScript

This section lists all steps required to create a small application that store the text ‘Hello, World’ in the Appjangle cloud database using JavaScript.

Step 1: Download and Extract Appjangle Bootstrap

Head over to github to download the Appjangle Bootstrap project or, even better, fork it if you are familiar with github.
Extract the project and open app.html in your faverioute HTML/JS editor.

Step 2: Write App to Download Data

Replace the text // Your JavaScript here with the following application:
<body>
    <script
        src="http://appjangle.com/js/v01/appjangle/appjangle.nocache.js">
    </script>

    <script>
        window.onNextwebOnedb = function() {
            var session = Nextweb.createSession();
            var hello = session
                    .node("http://slicnet.com/seed1/seed1/2/1/2/h/sd/Hello__1");
            hello.get(function(node) {
                document.body
                        .appendChild(document.createTextNode(node.value()));
            });
        }
    </script>
</body>
Save app.html and close your editor.

Step 3: Upload App to Appjangle

Open the TextSync JAR file in the appjangle-bootstrap directory (for instance, by double clicking on the file if supported by your OS).
Drag and drop the file app.html onto the files list in the TextSync App and click [Synchronize].
Note: You will need to get an Appjangle account in order to use the TextSync App. All your uploaded applications will be stored under this account.

Step 4: Open Application

Open app.html again. In the beginning of the file, copy the URL to the right of one.upload .
Past the URL into your web browser. Add to the end of the URL .value.html and replace https with http at the beginning of the URL.
Loading the page should result in the output (also see an example deployed app here):

Hello, Java

It’s not ‘Hello, JavaScript’ since here we are loading the node created by the Java Hello, World application described above. Check out the Nextweb.io API docs to find out how to change the text stored in the Appjangle cloud to a more fitting ‘Hello, JavaScript’.
This tutorial is also published on the Appjangle blog.

Java Get Process Id: Three Approaches

Java provides no robust way to obtain the id of the OS process in which the application is running.

That does not mean, however, that there is no way. There are actually a couple of ways, each with its own advantages and disadvantages. I will describe three possible ways in the following:

1. ManagementFactory.getRuntimeMXBean().getName()

The class ManagementFactory in the package java.lang.management provides access to the “managed bean for the runtime system of the Java virtual machine”. The getName() method of this class is described as:

Returns the name representing the running Java virtual machine.

This name, as it happens, contains the process id in the Sun/Oracle JVM implementation of this methods in a format such as.


System.out.println(ManagementFactory.getRuntimeMXBean().getName());

// --> 742912@localhost

Through applying a simple String split, this thus allows to obtain the pid of the current process through the following expression:


ManagementFactory.getRuntimeMXBean().getName().split("@")[0]

Advantages:

  • Quick and dirty

Disadvantages:

  • Not guaranteed to work on all JVM implementations

2. Use Java Native Interface

At least on most UNIX based systems, it is relatively easy to write a quick JNA wrapper to the C library.


// Alternative 1: interface-mapped class, dynamically load the C library
public interface CLibrary extends Library {
 CLibrary INSTANCE = (CLibrary)Native.loadLibrary("c", CLibrary.class);
}

You can then add a mapping to the getpid function:


public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)Native.loadLibrary("c", CLibrary.class);

int getpid ();

}

And call this mapper as follows:


int mypid = CLibrary.INSTANCE.getpid();

Advantages:

  • Guaranteed to work as long as C library is available
  • Depends on JNA under com.sun. ….

Disadvantages:

  • Only works on UNIX based systems

3. Use the Java Virtual Machine Process Status Tool (jps)

If your JVM comes with the utility Java Virtual Machine Process Status Tool you can call this tool by spawning a new process, and analyse its output to obtain the id of your Java process.

Since there are a million ways to get starting a process from Java and reading its output wrong, I will use the utility java-start-process for this:


public static void getProcessId(final Class<?> mainClass,
 final Callback<String> callback) {

Spawn.startProcess("jps -l", null, new ProcessListener() {

@Override
 public void onProcessQuit(final int returnValue) {

}

@Override
 public void onOutputLine(final String line) {
 final String[] parts = line.split(" ");
 if (parts.length > 1 && parts[1].endsWith(mainClass.getName())) {
 callback.onDone(parts[0]);
 }
 }

@Override
 public void onErrorLine(final String line) {

}

@Override
 public void onError(final Throwable t) {

}
 });

}

Advantages:

  • Works on both Windows and Linux when Oracle/Sun Java is installed and on classpath

Disadvantages:

  • Complex
  • Requires Sun JVM

Resources

stackoverflow – How can a Java program get it’s own process ID?

stackoverflow – How do I obtain the PID of a spawned java process