SpringCloud中服务集成PlumeLog日志系统
目录一、plumeLog是什么二、ES搭建2.1、下载ES安装包安装2.2、修改elasticsearch.yml配置2.3、修改系统相关配置2.4、启动ES服务2.5、使用 Systemd 管理ES服务开机自启三、plumeLog-server搭建3.1、下载安装包安装3.2、修改application.properties配置3.3、启动服务四、springboot服务集成plumelog4.1、项目引入pom.xml依赖4.2、项目logback-spring.xml配置4.3、增加TraceId过滤器(Http请求线程打入TraceId)4.4、全局异常处理器改造4.5、新增TraceResponseAdvice返回对象Resp中设置traceId4.6、其他后台线程设置traceId五、请求接口验证5.1、正常响应请求5.2、异常响应请求5.3、plume日志查看六、扩展 雪花算法中workerId怎么取在我们实际的项目开发过程中服务日志从收集、索引及查询也是非常重要的一环本篇给大家介绍下一款非常优秀的日志收集、处理框架plumeLog一、plumeLog是什么参考官网README1、plume是无代码入侵的分布式日志系统基于log4j、log4j2、logback搜集日志设置链路ID2、方便查询关联日志3、基于elasticsearch作为查询引擎4、高吞吐查询效率高5、全程不占应用程序本地磁盘空间免维护;对于项目透明不影响项目本身运行6、无需修改老项目引入直接使用支持dubbo,支持springcloud二、ES搭建从plumeLog的架构图上我们得知日志的底层存储是依赖ES的我们先最底层开始搭建起先搭建ES笔者采用的是elasticsearch7.x的版本。2.1、下载ES安装包安装# 下载安装包若已上传至服务器可跳过wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.10.1-linux-x86_64.tar.gz# 解压文件tar -zxvf elasticsearch-7.10.1-linux-x86_64.tar.gz# 移动到 /usr/local/ 目录并重命名mv elasticsearch-7.10.1 /usr/local/elasticsearch# 创建数据目录、日志目录mkdir -p /data/es/datamkdir -p /data/es/log#创建es用户groupadd elasticuseradd -g elastic elastic# 将es目录所属主用户赋予给elastic用户chown -R elastic:elastic /usr/local/elasticsearchchown -R elastic:elastic /data/es2.2、修改elasticsearch.yml配置cluster.name: my-application # 集群名称 node.name: node-1 # 节点名称 path.data: /var/data/elasticsearch # 数据存放路径 path.logs: /var/log/elasticsearch # 日志存放路径 network.host: 0.0.0.0 # 允许所有IP访问生产环境建议设置具体IP http.port: 9200 # 端口号 discovery.seed_hosts: [127.0.0.1] # 集群自动发现节点 discovery.type: single-node #单节点加上这项 #cluster.initial_master_nodes: [node-1] 如果是多节点则加上这项指定默认初始化的主节点如果有内存限制可以修改 JVM 配置文件 /usr/local/elasticsearch/config/jvm.options一般建议设为物理内存的一半且不超过 32GB# 修改 JVM 堆内存大小根据你服务器内存情况调整单位m 或 g -Xms1g -Xmx1g2.3、修改系统相关配置1、修改文件描述符和用户进程限制编辑 /etc/security/limits.conf 文件在末尾添加* soft nofile 65536 * hard nofile 65536 * soft nproc 4096 * hard nproc 40962、修改虚拟内存区域最大数量编辑 /etc/sysctl.conf 文件在末尾添加vm.max_map_count262144完成后执行 sysctl -p 使配置立即生效2.4、启动ES服务切换到 elastic 用户启动服务su elastic cd /usr/local/elasticsearch/bin ./elasticsearch -d2.5、使用 Systemd 管理ES服务开机自启创建服务文件 /etc/systemd/system/elasticsearch.service这样可以用 systemctl 来管理并支持开机自启[Unit] DescriptionElasticsearch [Service] Userelastic ExecStart/usr/local/elasticsearch/bin/elasticsearch Restartalways LimitNOFILE65535 LimitNPROC4096 [Install] WantedBymulti-user.target保存后执行以下命令使之生效:systemctl daemon-reloadsystemctl enable elasticsearchsystemctl start elasticsearch在服务器本地执行以下命令测试curl http://localhost:9200{ name : node-1, cluster_name : my-application, cluster_uuid : _na_, version : { number : 7.10.1, build_flavor : default, build_type : tar, build_hash : 1c34507e66d7db1211f66f3513706fdf548736aa, build_date : 2020-12-05T01:00:33.671820Z, build_snapshot : false, lucene_version : 8.7.0, minimum_wire_compatibility_version : 6.8.0, minimum_index_compatibility_version : 6.0.0-beta1 }, tagline : You Know, for Search }至此ES搭建完成 三、plumeLog-server搭建plumeLog-server的搭建相对笔记简单需要注意的是连接ES的地址要确保正确我们采用最常见的redis队列模式当然你也可以采用kafka作消息中间件。plumeLog-server笔者使用的是3.5的版本。3.1、下载安装包安装下载地址Plumelogunzip plume-log-server-3.5.zipmv plume-log-server-3.5 /usr/local #移动目录3.2、修改application.properties配置spring.application.nameplumelog_server spring.profiles.activetest-confidential server.port8891 spring.thymeleaf.modeLEGACYHTML5 spring.mvc.view.prefixclasspath:/templates/ spring.mvc.view.suffix.html spring.mvc.static-path-pattern/plumelog/** spring.boot.admin.context-pathadmin plumelog.modelredis plumelog.queue.redis.redisHostlocalhost:6379 plumelog.queue.redis.redisPassWord123456 plumelog.queue.redis.redisDb11 plumelog.redis.redisHostlocalhost:6379 plumelog.redis.redisPassWord123456 plumelog.redis.redisDb11 plumelog.es.esHostslocalhost:9200 plumelog.es.shards3 plumelog.es.replicas1 plumelog.es.refresh.interval30s #日志索引建立方式day表示按天、hour表示按照小时 plumelog.es.indexType.modelday plumelog.es.maxShards100000 #ES设置密码,启用下面配置 plumelog.es.userNameelastic plumelog.es.passWordelastic #单次拉取日志条数 plumelog.maxSendSize100 #拉取时间间隔kafka不生效 plumelog.interval100 #plumelog-ui的地址 如果不配置报警信息里不可以点连接 plumelog.ui.urlhttp://demo.plumelog.com #管理密码手动删除日志的时候需要输入的密码 admin.password123456 #日志保留天数,配置0或者不配置默认永久保留 admin.log.keepDays30 #链路保留天数,配置0或者不配置默认永久保留 admin.log.trace.keepDays30 #登录配置配置后会有登录界面 login.usernameadmin login.passwordadmin3.3、启动服务cd /usr/local/plume-log-server-3.5 ./startup.sh查看plumeLog服务日志四、springboot服务集成plumelog4.1、项目引入pom.xml依赖!-- 引入plumeLog -- dependency groupIdcom.plumelog/groupId artifactIdplumelog-logback/artifactId version3.5.3/version /dependency dependency groupIdcom.plumelog/groupId artifactIdplumelog-trace/artifactId version3.5.3/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency4.2、项目logback-spring.xml配置appender nameplumeLog classcom.plumelog.logback.appender.RedisAppender appNametmccloud-merchant/appName redisHost192.168.31.111:6379/redisHost redisAuth123456/redisAuth redisDb11/redisDb envdev/env /appender root levelINFO appender-ref refCONSOLE / appender-ref refASYNC/ appender-ref referrorLog / !-- 加入plumeLog-- appender-ref refplumeLog/ /root4.3、增加TraceId过滤器(Http请求线程打入TraceId)PlumeLogFilterConfig.javaimport com.plumelog.core.TraceId; import com.tingcream.tmccloud.base.worker.WorkerIdUtil; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.MDC; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; public class MyTraceIdFilter extends OncePerRequestFilter { public static String TRACE_IDtraceId; Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { //1、尝试从http请求头中获取traceId 若没有则生成一个traceId String traceId request.getHeader(TRACE_ID); if (traceId null || traceId.isEmpty()) { traceId WorkerIdUtil.getNextSwIdStr(); } try { // 2. 设置到 PlumeLog上下文 及slf4j上下文 TraceId.logTraceID.set(traceId); MDC.put(TRACE_ID, traceId); // 3. 也可以添加到响应头 response.setHeader(TRACE_ID, traceId); // 4. 放行 chain.doFilter(request, response); } finally { // 5. 清理避免内存泄漏 TraceId.logTraceID.remove(); MDC.remove(TRACE_ID); } } }MyTraceIdFilter.javaimport cn.hutool.core.util.IdUtil; import com.plumelog.core.TraceId; import com.tingcream.tmccloud.base.worker.WorkerIdUtil; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; public class MyTraceIdFilter implements Filter { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { try { HttpServletRequest request (HttpServletRequest)servletRequest; String traceId request.getParameter(traceId); if (traceId ! null !.equals(traceId)) { TraceId.logTraceID.set(traceId); } else { //TraceId.set(); // 给当前线程打入traceId long workerId WorkerIdUtil.getWorkerIdByIp(); String id IdUtil.getSnowflake(workerId, 0).nextIdStr(); TraceId.logTraceID.set(id); } } finally { filterChain.doFilter(servletRequest, servletResponse); } } public void destroy() { } }4.4、Resp响应类改造将Resp中增加traceId字段, 方便搜索报错日志信息。Resp响应类import lombok.Getter; import lombok.Setter; Getter Setter public class RespT { public static final int SUCCESS200; public static final int FAILURE500; public static final int ERROR9999; /** * 返回状态码200 操作成功 500操作失败 9999系统异常... */ private Integer code ; /** * 返回消息 */ private String msg ; /** * 日志追踪码Id */ private String traceId; /** * 返回数据 */ private T data; public Resp(Integer code, String msg) { this.code code; this.msg msg; } public Resp(Integer code, String msg,T data) { this.code code; this.msg msg; this.datadata; } public static T RespT success(){ return new Resp(SUCCESS,操作成功) ; } public static T RespT successMsg(String msg){ return new Resp(SUCCESS,msg) ; } public static T RespT success(T data){ return new Resp(SUCCESS,操作成功,data) ; } public static T RespT failure(String msg){ return new Resp(FAILURE,msg) ; } public static T RespT error(String msg){ return new Resp(ERROR,msg) ; } }4.5、新增TraceResponseAdvice返回对象Resp中设置traceIdimport com.plumelog.core.TraceId; import com.tingcream.tmccloud.base.core.Resp; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; RestControllerAdvice public class TraceResponseAdvice implements ResponseBodyAdviceObject { Override public boolean supports(MethodParameter returnType, Class? extends HttpMessageConverter? converterType) { // 只处理 Resp 类型的返回值 return returnType.getParameterType() Resp.class; } Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class? extends HttpMessageConverter? selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body instanceof Resp) { Resp? resp (Resp?) body; // 从 Plumelog 上下文中获取当前请求的 traceId String traceId TraceId.logTraceID.get(); resp.setTraceId(traceId); } return body; } }4.6、其他后台线程设置traceId如xxl-job的定时任务执行并非是用户http请求行为而是后台线程执行这时我们需要手动打入traceId才行。import cn.hutool.core.util.IdUtil; import com.plumelog.core.TraceId; import com.tingcream.tmccloud.base.worker.WorkerIdUtil; import org.slf4j.MDC; import java.util.UUID; /** * 手动打入traceId 例如xxl-job定时任务中的后台线程 */ public class TraceHelper { // MDC 中的 KeyPlumelog 默认会识别 private static final String TRACE_ID_KEY traceId; public static void setTraceId() { long workerId WorkerIdUtil.getWorkerIdByIp(); String traceId IdUtil.getSnowflake(workerId, 0).nextIdStr(); TraceId.logTraceID.set(traceId); // 设置 Plumelog 上下文 MDC.put(TRACE_ID_KEY, traceId); // 设置 MDC方便普通日志打印 } public static void clearTraceId() { TraceId.logTraceID.remove(); MDC.remove(TRACE_ID_KEY); } }五、请求接口查看日志发现无论是正常响应的请求还是异常请求返回结果中都给出了traceId 。开发人员只需要拿着这个id就可以精准地搜索到相关的日志5.1、正常响应请求5.2、异常响应请求5.3、plume日志查看可根据应用名称、环境、追踪码等条件查询 当然我们直接输入追踪码查询才是最方便直接的六、扩展 雪花算法中workerId怎么取请注意 我们项目中生成traceId 使用的是雪花算法雪花算法中最重要的概念是workerId、datacenterId。那么我们该怎么得到服务的workerId呢首先指出直接在nacos配置死服务的workerId是不对的 这样做服务的所有实例都是相同的workerId了不具有区分性。最好的做法是根据服务所在的物理网络环境生成workerId 我们也可根据服务所在机器的ip、主机名 或docker容器id及容器名称等 得到workerId。以下是笔者生成workerId的简单实现 根据机器ipimport java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; public class WorkerIdUtil { /** * 根据IP地址生成workerId * 策略取IPv4地址的最后两段计算哈希后映射到0-31范围 */ public static long getWorkerIdByIp() { try { String ip getLocalIpAddress(); // 例如192.168.31.1 - 取最后两段 31.1 String[] ipParts ip.split(\\.); if (ipParts.length 4) { String lastTwo ipParts[2] . ipParts[3]; // 计算哈希并映射到0-31范围 return Math.abs(lastTwo.hashCode()) % 32; } // 备用方案使用完整IP的哈希 return Math.abs(ip.hashCode()) % 32; } catch (Exception e) { // 降级方案返回默认值 return 1L; } } /** * 获取本机IPv4地址优先取eth0网卡 */ private static String getLocalIpAddress() throws Exception { EnumerationNetworkInterface interfaces NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface ni interfaces.nextElement(); // 跳过回环地址和未启用的网卡 if (ni.isLoopback() || !ni.isUp()) { continue; } EnumerationInetAddress addresses ni.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress addr addresses.nextElement(); if (!addr.isLoopbackAddress() addr.isSiteLocalAddress()) { return addr.getHostAddress(); } } } // 降级获取本地主机地址 return InetAddress.getLocalHost().getHostAddress(); } }

相关新闻