进阶式-日志打印-构建器模式

项目内日志打印,从第一版修改到第四版。每次修改都是进步。

第一版:直接打印

一般日志打印方法,更直接的会直接在这里打印字符串。这种方式打印的日志在编码过程中调试方便,便于理解。

但是对于已上线的项目,就比较麻烦了。日志太多,且杂乱。

此示例打印时json格式日志。第一个参数输入日志类型,后面参数都是 Key ,Value , Key , Value ..... 模式输入数据。

json格式日志便于日志收集和日志的统计分析。

log.info("JSON", "productKey", "ProductKey", "deviceName", "DeviceName", "businessType", "OTA"); 

第二版:将这些需要打印的参数抽出来封装为一个对象打印

这种方式打印日志,每次在需要打印日志地方调用对应的日志方法即可。统一了日志打印参数。

package com.log;

import com.alibaba.fastjson.JSONObject;
import lombok.Data;

/**
 * @version 1.0
 * @description
 * @date 2020/11/12 14:24
 */
@Data
public class ServiceLog<T> {
    private String businessType;
    private String messageId;
    private String productKey;
    private String deviceName;
    private String operation;
    private T data;
    private int status;

    private ServiceLog(String businessType){
        this.messageId = "";
        this.productKey = "";
        this.deviceName = "";
        this.businessType = businessType;
        this.operation = "";
        this.data = (T)new JSONObject();
        this.status = 200;
    }

    private ServiceLog(String productKey, String deviceName, String businessType){
        this.messageId = "";
        this.productKey = productKey;
        this.deviceName = deviceName;
        this.businessType = businessType;
        this.operation = "";
        this.data = (T)new JSONObject();
        this.status = 200;
    }

    private ServiceLog(String messageId, String productKey, String deviceName, String businessType, String operation){
        this.messageId = messageId;
        this.productKey = productKey;
        this.deviceName = deviceName;
        this.businessType = businessType;
        this.operation = operation;
        this.data = (T)new JSONObject();
        this.status = 200;
    }

    private ServiceLog(String messageId, String productKey, String deviceName, String businessType, String operation, T data, int status){
        this.messageId = messageId;
        this.productKey = productKey;
        this.deviceName = deviceName;
        this.businessType = businessType;
        this.operation = operation;
        this.data = data;
        this.status = status;
    }
}

第二版优化:将对象中多个重复代码合并。

合并第二版中重复代码,将方法合并为this调用。

package com.log;

import com.alibaba.fastjson.JSONObject;
import lombok.Data;

/*
 * @version 1.0
 * @description
 * @date 2020/11/12 14:24
 */
@Data
public class ServiceLog<T> {
    private String businessType;
    private String messageId;
    private String productKey;
    private String deviceName;
    private String operation;
    private T data;
    private int status;

    public ServiceLog(String businessType){
        this("","",businessType);
    }

    public ServiceLog(String productKey, String deviceName, String businessType){
        this("", productKey, deviceName, businessType,"");
    }

    public ServiceLog(String messageId, String productKey, String deviceName, String businessType, String operation){
        this(messageId, productKey, deviceName, businessType,operation, (T)new JSONObject(), 200);
    }

    public ServiceLog(String messageId, String productKey, String deviceName, String businessType, String operation, T data, int status){
        this.messageId = messageId;
        this.productKey = productKey;
        this.deviceName = deviceName;
        this.businessType = businessType;
        this.operation = operation;
        this.data = data;
        this.status = status;
    }
}

第三版:构建器(build)模式

构建器模式打印日志,此时将日志类型抽象为枚举(BusinessTypeEnum)。

log.info 语句也放在了ServiceLog对象里,但是此时 log是从外部输入的,打印日志也相当于在原调用对象中打印。

package com.log;

import com.log.enums.BusinessTypeEnum;
import lombok.Data;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Map;

/**
 * @version 1.0
 * @description
 * @date 2020/11/12 14:24
 */
@Data
public class ServiceLog {
    private BusinessTypeEnum businessType;

    private String messageId;
    private String productKey;
    private String deviceName;
    private String operation;
    private Map data;
    private int status;

