Configuring Logging in the PubSub+ Java API

The PubSub+ Java API uses Jakarta Commons Logging (JCL), also known as Apache Commons Logging, to support different logging frameworks. PubSub+ Java API applications can choose either log4j2 or java.util.logging as their logging framework.

Supported Log Levels

This section shows the different levels of logging severity that the logging framework recognizes and allows you to use when generating log messages. Each log level indicates a different degree of importance or severity for the log messages. When you use different log levels, you can filter and prioritize log messages based on their importance, which helps you efficiently monitor and troubleshoot your applications.

To turn on logging of Kerberos events, set the Java system property to the following: -Dsun.security.krb5.debug=true.

Supported Log Levels for Java

Level Description

Fatal

These log levels are reserved for internal errors and should be a cause for investigation. Contact Solace customer support for log events at this level.

Error

Warn

Indicates an application error (for example, invalid parameters passed in or unsupported use of the APIs).

Info

Typically used for state changes at a high level (for example, connect/disconnect/reconnect).

It is also used for unusual events that do not indicate any error, but they are unexpected and might need investigation.

Debug

This log level is voluminous and typically requires Solace customer support to interpret.

Trace

Not used.

When a log level is set at a given level, all log events with log levels less severe than the selected level are filtered out, all log levels equally or more severe are included. For example, if the Error log filter level is selected, only Error and Fatal events are included.

Java designers should be familiar with such logging frameworks because they are an established standard for Java-based systems. However, you should consider the following points:

  • When using log4j, the directory in which the log4j.properties file and log4j.jar file exist is on the classpath.
  • When using log4j, including the log4j.jar library file without log4j.properties in the classpath negatively impacts performance.

To see an example of logging in the PubSub+ Java API, see GuaranteedSubscriber.java on the Solace Developer's Hub.

Logging Example in the PubSub+ Java API

The following example shows how to use log4j in your application:

  1. Add log4j as a dependency in your application:
    • Maven example:
      <dependencies> 
          <dependency>
              <groupId>com.solace</groupId>
              <artifactId>solace-messaging-client</artifactId>
              <version>1.7.0</version>
          </dependency>
          <dependency>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j-api</artifactId>
              <version>2.17.2</version>
          </dependency>
          <dependency>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j-core</artifactId>
              <version>2.17.2</version>
          </dependency>
          <dependency>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j-jcl</artifactId>
              <version>2.17.2</version>
          </dependency>
      </dependencies>
      
    • Gradle example:
      dependencies {
          implementation group: 'com.solace', name: 'solace-messaging-client', version: '1.+'
      	...
          implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.+'
          implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.+'
          implementation group: 'org.apache.logging.log4j', name: 'log4j-jcl', version: '2.+'
      }
      
  2. In the src/dist/config directory, configure your log4j2.xml file. The following example sets up console logging for specific packages and classes in a PubSub+ Java API application, with a consistent log message format and info, debug, and warn level log messages:
    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn" monitorInterval="30">
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="--LOG-- %d{HH:mm:ss.SSS} [%t] %-5level %logger{5.} - %msg%n"/>
            </Console>
        </Appenders>
        <Loggers>
            <Logger name="com.solacesystems" level="info" additivity="false">
                <AppenderRef ref="Console"/>
            </Logger>
            <Logger name="com.solace.samples" level="debug" additivity="false">
                <AppenderRef ref="Console"/>
            </Logger>
            <Logger name="com.solace.samples.GuaranteedPublisher" level="warn" additivity="false">
                <AppenderRef ref="Console"/>
            </Logger>
            <Root level="info">
                <AppenderRef ref="Console"/>
            </Root>
        </Loggers>
    </Configuration>
    
  3. Import the LogManager class and Logger interface from the Apache Log4j library to enable logging functionality in your application. Next, create a static logger instance enable logging capabilities:
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    // ...
    // ...
    private static final Logger logger = LogManager.getLogger();			
  4. Add logging to your program to provide detailed logging of key events, such as service interruptions and reconnection attempts. The example below shows how to log a warning message when a service interruption occurs, including the cause of the interruption, an informational message when a reconnection attempt is made, and an informational message when the service successfully reconnects:
    final MessagingService messagingService = MessagingService.builder(ConfigurationProfile.V1)
        .fromProperties(properties)
        .build();
    messagingService.connect();
    messagingService.addServiceInterruptionListener(serviceEvent -> {
        logger.warn("### SERVICE INTERRUPTION: " + serviceEvent.getCause());
    });
    messagingService.addReconnectionAttemptListener(serviceEvent -> {
        logger.info("### RECONNECTING ATTEMPT: " + serviceEvent);
    });
    messagingService.addReconnectionListener(serviceEvent -> {
        logger.info("### RECONNECTED: " + serviceEvent);
    });	
  5. The following console output shows example log messages for a successful reconnection attempt after a service interruption:
    --LOG-- 13:58:20.720 [Context_1_Thread_reconnect_service] INFO  com.solac.sampl.java.patte.GuaranteedPublisher - ### RECONNECTING ATTEMPT: ServiceEventImpl{timestamp=1721843900720, brokerURI='...', message='null', cause=com.solace.messaging.PubSubPlusClientException: com.solacesystems.jcsmp.JCSMPTransportException: Channel is closed by peer} peer}
    --LOG-- 13:58:20.720 [Context_1_Thread_reconnect_service] INFO  com.solac.jcsmp.proto.impl.TcpClientChannel - Client-1: Connecting to host 'orig=..., host=...' (host 1 of 1, smfclient 1, attempt 3 of 20, this_host_attempt: 4 of 6)
    --LOG-- 13:58:20.737 [Context_1_Thread_reconnect_service] INFO  com.solac.jcsmp.proto.impl.TcpClientChannel - Client-1: Connected to host 'orig=..., host=...' (smfclient 1) local(/...) remote(.../...)
    --LOG-- 13:58:20.754 [Context_1_Thread_reconnect_notify_service] INFO  com.solac.sampl.java.patte.GuaranteedPublisher - ### RECONNECTED: ServiceEventImpl{timestamp=1721843900754, brokerURI='...', message='null', cause=null}