1. 日志上云工具
阿里云 sls + aliyun logback appender
logback 是由 log4j 创始人设计的又一个开源日志组件。通过使用 logback,您可以控制日志信息输送的目的地是控制台、文件、gui 组件、甚至是套接口服务器、nt 的事件记录器、unix syslog 守护进程等;您也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,您能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
aliyun logback appender
通过aliyun log logback appender,您可以控制日志的输出目的地为阿里云日志服务,写到日志服务中的日志的样式如下:
vbnet
复制代码
level: error location: com.aliyun.openservices.log.logback.example.logbackappenderexample.main(logbackappenderexample.java:18) message: error log throwable: java.lang.runtimeexception: xxx thread: main time: 2018-01-02t03:15+0000 log: 2018-01-02 11:15:29,682 error [main] com.aliyun.openservices.log.logback.example.logbackappenderexample: error log __source__: xxx __topic__: yyy
- level: 日志级别。
- location: 日志打印语句的代码位置,可以通过配置关闭此选项。
- message: 日志内容。
- throwable: 日志异常信息(只有记录了异常信息,这个字段才会出现)。
- thread: 线程名称。
- time: 日志打印时间(可以通过 timeformat 或 timezone 配置 time 字段呈现的格式和时区)。
- log: 自定义日志格式(只有设置了 encoder,这个字段才会出现)。
- source: 日志来源,用户可在配置文件中指定。
- topic: 日志主题,用户可在配置文件中指定。
aliyun logback appender 的功能优势
- 日志不落盘:产生数据实时通过网络发给服务端。
- 无需改造:对已使用logback应用,只需简单配置即可采集。
- 异步高吞吐:高并发设计,后台异步发送,适合高并发写入。
- 上下文查询:服务端除了通过关键词检索外,给定日志能够精确还原原始日志文件上下文日志信息。
2. springboot 整合 aliyun logback appender
阿里云 sls 日志服务
如图所示,创建一个 project:
进入 project,创建一个日志库 log store:
maven 工程中引入依赖
xml
复制代码
<dependency> <groupid>com.google.protobuf</groupid> <artifactid>protobuf-java</artifactid> <version>2.5.0</version> </dependency> <dependency> <groupid>com.aliyun.openservices</groupid> <artifactid>aliyun-log-logback-appender</artifactid> <version>0.1.25</version> </dependency>
添加 logback 配置文件
如图所示,在 spirngboot 的 resources 下,建立 logstore 目录,创建名为 logback-{环境}.xml 的配置文件:
xml
复制代码
<?xml version="1.0" encoding="utf-8"?> <configuration> <!--为了防止进程退出时,内存中的数据丢失,请加上此选项--> <shutdownhook class="ch.qos.logback.core.hook.delayingshutdownhook"/> <appender name="stdout" class="ch.qos.logback.core.consoleappender"> <encoder> <pattern>%d{hh:mm:ss.sss} [%thread] %-5level %logger - %msg %x{thread_id} %n</pattern> </encoder> </appender> <appender name="aliyun" class="com.aliyun.openservices.log.logback.loghubappender"> <!--必选项--> <!-- 账号及网络配置 --> <endpoint>这里写你自己的endpoint</endpoint> <accesskeyid>这里写你自己的accesskeyid</accesskeyid> <accesskeysecret>这里写你自己的accesskeysecret</accesskeysecret> <!-- sls 项目配置 --> <project>这里写你自己的project</project> <logstore>这里写你自己的logstore</logstore> <!--必选项 (end)--> <!-- 可选项 详见 '参数说明'--> <totalsizeinbytes>104857600</totalsizeinbytes> <maxblockms>0</maxblockms> <iothreadcount>8</iothreadcount> <batchsizethresholdinbytes>524288</batchsizethresholdinbytes> <batchcountthreshold>4096</batchcountthreshold> <lingerms>2000</lingerms> <retries>10</retries> <baseretrybackoffms>100</baseretrybackoffms> <maxretrybackoffms>50000</maxretrybackoffms> <!--只打印级别含info及以上的日志--> <filter class="ch.qos.logback.classic.filter.thresholdfilter"> <level>info</level> </filter> <!-- 可选项 设置时间格式 --> <timezone>asia/shanghai</timezone> <timeformat>yyyy-mm-dd hh:mm:ss</timeformat> <mdcfields>traceid</mdcfields> </appender> <root> <level value="info"/> <appender-ref ref="stdout"/> <appender-ref ref="aliyun"/> </root> </configuration>
配置中的 endpoint accesskeyid accesskeysecret 如何获取?参考阿里云sls帮助: help.aliyun.com/zh/sls/deve…
配置文件参数说明
properties
复制代码
#日志服务的 project 名,必选参数 project = [your project] #日志服务的 logstore 名,必选参数 logstore = [your logstore] #日志服务的 http 地址,必选参数 endpoint = [your project endpoint] #用户身份标识,必选参数 accesskeyid = [your accesskey id] accesskeysecret = [your accesskeysecret] #单个 producer 实例能缓存的日志大小上限,默认为 100mb。 totalsizeinbytes=104857600 #如果 producer 可用空间不足,调用者在 send 方法上的最大阻塞时间,默认为 60 秒。为了不阻塞打印日志的线程,强烈建议将该值设置成 0。 maxblockms=0 #执行日志发送任务的线程池大小,默认为可用处理器个数。 iothreadcount=8 #当一个 producerbatch 中缓存的日志大小大于等于 batchsizethresholdinbytes 时,该 batch 将被发送,默认为 512 kb,最大可设置成 5mb。 batchsizethresholdinbytes=524288 #当一个 producerbatch 中缓存的日志条数大于等于 batchcountthreshold 时,该 batch 将被发送,默认为 4096,最大可设置成 40960。 batchcountthreshold=4096 #一个 producerbatch 从创建到可发送的逗留时间,默认为 2 秒,最小可设置成 100 毫秒。 lingerms=2000 #如果某个 producerbatch 首次发送失败,能够对其重试的次数,默认为 10 次。 #如果 retries 小于等于 0,该 producerbatch 首次发送失败后将直接进入失败队列。 retries=10 #该参数越大能让您追溯更多的信息,但同时也会消耗更多的内存。 maxreservedattempts=11 #首次重试的退避时间,默认为 100 毫秒。 #producer 采样指数退避算法,第 n 次重试的计划等待时间为 baseretrybackoffms * 2^(n-1)。 baseretrybackoffms=100 #重试的最大退避时间,默认为 50 秒。 maxretrybackoffms=50000 #指定日志主题,默认为 "",可选参数 topic = [your topic] #指的日志来源,默认为应用程序所在宿主机的 ip,可选参数 source = [your source] #输出到日志服务的时间的格式,默认是 yyyy-mm-dd't'hh:mmz,可选参数 timeformat = yyyy-mm-dd't'hh:mmz #输出到日志服务的时间的时区,默认是 utc,可选参数(如果希望 time 字段的时区为东八区,可将该值设定为 asia/shanghai) timezone = utc #是否要记录 location 字段(日志打印位置),默认为 true,如果希望减少该选项对性能的影响,可以设为 false includelocation = true #当 encoder 不为空时,是否要包含 message 字段,默认为 true includemessage = true
进入 sls 查看日志信息
3. springboot 项目加入基础 traceid
公共的请求返回对象中加入 traceid
java
复制代码
@data @apimodel("统一返回实体") public final class apiresult<t> implements serializable { private static final long serialversionuid = -5907790295620098443l; @apimodelproperty("状态码") private int code = 200; @apimodelproperty("数据对象") private t data; @apimodelproperty("错误信息") private string error; @apimodelproperty("请求状态") private boolean success = true; @apimodelproperty("链路追踪id") private string traceid; public static final string trace_id = "traceid"; private apiresult() { this.traceid = mdc.get(apiresult.trace_id); } private apiresult(t data) { this.data = data; this.traceid = mdc.get(apiresult.trace_id); } private apiresult(int code, string error) { this.code = code; this.error = error; this.success = false; this.traceid = mdc.get(apiresult.trace_id); } private apiresult(int code, t data, string error) { this.code = code; this.data = data; this.error = error; this.success = false; this.traceid = mdc.get(apiresult.trace_id); } public static <t> apiresult<t> ok() { return new apiresult<>(); } public static <t> apiresult<t> ok(t data) { return new apiresult<>(data); } public static <t> apiresult<t> error(int code, string error) { return new apiresult<>(code, error); } public static <t> apiresult<t> error(int code, t data, string error) { return new apiresult<>(code, data, error); } }
创建一个上下文对象持有 traceid
java
复制代码
@data public class tracesession implements serializable { private static final long serialversionuid = -1545421111337427237l; /** * 当前环境 */ private string env; /** * 登录用户id */ private string userid; /** * 请求追踪id */ private string traceid; /** * 请求语言信息 */ private string language; /** * 请求平台信息 */ private string platform; /** * 请求渠道信息 */ private string channel; /** * 请求版本信息 */ private string version; }
这里先引入一下这个包,后文会具体解释:
xml
复制代码
<dependency> <groupid>com.alibaba</groupid> <artifactid>transmittable-thread-local</artifactid> <version>2.14.2</version> </dependency>
java
复制代码
public class belifecontext { private static final threadlocal<tracesession> thread_local = new transmittablethreadlocal<>(); public static void initsession(tracesession tracesession) { thread_local.set(tracesession); } public static tracesession getsession() { return thread_local.get(); } public static void clearsession() { thread_local.remove(); } public static string gettraceid() { tracesession tracesession = getsession(); if (tracesession == null) return null; return tracesession.gettraceid(); } }
利用拦截器来生成和处理这个 traceid
java
复制代码
@component public class traceinterceptor implements handlerinterceptor { @value("${spring.profiles.active}") private string profile; @override public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception { string traceid = uuid.randomuuid().tostring().replace("-", ""); mdc.put(apiresult.trace_id, traceid); tracesession tracesession = buildtracesession(traceid); belifecontext.initsession(tracesession); return true; } @override public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception { mdc.remove(apiresult.trace_id); belifecontext.clearsession(); } private tracesession buildtracesession(string traceid) { tracesession tracesession = new tracesession(); tracesession.setenv(profile); tracesession.settraceid(traceid); // 用户信息和请求头信息这里省略 ... return tracesession; } }
建立一个测试请求来测试日志和返回值
java
复制代码
@slf4j @restcontroller @requestmapping("/v1/app/version") @api(tags = {"app版本接口"}) public class versioncontroller { @apioperation("版本信息") @getmapping("/info") public apiresult<string> versioninfo() { log.info("测试一下app版本信息"); return apiresult.ok(); } }
bash
复制代码
### app版本信息 get http://localhost:8081/v1/app/version/info content-type: application/json
我们得到的返回值中的 traceid,然后去日志库中查询相关日志信息:
yaml
复制代码
http/1.1 200 content-type: application/json transfer-encoding: chunked date: wed, 22 may 2024 07:42:45 gmt keep-alive: timeout=60 connection: keep-alive { "code": 200, "data": null, "error": null, "success": true, "traceid": "4f267ab7deaa4149acca19cac69b3912" }
4. 项目中运行报错加入 traceid
定义一个自己的义务异常 bizexception
java
复制代码
@data public class bizexception extends runtimeexception { private static final long serialversionuid = -3697924501642645015l; private int code; public bizexception(string message) { super(message); } public bizexception(int code, string message) { super(message); this.code = code; } }
使用全局异常捕获器处理 bizexception
java
复制代码
@slf4j @controlleradvice public class exceptionadvisor { @responsebody @exceptionhandler(bizexception.class) public apiresult<?> exceptionhandler(httpservletrequest request, bizexception ex) { log.error(ex.getmessage(), ex); apiresult<?> apiresult = apiresult.error(httpstatus.internal_server_error.value(), ex.getmessage()); return apiresult; } @responsebody @exceptionhandler(exception.class) public apiresult<?> exceptionhandler(httpservletrequest request, exception ex) { log.error(ex.getmessage(), ex); apiresult<?> apiresult = apiresult.error(httpstatus.internal_server_error.value(), "internal server error"); return apiresult; } }
建立一个测试请求来测试日志和返回值
java
复制代码
@slf4j @restcontroller @requestmapping("/v1/app/version") @api(tags = {"app版本接口"}) public class versioncontroller { @apioperation("版本信息") @getmapping("/info") public apiresult<string> versioninfo() { throw new bizexception("测试一下app版本信息-接口报错"); // return apiresult.ok(); } }
bash
复制代码
### app版本信息 get http://localhost:8081/v1/app/version/info content-type: application/json
我们得到的返回值中的 traceid,然后去日志库中查询相关日志信息:
yaml
复制代码
http/1.1 200 content-type: application/json transfer-encoding: chunked date: wed, 22 may 2024 08:03:16 gmt keep-alive: timeout=60 connection: keep-alive { "code": 500, "data": null, "error": "测试一下app版本信息-接口报错", "success": false, "traceid": "8f883ce3b7a845bca5ea83b348b5113a" }
5. 项目中 mybatis-plus 的 sql 日志加入 traceid
maven 工程中引入依赖
xml
复制代码
<dependency> <groupid>p6spy</groupid> <artifactid>p6spy</artifactid> <version>3.9.1</version> </dependency>
项目中新加入配置文件 spy.properties
java
复制代码
@propertysource(value = "classpath:spy.properties")
appender 选择 com.p6spy.engine.spy.appender.slf4jlogger 就自动接入了我们项目中的 aliyun logback appender 体系:
properties
复制代码
# 模块列表,根据版本选择合适的配置 modulelist=com.baomidou.mybatisplus.extension.p6spy.mybatispluslogfactory,com.p6spy.engine.outage.p6outagefactory # 自定义日志格式 # (替换 p6spyformatconfig) logmessageformat=com.baomidou.mybatisplus.extension.p6spy.p6spylogger logmessageformat=org.belife.domain.config.p6spyformatconfig # 日志输出到控制台 # (替换 slf4jlogger) appender=com.baomidou.mybatisplus.extension.p6spy.stdoutlogger appender=com.p6spy.engine.spy.appender.slf4jlogger # 取消jdbc驱动注册 deregisterdrivers=true # 使用前缀 useprefix=true # 排除的日志类别 excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-mm-dd hh:mm:ss # 实际驱动列表 # driverlist=org.h2.driver # 开启慢sql记录 outagedetection=true # 慢sql记录标准(单位:秒) outagedetectioninterval=2
自定义 sql 日志格式化器
java
复制代码
public class p6spyformatconfig implements messageformattingstrategy { @override public string formatmessage(int connectionid, string now, long elapsed, string category, string prepared, string sql, string url) { return stringutils.isnotblank(sql) ? dateformatutils.format(new date(), "yyyy-mm-dd hh:mm:ss") + " | " + elapsed + " ms | " + sql.replaceall("[\\s]+", stringutils.space) + ";" : ""; } }
修改 springboot 数据源连接的配置
yml
复制代码
spring: datasource: driver-class-name: com.p6spy.engine.spy.p6spydriver url: jdbc:p6spy:mysql://(写你自己的连接地址):3306/test_belife?servertimezone=gmt%2b8&useunicode=true&characterencoding=utf8&usessl=true&connecttimeout=60000&sockettimeout=60000&allowmultiqueries=true username: (写你自己的账号) password: (写你自己的密码) druid: initial-size: 3 min-idle: 5 max-active: 30 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: select 1 from dual test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 50
这里为了方便对比,放出原先的 druid 数据源配置:
yml
复制代码
spring: datasource: driver-class-name: com.mysql.cj.jdbc.driver url: jdbc:mysql://(写你自己的连接地址):3306/prd_belife?servertimezone=gmt%2b8&useunicode=true&characterencoding=utf8&usessl=true&connecttimeout=60000&sockettimeout=60000&allowmultiqueries=true username: (写你自己的账号) password: (写你自己的密码) druid: initial-size: 3 min-idle: 5 max-active: 30 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: select 1 from dual test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 50
建立一个测试请求来测试日志和返回值
java
复制代码
@slf4j @restcontroller @requestmapping("/v1/app/version") @api(tags = {"app版本接口"}) public class versioncontroller { @autowired private versionservice versionservice; @apioperation("版本信息") @getmapping("/info") public apiresult<versiondo> versioninfo() { log.info("测试一下app版本信息"); versiondo latest = versionservice.findlatest("android"); return apiresult.ok(latest); } }
bash
复制代码
### app版本信息 get http://localhost:8081/v1/app/version/info content-type: application/json
我们得到的返回值中的 traceid,然后去日志库中查询相关日志信息:
javascript
复制代码
http/1.1 200 content-type: application/json transfer-encoding: chunked date: wed, 22 may 2024 08:26:55 gmt keep-alive: timeout=60 connection: keep-alive { "code": 200, "data": { "id": "44", "platform": "android", "channel": null, "version": "1.7.0", "forces": 0, "chnotice": "中文", "ennotice": "更新", "fileurl": null, "filesize": null, "status": 1, "remark": "" }, "error": null, "success": true, "traceid": "7c5016ae8a9147f89f0b73e7c093bce4" }
sql
复制代码
16:26:55.268 [http-nio-8081-exec-1] info p6spy - 2024-05-22 16:26:55 | 79 ms | select * from app_version where platform = 'android' and status = 1 order by gmt_created desc limit 0,1;
6. 项目中异步线程池运行加入 traceid
引入阿里的 ttl 工具包
xml
复制代码
<dependency> <groupid>com.alibaba</groupid> <artifactid>transmittable-thread-local</artifactid> <version>2.14.2</version> </dependency>
具体实现原理可以参考作者的其他文章:
transmittablethreadlocal 线程池内异步线程值传递解决方案
实现一个抽象的线程池处理器
java
复制代码
@slf4j public abstract class ttlpoolmanager { executorservice executorservice = initexecutorservice(); executorservice ttlexecutorservice = ttlexecutors.getttlexecutorservice(executorservice); protected abstract executorservice initexecutorservice(); public future<?> submit(runnable task) { return ttlexecutorservice.submit(() -> { try { mdc.put(apiresult.trace_id, gettraceid()); ttlrunnable.get(task).run(); } catch (exception e) { log.error(e.getmessage(), e); } finally { mdc.remove(apiresult.trace_id); } }); } public <t> future<t> submit(callable<t> task) { return ttlexecutorservice.submit(() -> { try { mdc.put(apiresult.trace_id, gettraceid()); return ttlcallable.get(task).call(); } finally { mdc.remove(apiresult.trace_id); } }); } public static string gettraceid() { return belifecontext.gettraceid(); } }
这里的 traceid 就从我们前文实现的 belifecontext.gettraceid() 中获取。 如果我们要使用线程池,下边就给出一个简单的参考:
scala
复制代码
@component public class messagepoolmanager extends ttlpoolmanager { @override protected executorservice initexecutorservice() { // 这里初始化自己想要的线程池 return executors.newfixedthreadpool(10); } }
建立一个测试请求来测试日志和返回值
java
复制代码
@slf4j @restcontroller @requestmapping("/v1/app/version") @api(tags = {"app版本接口"}) public class versioncontroller { @autowired private messagepoolmanager messagepoolmanager; @apioperation("版本信息") @getmapping("/info") public apiresult<string> versioninfo() { log.info("测试一下app版本信息-主线程"); messagepoolmanager.submit(() -> log.info("测试一下app版本信息-异步线程")); return apiresult.ok(); } }
bash
复制代码
### app版本信息 get http://localhost:8081/v1/app/version/info content-type: application/json
我们得到的返回值中的 traceid,然后去日志库中查询相关日志信息:
yaml
复制代码
http/1.1 200 content-type: application/json transfer-encoding: chunked date: wed, 22 may 2024 08:43:05 gmt keep-alive: timeout=60 connection: keep-alive { "code": 200, "data": null, "error": null, "success": true, "traceid": "90e1d2878280462084a6145c8ce1e00f" }
7. 项目中使用 rocketmq 加入 traceid
使用的阿里云的 rocketmq 4.0版
以 message 的 key 作为 traceid 改造生产者和消费者
这里的代码只是一种改造思路(代码涉及到业务会有很多,只贴了重要的一部分),每个人封装的 rocketmq 方式不同:
java
复制代码
@slf4j @component public final class syncmessageproducer { @autowired private producerbean producer; public void sendnormalmessage(string content, string topic, mqbiztags tags) { message message = new message(); message.settopic(topic); message.settag(tags.name()); message.setkey(belifecontext.gettraceid()); message.setbody(content.getbytes(standardcharsets.utf_8)); try { sendresult sendresult = producer.send(message); assert sendresult != null; log.info(sendresult + " text: {}", content); } catch (onsclientexception e) { log.error("mq发送失败, text: {}", content); // 出现异常意味着发送失败,为了避免消息丢失,建议缓存该消 } } public void sendtimingmessage(string content, string topic, mqbiztags tags, date delivertime) { message message = new message(); message.settopic(topic); message.settag(tags.name()); message.setkey(belifecontext.gettraceid()); message.setbody(content.getbytes(standardcharsets.utf_8)); message.setstartdelivertime(delivertime.gettime()); try { sendresult sendresult = producer.send(message); assert sendresult != null; log.info(sendresult + " text: {}, time: {}", content, delivertime); } catch (onsclientexception e) { log.error("mq发送失败, text: {}, time: {}", content, delivertime); // 出现异常意味着发送失败,为了避免消息丢失,建议缓存该消 } } }
这里的 traceid 就从我们前文实现的 belifecontext.gettraceid() 中获取。
java
复制代码
@slf4j public abstract class absmessageconsumer implements messagelistener { public abstract mqbiztags support(); @override public action consume(message message, consumecontext context) { mdc.put(apiresult.trace_id, message.getkey()); string messagebody = new string(message.getbody()); log.info("receive message: {}", messagebody); return consume(messagebody); } protected abstract action consume(string messagebody); }
java
复制代码
@slf4j @service public class ordercloseconsumer extends absmessageconsumer { @autowired private paymentfacadeservice paymentfacadeservice; @override public mqbiztags support() { return mqbiztags.close_order; } @override protected action consume(string messagebody) { string ordersn = messagebody; try { paymentfacadeservice.dealwithmqconsumer(ordersn); } catch (bizexception bizex) { log.error(bizex.getmessage(), bizex); } catch (exception e) { log.error(e.getmessage(), e); return action.reconsumelater; } return action.commitmessage; } }
建立一个测试请求来测试日志和返回值
测试代码涉及业务代码会有很多,简单描述下业务场景:
通过 traceid 查询到我们对应的请求的订单相关的消息:
去日志库中查询相关日志信息,即使相隔15分钟,依然能追溯完整的生命周期:
8. 项目中的请求 httprequest 快照加入 traceid
利用 spring 的 aop 处理控制器的方法
java
复制代码
@slf4j @aspect @component public class weblogaspect { @value("${be-life-app.token-header:belife-app-token}") private string tokenheader; @value("${be-life-app.version-header:belife-app-version}") private string versionheader; @value("${be-life-app.platform-header:belife-app-platform}") private string platformheader; @value("${be-life-app.channel-header:belife-app-channel}") private string channelheader; @value("${be-life-app.language-header:belife-app-language}") private string languageheader; @pointcut("execution(public * org.belife.app.controller..*.*(..))") public void weblog() { } @around("weblog()") public object doaround(proceedingjoinpoint proceedingjoinpoint) throws throwable { servletrequestattributes attributes = (servletrequestattributes) requestcontextholder.getrequestattributes(); httpservletrequest request = attributes.getrequest(); long starttime = system.currenttimemillis(); object result = null; try { result = proceedingjoinpoint.proceed(); return result; } finally { if (result instanceof apiresult) { apiresult<?> apiresult = (apiresult<?>) result; apiresult.settraceid(mdc.get(apiresult.trace_id)); } long endtime = system.currenttimemillis(); string requestmethod = request.getmethod(); string requesturi = request.getrequesturi(); string costtime = (endtime - starttime) + "ms"; string jsonparams = getparams(proceedingjoinpoint); string token = request.getheader(tokenheader); string version = request.getheader(versionheader); string platform = request.getheader(platformheader); string channel = request.getheader(channelheader); string language = request.getheader(languageheader); string area = request.getheader(cnareaheader); log.info("method: {}, url: {}, version: {}, platform: {}, channel: {}, language: {}, token: {}, time: {}, params: {}", requestmethod, requesturi, version, platform, channel, language, token, costtime, jsonparams); } } /** * 获取参数名和参数值 * * @param joinpoint * @return 返回json结构字符串 */ public string getparams(joinpoint joinpoint) { linkedhashmap<string, object> map = new linkedhashmap<>(); object[] values = joinpoint.getargs(); string[] names = ((codesignature) joinpoint.getsignature()).getparameternames(); for (int i = 0; i < names.length; i++) { if (names[i].equals("request") || names[i].equals("response")) continue; map.put(names[i], values[i]); } return jsonobject.tojsonstring(map); } }
建立一个测试请求来测试日志和返回值
java
复制代码
@slf4j @restcontroller @requestmapping("/v1/app/version") @api(tags = {"app版本接口"}) public class versioncontroller { @autowired private messagepoolmanager messagepoolmanager; @apioperation("版本信息") @getmapping("/info") public apiresult<string> versioninfo() { log.info("测试一下app版本信息-主线程"); messagepoolmanager.submit(() -> log.info("测试一下app版本信息-异步线程")); return apiresult.ok(); } }
makefile
复制代码
### app版本信息 get http://localhost:8081/v1/app/version/info?version=1.6.0 belife-app-platform: android belife-app-channel: googleplay belife-app-version: 1.6.0 belife-app-language: zh belife-app-token: 0m_sv7ju6tsytdl5q_n8mbduttenratyzg__
我们得到的返回值中的 traceid,然后去日志库中查询相关日志信息:
yaml
复制代码
http/1.1 200 content-type: application/json transfer-encoding: chunked date: wed, 22 may 2024 09:07:09 gmt keep-alive: timeout=60 connection: keep-alive { "code": 200, "data": null, "error": null, "success": true, "traceid": "e2a3c9c2adfa4db8b3e4761f67943802" }
yaml
复制代码
17:12:51.390 [http-nio-8081-exec-1] info org.belife.app.aspect.weblogaspect - method: get, url: /v1/app/version/info, version: 1.6.0, platform: android, channel: googleplay, language: zh, area: null, token: 0m_sv7ju6tsytdl5q_n8mbduttenratyzg__, time: 6ms, params: {"version":"1.6.0"}
发表评论