    private ServiceLog(Builder builder){
        this.messageId = builder.messageId;
        this.productKey = builder.productKey;
        this.deviceName = builder.deviceName;
        this.businessType = builder.businessType;
        this.operation = builder.operation;
        this.data = builder.data;
        this.status = builder.status;
    }

    public static class Builder {
        private BusinessTypeEnum businessType;
        private String messageId;
        private String productKey;
        private String deviceName;
        private String operation;
        private Map data;
        private int status;

        public Builder(BusinessTypeEnum businessType){
            this.businessType = businessType;
            this.data = new HashMap();
        }

        public Builder mid(String messageId){
            this.messageId = messageId;
            return this;
        }

        public Builder pk(String productKey){
            this.productKey = productKey;
            return this;
        }

        public Builder dn(String deviceName){
            this.deviceName = deviceName;
            return this;
        }

        public Builder opt(String operation){
            this.operation = operation;
            return this;
        }

        public Builder params(Object params){
            this.data.put("params", params);
            return this;
        }

        public Builder msg(String message){
            this.data.put("message", message);
            return this;
        }

        public Builder status(int status){
            this.status = status;
            return this;
        }

        public ServiceLog build(Logger log){
            ServiceLog serviceLog = new ServiceLog(this);
            log.info("JSON_OBJECT", serviceLog);
            return serviceLog;
        }
    }
}

第四版:构建器模式优化版,打印日志

1、用 @getter,省略了内部参数的get方法。由于此类没有set,故不用 @Data

2、把status数据类型 int 更换为Integer 是为了不让status 有默认值。int类型会有默认数据 0,会影响状态码为 0的数据。

3、初始化status值。status = builder.status == null ? CommonCodeEnum.SUCCESS.getCode() : builder.status;

4、将data初始化,便于下方params 和 msg 插入。这里不再用new方式初始化Map,改用 Maps.newHashMapWithExpectedSize 方式。

同时初始化了Map的数据。因为在业务中定义Map只有两个参数。此时给定Map Size,也避免了空间浪费。

package com.log;
import ch.qos.logback.LogbackConstants; import com.log.enums.BusinessTypeEnum; import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map;
/** * @version 1.0 * @description 构建器构建日志打印 * @date 2020/11/11 13:06 */ // --这里用 @getter,省略了内部参数的get方法。由于此类没有set,故不用 @Data @Getter public class ServiceLog { private final static Logger log = LoggerFactory.getLogger(ServiceLog.class); private BusinessTypeEnum businessType; private String messageId; private String productKey; private String deviceName; private String operation; private Map data; private int status; private ServiceLog(Builder builder){ this.messageId = builder.messageId; this.productKey = builder.productKey; this.deviceName = builder.deviceName; this.businessType = builder.businessType; this.operation = builder.operation; this.data = builder.data;
     // 给定status 数据值,避免 status == null 情况。
this.status = builder.status == null ? CommonCodeEnum.SUCCESS.getCode() : builder.status; } // -- 打印日志 public void print(){ // LogbackConstants.JSON_OBJECT = "JSON_OBJECT", 将日志类型做成枚举 log.info(LogbackConstants.JSON_OBJECT, this); } // -- 静态方法,初始化 Builder public static Builder builder(BusinessTypeEnum businessType){ return new Builder(businessType); } public static class Builder { // -- 必填参数。这个必填,使用了枚举参数 private BusinessTypeEnum businessType; // -- 非必填参数 private String messageId; private String productKey; private String deviceName; private String operation; private final Map<String, Object> data;
     // -- 这里把 int 更换为Integer 是为了不让status 有默认值。int类型会有默认数据 0,会影响状态码为 0的数据。
