Awesome Open Source
Awesome Open Source


build Coverage Status License maven

sofa-common-tools is a common dependency of SOFAStack middleware, it provides:

  1. Separate log space for application and middleware
  2. SOFA thread

The audience of this library is middleware and SDK developer.

Note: Since version 1.2.0, sofa-common-tools don't support JDK 1.6 anymore.


In daily developing, Java logging usually consists of choosing a log facade (e.g., JCL and SLF4j) and log implementation (e.g., Log4j2 and logback). Say you are developing an application that uses a JAR which utilizes log4j2 for logging. In such scenario, you cannot choose log implementation other than log4j2 (log implementation conflicts if you choose Logback). Some solutions available:

  1. The jar uses log facade instead log implementation but application developers still have to provide log configuration
  2. The jar initialize loggers and appenders programmatically (This works well in Multi-ClassLoader environment where middleware/SDK developers handle many repeated work)
  3. Application resort to same log implementation as the JAR and provide also log configuration

None of the above solutions is perfect, sofa-common-tools provides a Midas touch: middleware/SDK developers print logs using only facade and hand the right to select whichever log implementation to application developer. At the mean time, middleware/SDK developers provide log configurations per log implementation. sofa-common-tools detects automatically the log implementation and initializes appenders and loggers for middleware/SDK. To differentiate SDKs/middlewares, each jar has its own log context and log space identifiable via SpaceID in sofa-common-tools.

Some notes:

  • sofa-common-tools only supports SLF4j facade currently

Quick Start

Say you are developing an OCR SDK for downstream to integrate. First, you choose as your log space. Second, define a logger factory to retrieve all the loggers you need:

import org.slf4j.Logger;

public class AlipayOcrLoggerFactory {
    private static final String OCR_LOGGER_SPACE = "";

    public static Logger getLogger(String name) {
        if (name == null || name.isEmpty()) {
            return null;

        return LoggerSpaceManager.getLoggerBySpace(name, OCR_LOGGER_SPACE);

    public static Logger getLogger(Class<?> klass) {
        if (klass == null) {
            return null;

        return getLogger(klass.getCanonicalName());

Third, create log configuration for your log space in classpath (space name maps to com/alipay/sdk/ocr/log/ ), for example

$ cd com/alipay/sdk/ocr/log && tree
├── log4j
│   └── log-conf.xml
├── log4j2
│   └── log-conf.xml
└── logback
    └── log-conf.xml

The directory name is quite self-evident. If application choose a log implementation you don't configure, error will be thrown.

A sample configuration for logback logback/log-conf.xml:

<?xml version="1.0" encoding="UTF-8"?>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder charset="UTF-8">
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

    <logger name="" level="INFO" additivity="false">
        <appender-ref ref="stdout"/>

    <root level="INFO">
        <appender-ref ref="stdout"/>

Lastly, just logging

public class Main {
    public static void main(String[] args) {
        Logger ocrLogger = AlipayOcrLoggerFactory.getLogger("");"hello world");

In console, the following log will be printed:

17:42:41.083 [main] INFO - hello world


The configuration of corresponding logging implementation can be parameterized, that is to say, placeholders are allowed in XML file. By default, sofa-common-tools provides following parameters with sensible default values:

Parameter Default value
logging.path ${user.home}
file.encoding UTF-8
logging.level.{spaceName} INFO
logging.path.{spaceName} ${logging.path}

Application is able to override the value through JVM options, e.g., -Dlogging.path=/home/admin.

Customized Parameter

Middlewares/SDKs can defined customized parameters for xml placeholders as well. Those parameters must be initialized before using:

import org.slf4j.Logger;
import;import java.util.HashMap;

public class AlipayOcrLoggerFactory {
    private static final String OCR_LOGGER_SPACE = "";

    static {
        // Note: this step is important, as in Ark environment your SDK may be used in module dependency
        // and will be initialized multiple times.
        if (!MultiAppLoggerSpaceManager.isSpaceInitialized(OCR_LOGGER_SPACE)) {
            Map spaceIdProperties = new HashMap<String, String>();
            // Initialize your parameters here
            MultiAppLoggerSpaceManager.init(OCR_LOGGER_SPACE, spaceIdProperties);

    public static Logger getLogger(String name) {
        if (name == null || name.isEmpty()) {
            return null;

        return LoggerSpaceManager.getLoggerBySpace(name, OCR_LOGGER_SPACE);

    public static Logger getLogger(Class<?> klass) {
        if (klass == null) {
            return null;

        return getLogger(klass.getCanonicalName());


  1. The logging ability can be disabled totally through sofa.middleware.log.disable JVM option (Of course for middleware/SDK jar using sofa-common-tools).
  2. Debugging with specific log implementation, lock-down of other log implementation, e.g., -Dlogback.middleware.log.disable=true disables logback. All supported switch:
    • log4j.middleware.log.disable
    • log4j2.middleware.log.disable
    • logback.middleware.log.disable


  • sofa.middleware.log.disable, defaults to false
  • logback.middleware.log.disable, defaults to false
  • log4j2.middleware.log.disable, defaults to false
  • log4j.middleware.log.disable, defaults to false


sofa-common-tools uses internally System.out for logging, logging level can be set via JVM option sofa.middleware.log.internal.level.

Console logging

  • Global configuration
    • Switch sofa.middleware.log.console toggles console logging for all middleware/SDK, defaults to false
    • sofa.middleware.log.console.level configures log level globally
  • Independent middleware/SDK configuration
    • Switch sofa.middleware.log.${spaceid}.console toggles console logging for corresponding middleware/SDK, defaults to false
    • sofa.middleware.log.{space id}.console.level configures log level correspondingly, which overrides global log level
Logging pattern
  • logback: sofa.middleware.log.console.logback.pattern defaults to %d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } --- [%15.15t] %-40.40logger{39} : %m%n
  • log4j2: sofa.middleware.log.console.log4j2.pattern defaults to %d{yyyy-MM-dd HH:mm:ss.SSS} %5p %X{PID} --- [%15.15t] %-40.40logger{39} : %m%n

Console logging options can be passed through JVM option or Spring Boot properties.


Maven 3.2.5+, JDK Version 1.6+


Apache 2.0


Contribution Guide

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
java (30,382
logback (27
slf4j (17
sofastack (17
log4j2 (15

Find Open Source By Browsing 7,000 Topics Across 59 Categories