sofa-common-tools

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.

Background

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. 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 to have its own log space identifiable via SpaceID in sofa-common-tools.

Some notes:

Quick Start

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

import org.slf4j.Logger;
import com.alipay.sofa.common.log.LoggerSpaceManager;

public class AlipayOcrLoggerFactory {
    private static final String OCR_LOGGER_SPACE = "com.alipay.sdk.ocr";

    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 com.alipay.sdk.ocr 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"?>
<configuration>
    <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>
        </encoder>
    </appender>

    <logger name="com.alipay.foo" level="INFO" additivity="false">
        <appender-ref ref="stdout"/>
    </logger>

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

Lastly, just logging

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

In console, the following log will be printed:

17:42:41.083 [main] INFO com.alipay.foo - hello world

Configuration

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 com.alipay.sofa.common.log.LoggerSpaceManager;import java.util.HashMap;

public class AlipayOcrLoggerFactory {
    private static final String OCR_LOGGER_SPACE = "com.alipay.sdk.ocr";

    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());
    }
}

Debugging

  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

Miscellaneous

LogLog

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

Console logging

Logging pattern

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

Compiling

Maven 3.2.5+, JDK Version 1.6+

LICENSE

Apache 2.0

Contribution

Contribution Guide