private Integer status; public Builder(BusinessTypeEnum businessType){ this.businessType = businessType; // -- 这里先将data初始化,便于下方params 和 msg 插入。这里不再用new方式初始化Map,改用 Maps.newHashMapWithExpectedSize 方式。
       // 同时初始化了Map的数据。因为在业务中定义Map只有两个参数。此时给定Map Size,也避免了空间浪费。
this.data = Maps.newHashMapWithExpectedSize(2); } public Builder mid(String messageId){ this.messageId = messageId; return this; } public Builder pk(String productKey){ this.productKey = productKey; return this; } public Builder dn(String deviceName){ this.deviceName = deviceName; return this; } public Builder opt(String operation){ this.operation = operation; return this; } public Builder params(Object params){ this.data.put("params", params); return this; } public Builder msg(String message){ this.data.put("message", message); return this; } public Builder status(int status){ this.status = status; return this; } public ServiceLog build(){ return new ServiceLog(this); } } }

 

调用示例:

public static void main(String[] args) {
    // -- 第一版调用
    log.info("JSON","productKey", "ProductKey", "deviceName", "DeviceName","businessType","OTA");
    
    // -- 第二版调用
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("params",new JSONObject());
    jsonObject.put("message","调用成功!");
    log.info("JSON_OBJECT", new ServiceLog("msgid", "ProductKey","DeviceName","OTA","operation",jsonObject,200));
    
     // -- 第三版调用  
     new ServiceLog.Builder(BusinessTypeEnum.OTA).pk("ProductKey").msg("调用成功").build(log);
    
    // -- 第四版调用  对于非必填的参数 mid, 可调用可不调用
    ServiceLog.builder(BusinessTypeEnum.OTA).pk("ProductKey").dn("DeviceName").opt("OTAProgress").msg("固件升级异常").status(CommonCodeEnum.SYS_ERROR.getCode()).build().print();
    ServiceLog.builder(BusinessTypeEnum.OTA).mid("1001").pk("ProductKey").dn("DeviceName").opt("OTAProgress").params(new JSONObject()).status(200).build().print();

    // -- 第四版调用,合并版
    ServiceLog.Builder builder = ServiceLog.builder(BusinessTypeEnum.OTA).pk("ProductKey").dn("DeviceName").opt("OTAProgress");
    builder.msg("固件升级异常").status(CommonCodeEnum.SYS_ERROR.getCode()).build().print();
    builder.mid("1001").params(new JSONObject()).status(200).build().print();


}

 

打印结果:

// -- msg 内是日志打印内容,其他如:date \ level \ TRACE_ID 是logback打印内容
// 第一版结果
{"date":"2020-11-12 15:33:00.267","msg":{"productKey":"ProductKey","businessType":"OTA","deviceName":"DeviceName"},"level":"INFO","line":"27","logger":"c.c.i.i.IotServiceApplicationTests","TRACE_ID":"","client":""}
// 第二版结果
{"date":"2020-11-12 15:33:00.360","msg":{"businessType":"OTA","data":{},"deviceName":"DeviceName","messageId":"","operation":"","productKey":"ProductKey","status":200},"level":"INFO","line":"28","logger":"c.c.i.i.IotServiceApplicationTests","TRACE_ID":"","client":""}
// 第三版结果
{"date":"2020-11-12 16:21:26.798","msg":{"businessType":"OTA","data":{"message":"调用成功"},"productKey":"ProductKey","status":0},"level":"INFO","line":"87","logger":"c.c.i.i.IotServiceApplicationTests","TRACE_ID":"","client":""}

// 第四版结果
{"date":"2020-11-12 14:17:33.480","msg":{"businessType":"OTA","data":{"message":"固件升级异常"},"deviceName":"DeviceName","operation":"OTAProgress","productKey":"ProductKey","status":500},"level":"INFO","line":"42","logger":"com.iot.log.dto.ServiceLog","TRACE_ID":"","client":""}
{"date":"2020-11-12 14:17:33.618","msg":{"businessType":"OTA","data":{"params":{}},"deviceName":"DeviceName","messageId":"1001","operation":"OTAProgress","productKey":"ProductKey","status":200},"level":"INFO","line":"42","logger":"com.iot.log.dto.ServiceLog","TRACE_ID":"","client":""}

 

 

posted @ 2020-11-16 20:44  currentTimeMillis  阅读(185)  评论(0编辑  收藏  举报