
阿里妹导读
一、创建 Logger 实例
1.1 工厂函数
如果是字符串,这个字符串会作为返回Logger实例的名字;
public class ExampleService {// 传 Class,一般都是传当前的 Classprivate static final Logger log = LoggerFactory.getLogger(ExampleService.class);// 上边那一行相当于:private static final Logger log = LoggerFactory.getLogger("com.example.service.ExampleService");// 你也可以指定任意字符串private static final Logger log = LoggerFactory.getLogger("service");}
1.2 Lombok
@Slf4jpublic class ExampleService {// 注解 @Slf4j 会帮你生成下边这行代码// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExampleService.class);}@Slf4j(topic = "service")public class ExampleService {// 注解 @Slf4j(topic = "service") 会帮你自动生成下边这行代码// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger("service");}
二、日志级别
TRACE:一般用于记录调用链路,比如方法进入时打印xxx start;
DEBUG:个人觉得它和 trace 等级可以合并,如果一定要区分,可以用来打印方法的出入参;
INFO:默认级别,一般用于记录代码执行时的关键信息;
WARN:当代码执行遇到预期外场景,但它不影响后续执行时,可以使用;
@Slf4jpublic class ExampleService {@Resourceprivate RpcService rpcService;public String querySomething(String request) {// 使用 trace 标识这个方法调用情况log.trace("querySomething start");// 使用 debug 记录出入参log.debug("querySomething request={}", request);String response = null;try {RpcResult rpcResult = rpcService.call(a);if (rpcResult.isSuccess()) {response = rpcResult.getData();// 使用 info 标识重要节点log.info("querySomething rpcService.call succeed, request={}, rpcResult={}", request, rpcResult);} else {// 使用 warn 标识程序调用有预期外错误,但这个错误在可控范围内log.warn("querySomething rpcService.call failed, request={}, rpcResult={}", request, rpcResult);}} catch (Exception e) {// 使用 error 记录程序的异常信息log.error("querySomething rpcService.call abnormal, request={}, exception={}", request, e.getMessage(), e);}// 使用 debug 记录出入参log.debug("querySomething response={}", response);// 使用 trace 标识这个方法调用情况log.trace("querySomething end");return response;}}
三、打印接口
public boolean info(...);
3.1 info 方法

3.2 isInfoEnabled 方法
if (log.isInfoEnabled()) {log.info(...)}
if (log.isInfoEnabled()) {// 有远程调用String resource = rpcService.call();log.info("resource={}", resource)// 要解析大对象Object result = ....; // 一个大对象log.info("result={}", JSON.toJSONString(result));}
四、Marker
Marker marker = MarkerFactory.getMarker("foobar");log.info(marker, "test a={}", 1);
将Marker通过%marker打印出来;
五、MDC
// 和 Map<String, String> 相似的接口定义MDC.put("key", "value");String value = MDC.get("key");MDC.remove("key");MDC.clear();// 获取 MDC 中的所有内容Map<String, String> context = MDC.getCopyOfContextMap();
六、Fluent API (链式调用)
Marker marker = MarkerFactory.getMarker("foobar");Exception e = new RuntimeException();// == 以下几个示例的最终效果是完全一致的 ==// 这是传统的调用方式log.info(market, "request a={}, b={}", 1, 2, e);// Fluent API 例1log.atInfo() // 表示这是 INFO 级别。你猜对了,还有 atTrace/atDebug/atWarn/atError.addMarker(marker).log("request a={}, b={}", 1, 2, e); // 与传统 API 很像// Fluent API 例2log.atInfo().addMarker(marker).setCause(e).setMessage("request a={}, b={}") // 传字符串模板.setMessage(() -> "request a={}, b={}") // setMessage 支持传入 Supplier.addArgument(1) // 添加与字符串模板中占们符所对应的值.addArgument(() -> 2) // addArgument 支持传入 Supplier.log(); // 大火收汁// == addKeyValue 的输出格式依赖日志实现层的配置,默认格式与上边示例不同 ==// Fluent API 例3log.atInfo().setMessage("request") // 注意这里没有占位符.setKeyValue("a", 1) // 通过 setKeyValue 添加关心的变量.setKeyValue("b", () -> 2) // value 支持传入 Supplier.log();// 通过 setKeyValue 设置的值默认会放在 message 前边,比如上边这个例子,默认会输出:// a=1 b=2 request

log.info("request a={}", () -> a);
七、后记
[1]https://projectlombok.org/features/log
[2]https://juejin.cn/post/6915015034565951501
[3]https://logback.qos.ch/apidocs/ch/qos/logback/classic/turbo/MarkerFilter.html
[4]https://www.aliyun.com/product/xtrace
[5]https://www.slf4j.org/manual.html#fluent