一个简单的工具类 日志合并成块打印

场景:日志需要跨方法甚至跨类打印,又想让一个线程的打印可以按顺序成块打印

例:如HttpUtil工具类发起工具类:

public String postForm(String url,HttpForm param){
    log.info("请求开始--");
    log.info("url:[{}]",url);
    log.info("param:[{}]",param);
    post(url,param.toString());
}

public String postJson(String url,String param){
    log.info("请求开始--");
    log.info("url:[{}]",url);
    log.info("param:[{}]",param);
    post(url,param.toString());
}

/**
* param 比如用HttpClient请求, 不管是表单提交还是form又或者文件最后都是要转成HttpEntity,所以可以
* 实际请求发出和返回封装一个方法,表单请求和json 另外用一个方法构造
*/
public String post(String url,String param){
    String result = httpclient.post(url,param.toString());
    log.info("接口返回结果:[{}]", result);
    log.info("请求结束--");
}

如果按照以上代码,多线程里每次请求的日志不会按顺序打印出来,会和其他请求的日志夹杂在一起,效果如下

请求开始--
url:[http:123.com]
请求开始--
param:[name=张三]
url:[http:321.com]
接口返回结果:[{"message":"你"}]
param:[{"task":"回家"}]
请求结束--
........

这样看着不太友好.

优化:(LogBuilder工具类末尾提供)

private LogBuilder logBuilder = new LogBuilder();
public String postForm(String url,HttpForm param){

    logBuilder.appendLog("请求开始--");
    logBuilder.appendLog("url:[{}]",url);
    logBuilder.appendLog("param:[{}]",param);
    post(url,param.toString());
}

public String postJson(String url,String param){
    logBuilder.appendLog("请求开始--");
    logBuilder.appendLog("url:[{}]",url);
    logBuilder.appendLog("param:[{}]",param);
    post(url,param.toString());
}

/**
* param 比如用HttpClient请求, 不管是表单提交还是form又或者文件最后都是要转成HttpEntity,所以可以
* 实际请求发出和返回封装一个方法,表单请求和json 另外用一个方法构造
*/
public String post(String url,String param){
    String result = httpclient.post(url,param.toString());
    logBuilder.appendLog("接口返回结果:[{}]", result);
    logBuilder.appendLog("请求结束--");

    //这种方法需要自己传入log类 ,我用的是slf4j 的log, 且需要实现设置日志等级
    logBuilder..setLogLevel(Level.INFO);
    logBuilder.log(log);

    //这种方法也需要自己传入log类 ,我用的是slf4j 的log, 不需要实现设置日志等级
    logBuilder.logInfo(log);

    //这种方法通过回调的方式,不需要传入log类,不会限制log类的框架
    logBuilder.log(logStr -> {
        log.info(logStr);
    });;
    
}

如果按以上方法效果如下:

请求开始--
url:[http:123.com]
param:[name=张三]
接口返回结果:[{"message":"失败"}]
请求结束--

请求开始--
url:[http:321.com]
param:[{"task":"回家"}]
接口返回结果:[{"message":"成功"}]
请求结束--

每次请求成块打印,看着友好多了。跨类的话可以定义成static,或者放入static map中。

工具类代码:

/**
 * 日志构建工具
 * 当前线程内不同地方的日志合并打印
 *
 */
public class LogBuilder {
    private ThreadLocal<StringBuilder> logBuilderThreadLocal = new ThreadLocal<>();

    private Level logLevel;

    /**
     * 添加日志内容
     *
     * @param format 添加内容 可以包含 {} 占位符
     * @param params 替换 {} 占位符的参数
     */
    public LogBuilder appendLog(String format, String... params) {
        StringBuilder builder = logBuilderThreadLocal.get();
        if (builder == null) {
            builder = new StringBuilder();
            logBuilderThreadLocal.set(builder);
        }
        builder.append("\r\n");
        String str = format(format, params);
        builder.append(str);
        return this;
    }

    /**
     * 把当前添加的日志内容全打印出来
     *
     * @param log sl4j logger
     */
    public void logTrace(Logger log) {
        log(logStr -> {
            log.trace(logStr);
        });
    }

