How to write a uniform log without configuration format

  java

Background

A large number of projects use logback to record logs. Some projects use logbacks in confusion and in different formats. Most people do not understand the configuration files, resulting in configuration errors. Now it is necessary to develop a set of unified and less configured log components, which is convenient to use.

Design ideas

Try to use 0 configuration instead of logback.xml

The log format is unified to facilitate the subsequent log analysis system.

There are only two log levels, one is normal log and the other is exception log.

Provide bridging schemes and version compatibility schemes such as log4j, jcl, logback, commons-log, etc.

Convenient log output methods such as lifting thread, json print format, map format, array format, and request response parameters (for time consumption)

Redis, db, http auto-switch configuration is supported.**

Add logger

Api adopts streaming structure, similar to StringBuffer.

Outline design

Zero configuration

Research code

java
static LoggerContext lc;
    static {
        lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        // 对应配置中的appender
        ConsoleAppender ca = new ConsoleAppender();
        ca.setContext(lc);
        ca.setName("console");
        // 格式
        PatternLayoutEncoder pl = new PatternLayoutEncoder();
        pl.setContext(lc);
        pl.setPattern("%d{MMddHHmmss.SSS} [%thread] %-5level %logger{36} - %msg%n");
        pl.start();
        ca.setEncoder(pl);
        ca.start();
        // 对应配置中的logger
        ch.qos.logback.classic.Logger rootLogger = lc.getLogger("com.test");
        rootLogger.addAppender(ca);}

The above code is equivalent to the following xml

%d{MMddHHmmss.SSS} [%thread] %-5level %logger{36} - %msg%n

Therefore, the contents in the configuration file can be written in code form at will, and the theory has already realized 0 configuration.

Output path

The contract fixes the output of logs to the relative path log/xxx.yyyy-MM-dd-HH.log, where xxx is the name of logger

Log format

Fixed format:
MMddHHmmss.SSS||id|| [transaction name ★ substep] ||context ||level
For example:
150000.311 | | n-xrutqzic 1531897200311 | | [citeefilter ★ ci interceptor] | | the full parameters requested by the ci interceptor are: {“merchant id”: [“0012444”], “userid”: [“13112341232”]} | | info
Fixed-format core code intercepts log requests and assembles them according to the format. The main method is to inherit ThrowableProxyConverter and MessageConverter to intercept logs and modify them to the desired format. For example, id is used in local variables, and MDC is the core.

Basic logger

All logs are output here by default. LoggerName: When the service system is initialized, this Logger and appender are defined, that is, this Logger is root log.

Custom logger

Provide the addLogger method, the parameter packageName package name, for example: com.testrequired parameter if name is not set, name defaults to the last of the package name. the following character name determines whether the name of the log file is required to enter pathlog path or additivity is required to enter the root log

Special log
Log configuration for special components is provided, for example: redis Default ERROR http Default ERROR db Connection Pool Default ERROR kafka Default ERRORSchedule Default ERROR spring Default Error

Exception and line feed log processing

Provide exception exception stack format printing and provide format printing code with line feed: inherit ThrowableProxyConverter, obtain exception stack, insert fixed format text in front of each line

Virgolog

Method Method description
setUniqKey(id) Set the current thread id, which can be set at the beginning of the thread, but need not be set later.
updateStep(trade, step) Update step information for current id
log(msg, param) Record normal log, msg replacement rule, normal replacement is {}, if you want to replace it with the format in the business log api, use “replace
logErr(msg, e) Record exception log
log( trade, step, msg, param) Record the ordinary log. This method will automatically update id, trade and step. It is not recommended.
logErr(trade, step, msg, e) Record exception log
log(cid, trade, step, msg, param) Record the ordinary log. This method will automatically update id, trade and step. It is not recommended.
logErr(cid, trade, step, msg, e) Record exception log
debug(msg, param) Record debug level log, not recommended.

Business log api(VirgoLog)

When logging at ordinary times, if a class does not have the time toString method, it will not be able to print the data correctly. at this time, a replacement method is provided to directly replace object with json printing. the core code idea is

