项目内日志打印,从第一版修改到第四版。每次修改都是进步。
第一版:直接打印
一般日志打印方法,更直接的会直接在这里打印字符串。这种方式打印的日志在编码过程中调试方便,便于理解。
但是对于已上线的项目,就比较麻烦了。日志太多,且杂乱。
此示例打印时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":""}