    /**
     * 把当前添加的日志内容全打印出来
     *
     * @param log sl4j logger
     */
    public void logDebug(Logger log) {
        log(logStr -> {
            log.debug(logStr);
        });
    }

    /**
     * 把当前添加的日志内容全打印出来
     *
     * @param log sl4j logger
     */
    public void logInfo(Logger log) {
        log(logStr -> {
            log.info(logStr);
        });
    }

    /**
     * 把当前添加的日志内容全打印出来
     *
     * @param log sl4j logger
     */
    public void logWarn(Logger log) {
        log(logStr -> {
            log.warn(logStr);
        });
    }

    /**
     * 把当前添加的日志内容全打印出来
     *
     * @param log sl4j logger
     */
    public void logError(Logger log) {
        log(logStr -> {
            log.error(logStr);
        });
    }

    /**
     * 把当前添加的日志内容全打印出来
     * 调用此方法得先调用setLogLevel 设置日志打印级别
     *
     * @param log sl4j logger
     */
    public void log(Logger log) {
        if (logLevel == null) {
            throw new CustomException("请先调用setLogLevel方法设置日志打印级别");
        }
        switch (logLevel) {
            case TRACE:
                log(logStr -> {
                    log.trace(logStr);
                });
                break;
            case DEBUG:
                log(logStr -> {
                    log.debug(logStr);
                });
                break;
            case INFO:
                log(logStr -> {
                    log.info(logStr);
                });
                break;
            case WARN:
                log(logStr -> {
                    log.warn(logStr);
                });
                break;
            case ERROR:
                log(logStr -> {
                    log.error(logStr);
                });
                break;
            default:
                throw new CustomException("找不到对应的日志打印级别");
        }
        logLevel = null;
    }

    /**
     * 设置日志打印等级
     *
     * @param logLevel sl4j的 level
     */
    public void setLogLevel(Level logLevel) {
        this.logLevel = logLevel;
    }

    /**
     * 当前添加的日志内容全打印出来
     *
     * @consumer 自己实现打印代码的回调 日志内容作为参数传入consumer方法
     */
    public void log(Consumer<String> consumer) {
        StringBuilder builder = logBuilderThreadLocal.get();
        if (builder != null) {
            consumer.accept(builder.toString());
            clear();
        }
    }

    /**
     * 日志打印完需要 清除当前日志内容
     */
    private void clear() {
        logBuilderThreadLocal.remove();
    }

    /* -----------------  下面代码是对字段串中的{}占位符 拼接的代码,可以提取到工具类,也可以自己用其他方法实现  ----------*/
    public static final String EMPTY_JSON = "{}";
    public static final char C_BACKSLASH = '\\';
    public static final char C_DELIM_START = '{';
    public static final char C_DELIM_END = '}';

    public String format(final String strPattern, final Object... argArray) {
        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) {
            return strPattern;
        }
        final int strPatternLength = strPattern.length();

        // 初始化定义好的长度以获得更好的性能
        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);

        int handledPosition = 0;
        int delimIndex;// 占位符所在位置
        for (int argIndex = 0; argIndex < argArray.length; argIndex++) {
            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
            if (delimIndex == -1) {
                if (handledPosition == 0) {
                    return strPattern;
                } else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
                    sbuf.append(strPattern, handledPosition, strPatternLength);
                    return sbuf.toString();
                }
            } else {
                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {
                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {
                        // 转义符之前还有一个转义符,占位符依旧有效
                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
                        sbuf.append(Convert.utf8Str(argArray[argIndex]));
                        handledPosition = delimIndex + 2;
                    } else {
                        // 占位符被转义
                        argIndex--;
                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
                        sbuf.append(C_DELIM_START);
                        handledPosition = delimIndex + 1;
                    }
                } else {
                    // 正常占位符
                    sbuf.append(strPattern, handledPosition, delimIndex);
                    sbuf.append(Convert.utf8Str(argArray[argIndex]));
                    handledPosition = delimIndex + 2;
                }
            }
        }
        // 加入最后一个占位符后所有的字符
        sbuf.append(strPattern, handledPosition, strPattern.length());

        return sbuf.toString();
    }
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值