MessageFormatter is a class that handles {} substitution. write a new class and support {} as well as `’ with a slight change. judge whether to replace it with json or toString api as follows

Method Method description
begin(msg) Record start
end(msg) When the recording is completed, the elapsed time from the previous begin in this thread to now will be printed.
logJson(json, format) Record json format log, format indicates whether to wrap or not
logMap(map, format) Record map format log
logCollection(list, format) Record Set Format Log
logArray(array, format) Record array format log
logObjct(obj, format) Record Object format log

Loggerhelper

Method Method description
getLogger() Get logger for logging
getLogger(name) Get logger via name
addLogger() Referring to the custom Logger, if the logger has already been created, it will not be created and will not be used unless you want to customize the log name, etc.
consoleOpen() Open the console log and configure the console log by default when the system starts up
commonOpen(name, level) The default component is the error level, this method can change the log level, such as redis http, etc.

Special formatting

Map: It is converted to json and then formatted
Collection: same as above
Array: same as above
Object: same as above

Problem

1, password desensitization, encryption and decryption is it necessary to separate extraction method

2, provide the parent thread print switch

Maven dependency

        <dependency>
            <groupId>com.cdc.ecliptic</groupId>
            <artifactId>virgo</artifactId>
            <version>1.5_1.6-SNAPSHOT</version>
        </dependency>

demo

public static void main(String[] args) throws InterruptedException {

        // 启动
        VirgoLancher.start("hahaha", "com.cdc.virgo", "D:/test/hahah.log");
        LoggerHelper.commonOpen("hahaha", LogLevel.DEBUG);
        Logger logger1 = LoggerFactory.getLogger("druid");
//        VirgoLancher.commonStart("abc", "com.cdc.virgo");
        // 打开控制台
        LoggerHelper.consoleOpen();
        // 设置cid
        VirgoLog.setUniqKey(null);
        // 设置步骤名和交易名
        VirgoLog.updateStep("adfa", "saf");
        // 获取Logger
        VirgoLog logger = VirgoLog.getLogger();

        // 打开debug级别(只有在开发阶段可以打开)
//        logger.changeLevel(LogLevel.DEBUG);
        // 记录换行
        logger.log("a");

        logger1.info("dddddddddd");
        logger1.error("dddddddddd");

//        logger1.info("sfdasfaf" +
//                "\nafafdasfd" +
//                "\nasfdasf");
        logger.log("sfdasfaf" +
                "\nafafdasfd" +
                "\nasfdasf");

//        logger1.info("b");
        // 正常日志
//        logger.log("我只有一行");
        Map<String, String> map = new HashMap();
        map.put("asdf", "1");
        map.put("asdf2", "2");
        map.put("asdf3", "13");
        map.put("asdf4", "14");
        map.put("asdf5", "15");
        map.put("asdf6", "16");
//        // 异常日志也支持格式化
//        logger.logErr("我错了:{},你没错:~~", new Exception("asdfsaflk"), "啊", map);
//        logger.log("----------------------------------------------");
//        // {}替换普通对象,调用toString()  ~~把对象转换为json并且格式化输出 ``把对象转换为json不格式化输出
        logger.log("你好{},你是谁~~``,sd~xx {}", map, map, map, "tttt");
        VirgoLog.updateStep("saf2");
//        // 把对象转换为json输出
//        logger.logJson(map, false);
//        // 更新步骤名和交易名
//        VirgoLog.updateStep("bbbbb", "ccccc");
//        // 耗时日志打印
        logger.begin("处理内容");
        logger.begin("处理第二个");
        logger.begin("处理第三个");
        Thread.sleep(3000L);
        logger.end();
        Thread.sleep(1000L);
        logger.end();
        VirgoLog.updateStep("saf3");
        logger.end();
//        // 记录debug日志,一般调试用
//        logger.logDebug("jajajajaja");

//        List l = new ArrayList();
//        B b = new B();
//        try {
//            b.b();
//        } catch (Exception e) {
//            logger.logErr("woqu", e);
//        }
    }

Yixin Institute of Technology