
阿里云日志服务
-
2023年3月18日发(作者:雨花英烈精神)⽇志分析系统分类有哪些_Java开发⽇志规范
打印⽇志是⼀门艺术,但长期被开发同学所忽视。⽇志就像车辆保险,没⼈愿意为保险付钱,但是⼀旦出了问题都⼜想有保险可⽤。
我们打印⽇志的时候都很随意,可是⽤的时候会吐槽各种SB包括⾃⼰!写好每⼀条⽇志吧,与君共勉
1、⽇志
1.1⽇志是什么?
⽇志,维基百科的定义是记录服务器等电脑设备或软件的运作。
⽇志⽂件提供精确的系统记录,根据⽇志最终定位到错误详情和根源。⽇志的特点是,它描述⼀些离散的(不连续的)事件。例如:应⽤通过
⼀个滚动的⽂件输出INFO或ERROR信息,并通过⽇志收集系统,存储到⼀些存储引擎(Elasticsearch)中⽅便查询。
1.2⽇志有什么⽤?
在上⽂中我们解释了⽇志的作⽤是提供精准的系统记录⽅便根因分析。那么具体在哪些具体⽅⾯它可以发挥作⽤?
打印调试:即可以⽤⽇志来记录变量或者某⼀段逻辑。记录程序运⾏的流程,即程序运⾏了哪些代码,⽅便排查逻辑问题。
问题定位:程序出异常或者出故障时快速的定位问题,⽅便后期解决问题。因为线上⽣产环境⽆法debug,在测试环境去模拟⼀套⽣
产环境,费时费⼒。所以依靠⽇志记录的信息定位问题,这点⾮常重要。还可以记录流量,后期可以通过ELK(包括EFK进⾏流量统
计)。
⽤户⾏为⽇志:记录⽤户的操作⾏为,⽤于⼤数据分析,⽐如监控、风控、推荐等等。这种⽇志,⼀般是给其他团队分析使⽤,⽽且可
能是多个团队,因此⼀般会有⼀定的格式要求,开发者应该按照这个格式来记录,便于其他团队的使⽤。当然,要记录哪些⾏为、操
作,⼀般也是约定好的,因此,开发者主要是执⾏的⾓⾊。
根因分析(甩锅必备):即在关键地⽅记录⽇志。⽅便在和各个终端定位问题时,别⼈说时你的程序问题,你可以理直⽓壮的拿出你的⽇
志说,看,我这⾥运⾏了,状态也是对的。这样,对⽅就会乖乖去定位他的代码,⽽不是互相推脱。
1.3什么时候记录⽇志?
上⽂说了⽇志的重要性,那么什么时候需要记录⽇志。
系统初始化:系统或者服务的启动参数。核⼼模块或者组件初始化过程中往往依赖⼀些关键配置,根据参数不同会提供不⼀样的服务。
务必在这⾥记录INFO⽇志,打印出参数以及启动完成态服务表述。
编程语⾔提⽰异常:如今各类主流的编程语⾔都包括异常机制,业务相关的流⾏框架有完整的异常模块。这类捕获的异常是系统告知开
发⼈员需要加以关注的,是质量⾮常⾼的报错。应当适当记录⽇志,根据实际结合业务的情况使⽤WARN或者ERROR级别。
业务流程预期不符:除开平台以及编程语⾔异常之外,项⽬代码中结果与期望不符时也是⽇志场景之⼀,简单来说所有流程分⽀都可以
加⼊考虑。取决于开发⼈员判断能否容忍情形发⽣。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等
等。
系统核⼼⾓⾊,组件关键动作:系统中核⼼⾓⾊触发的业务动作是需要多加关注的,是衡量系统正常运⾏的重要指标,建议记录INFO
级别⽇志,⽐如电商系统⽤户从登录到下单的整个流程;微服务各服务节点交互;核⼼数据表增删改;核⼼组件运⾏等等,如果⽇志频
度⾼或者打印量特别⼤,可以提炼关键点INFO记录,其余酌情考虑DEBUG级别。
第三⽅服务远程调⽤:微服务架构体系中有⼀个重要的点就是第三⽅永远不可信,对于第三⽅服务远程调⽤建议打印请求和响应的参
数,⽅便在和各个终端定位问题,不会因为第三⽅服务⽇志的缺失变得⼿⾜⽆措。
2、⽇志打印
2.1Slf4j&Logback
Slf4j英⽂全称为“SimpleLoggingFacadeforJava”,为Java提供的简单⽇志门⾯。Facade门⾯,更底层⼀点说就是接⼝。它允
许⽤户以⾃⼰的喜好,在⼯程中通过Slf4j接⼊不同的⽇志系统。
Logback是Slf4j的原⽣实现框架,同样也是出⾃Log4j⼀个⼈之⼿,但拥有⽐Log4j更多的优点、特性和更做强的性能,Logback相
对于Log4j拥有更快的执⾏速度。基于我们先前在Log4j上的⼯作,Logback重写了内部的实现,在某些特定的场景上⾯,甚⾄可以⽐
之前的速度快上10倍。在保证Logback的组件更加快速的同时,同时所需的内存更加少。
2.2⽇志⽂件
⽇志⽂件放置于固定的⽬录中,按照⼀定的模板进⾏命名,推荐的⽇志⽂件名称:
当前正在写⼊的⽇志⽂件名:[-].log如:已经滚⼊历史的⽇志⽂件名:[-].yyyy-MM-dd-hh.[滚动号].log如:example-server-b
2.3⽇志变量定义
推荐使⽤lombok(代码⽣成器)注解@4j来⽣成⽇志变量实例。
tlomboklombok1.18.10provided
代码⽰例
4j;@Slf4jpublicclassLogTest{publicstaticvoidmain(String[]args){("thisislogtest");}}
2.4⽇志配置
⽇志记录采⽤分级记录,级别与⽇志⽂件名相对应,不同级别的⽇志信息记录到不同的⽇志⽂件中。如有特殊格式⽇志,如accesslog,
单独使⽤⼀个⽂件,请注意避免重复打印(可使⽤additivity="false"避免)。
2.5参数占位格式
使⽤参数化形式{}占位,[]进⾏参数隔离,这样的好处是可读性更⾼,⽽且只有真正准备打印的时候才会处理参数。
//正确⽰例,必须使⽤参数化信息的⽅式("orderispayingwithuserId:[{}]andorderId:[{}]",userId,orderId);//错误⽰例,不要进⾏字符串拼接,那样会产⽣很
2.6⽇志的基本格式
2.6.1⽇志时间
作为⽇志产⽣的⽇期和时间,这个数据⾮常重要,⼀般精确到毫秒。
yyyy-MM-ddHH:mm:
2.6.2⽇志级别
⽇志的输出都是分级别的,不同的设置不同的场合打印不同的⽇志。
主要使⽤如下的四个级别:
DEBUG:DEUBG级别的主要输出调试性质的内容,该级别⽇志主要⽤于在开发、测试阶段输出。该级别的⽇志应尽可能地详尽,开
发⼈员可以将各类详细信息记录到DEBUG⾥,起到调试的作⽤,包括参数信息,调试细节信息,返回值信息等等,便于在开发、测试
阶段出现问题或者异常时,对其进⾏分析。
INFO:INFO级别的主要记录系统关键信息,旨在保留系统正常⼯作期间关键运⾏指标,开发⼈员可以将初始化系统配置、业务状态变
化信息,或者⽤户业务流程中的核⼼处理记录到INFO⽇志中,⽅便⽇常运维⼯作以及错误回溯时上下⽂场景复现。建议在项⽬完成
后,在测试环境将⽇志级别调成INFO,然后通过INFO级别的信息看看是否能了解这个应⽤的运⽤情况,如果出现问题后是否这些⽇
志能否提供有⽤的排查问题的信息。
WARN:WARN级别的主要输出警告性质的内容,这些内容是可以预知且是有规划的,⽐如,某个⽅法⼊参为空或者该参数的值不满
⾜运⾏该⽅法的条件时。在WARN级别的时应输出较为详尽的信息,以便于事后对⽇志进⾏分析。
ERROR:ERROR级别主要针对于⼀些不可预知的信息,诸如:错误、异常等,⽐如,在catch块中抓获的⽹络通信、数据库连接等
异常,若异常对系统的整个流程影响不⼤,可以使⽤WARN级别⽇志输出。在输出ERROR级别的⽇志时,尽量多地输出⽅法⼊参
数、⽅法执⾏过程中产⽣的对象等数据,在带有错误、异常对象的数据时,需要将该对象⼀并输出。
2.6.3DEBUG/INFO的选择
DEBUG级别⽐INFO低,包含调试时更详细的了解系统运⾏状态的东西,⽐如变量的值等等,都可以输出到DEBUG⽇志⾥。INFO是在
线⽇志默认的输出级别,反馈系统的当前状态给最终⽤户看的。输出的信息,应该对最终⽤户具有实际意义的。从功能⾓度上说,INFO输
出的信息可以看作是软件产品的⼀部分,所以需要谨慎对待,不可随便输出。如果这条⽇志会被频繁打印或者⼤部分时间对于纠错起不到作
⽤,就应当考虑下调为DEBUG级别。
由于DEBUG⽇志打印量远⼤于INFO,出于前⽂⽇志性能的考虑,如果代码为核⼼代码,执⾏频率⾮常⾼,务必推敲⽇志设计是否合
理,是否需要下调为DEBUG级别⽇志。
注意⽇志的可读性,不妨在写完代码review这条⽇志是否通顺,能否提供真正有意义的信息。
⽇志输出是多线程公⽤的,如果有另外⼀个线程正在输出⽇志,上⾯的记录就会被打断,最终显⽰输出和预想的就会不⼀致。
2.6.4WARN/ERROR的选择
当⽅法或者功能处理过程中产⽣不符合预期结果或者有框架报错时可以考虑使⽤,常见问题处理⽅法包括:
增加判断处理逻辑,尝试本地解决:增加逻辑判断吞掉报警永远是最优选择抛出异常,交给上层逻辑解决
抛出异常,交给上层逻辑解决
记录⽇志,报警提醒
使⽤返回码包装错误做返回
⼀般来说,WARN级别不会短信报警,ERROR级别则会短信报警甚⾄电话报警,ERROR级别的⽇志意味着系统中发⽣了⾮常严重的问
题,必须有⼈马上处理,⽐如数据库不可⽤,系统的关键业务流程⾛不下去等等。错误的使⽤反⽽带来严重的后果,不区分问题的重要程
度,只要有问题就error记录下来,其实这样是⾮常不负责任的,因为对于成熟的系统,都会有⼀套完整的报错机制,那这个错误信息什么
时候需要发出来,很多都是依据单位时间内ERROR⽇志的数量来确定的。
2.6.5强调ERROR报警
ERROR级别的⽇志打印通常伴随报警通知。ERROR的报出应该伴随着业务功能受损,即上⾯提到的系统中发⽣了⾮常严重的问题,
必须有⼈马上处理。
ERROR⽇志⽬标
给处理者直接准确的信息:ERROR信息形成⾃⾝闭环。
问题定位:
发⽣了什么问题,哪些功能受到影响
获取帮助信息:直接帮助信息或帮助信息的存储位置
通过报警知道解决⽅案或者找何⼈解决
2.6.6线程名称
输出该⽇志的线程名称,⼀般在⼀个应⽤中⼀个同步请求由同⼀线程完成,输出线程名称可以在各个请求产⽣的⽇志中进⾏分类,便于分清
当前请求上下⽂的⽇志。
2.6.7opentracing标识
在分布式应⽤中,⽤户的⼀个请求会调⽤若⼲个服务完成,这些服务可能还是嵌套调⽤的,因此完成⼀个请求的⽇志并不在⼀个应⽤的⽇志
⽂件,⽽是分散在不同服务器上不同应⽤节点的⽇志⽂件中。该标识是为了串联⼀个请求在整个系统中的调⽤⽇志。
唯⼀字符串(traceid)
调⽤层级(spanid)
通过搜索traceid就可以查到这个traceid标识的请求在整个系统中流转(处理)过程中产⽣的所有⽇志。
2.6.8biz标识
在业务开发中,我们的⽇志都是和业务相关联的,有时候是需要根据⽤户或者业务做聚类的,因此⼀次请求如果可以通过某项标识做聚类的
时候,可以将聚类标识打印到⽇志中。
⽤户标识(userid)
业务标识(bizid)
2.6.9⽇志记录器名称
⽇志记录器名称⼀般使⽤类名,⽇志⽂件中可以输出简单的类名即可,看实际情况是否需要使⽤包名和⾏号等信息。主要⽤于看到⽇志后到
哪个类中去找这个⽇志输出,便于定位问题所在。
2.6.10⽇志内容
禁⽤n和n
变参替换⽇志拼接
输出⽇志的对象,应在其类中实现快速的toString⽅法,以便于在⽇志输出时仅输出这个对象类名和hashCode
预防空指针:不要在⽇志中调⽤对象的⽅法获取值,除⾮确保该对象肯定不为null,否则很有可能会因为⽇志的问题⽽导致应⽤产⽣空指针
异常。
2.6.11异常堆栈
异常堆栈⼀般会出现在ERROR或者WARN级别的⽇志中,异常堆栈含有⽅法调⽤链的系统,以及异常产⽣的根源。异常堆栈的⽇志属于
上⼀⾏⽇志的,在⽇志收集时需要将其划⾄上⼀⾏中。
2.7最佳实践
2.7.1⽇志格式2019-12-0100:00:00.000|pid|log-level|[svc-name,trace-id,span-id,user-
id,biz-id]|thread-name|-name:logmessage
时间
pid,pid
log-level,⽇志级别
svc-name,应⽤名称
trace-id,调⽤链标识
span-id,调⽤层级标识
user-id,⽤户标识
biz-id,业务标识
thread-name,线程名称
-name,⽇志记录器名称
logmessage,⽇志消息体
2.7.2⽇志模块扩展
⽇志模块是基于以下技术点做扩展的。
Slf4jMDC实现原理(暂不开展详解,如有兴趣私下沟通)
OpentracingScope原理(暂不开展详解,如有兴趣私下沟通)
在每个tracing链路中,将OpentracingScope中的上下⽂信息放置MDC中,根据SpringBootLogging扩展接⼝扩展的取值逻辑
的取值逻辑。
相关源码参考:
SpringCloudSleuth:
修改logback配置⽂件中每个appender的pattern为以下默认值即可实现标准化。
%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-ddHH:mm:}}|${PID:-}|%level|${LOG_LEVEL_PATTERN:-%5p}|%t|%-40.40logger{39}:%msg%n
节选
${LOG_PATH}/${APP_NAME}-${LOG_PATH}/${APP_NAME}-info.%d{yyyy-MM-dd-HH}.%481GB20GB${LOG_PATTERN}INFOACCEPTDENY
代码使⽤⽰例:
@OverridepublicResult>page(@RequestParam(value="page-num",defaultValue="1")intpageNum,@RequestParam(value="
⽇志记录
2019-12-0416:29:08.223|43546|INFO|[example-server-book-service,ac613cff04bac8b1,4a9adc10fdf0eb5,userId123,bizId321]|XNIO-1task-4|rent.S
3、⽇志服务
3.1SLS阿⾥云⽇志服务
阿⾥云⽇志服务(简称SLS)是针对⽇志类数据的⼀站式服务,在阿⾥巴巴集团经历⼤量⼤数据场景锤炼⽽成。您⽆需开发就能快捷完成⽇志
数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建⽴DT时代海量⽇志处理能⼒。
project
项⽬、管理⽇志基础单元,服务⽇志建议⼀个环境建为⼀个Project,这样⽇志记录是整体⼀个闭环,⽇志记录随整个环境内的服务
调⽤产⽣。
logstore
⽇志库,⽇志库建议按照⽇志类型分为不同的,如特定格式的access⽇志,以及info/warn/error⽇志,特定格式可以配置更为
⽅⾯的索引以及告警设置。
注意:请勿按照应⽤服务区分为不同的logstore,在微服务架构中,⼀次请求交叉了多个应⽤服务,⽇志是散落在各个应⽤服务中的,按
照服务区分logstore,需要开发同学⼗分了解应⽤运⾏状况和调⽤拓扑图,这点往往是不具备的。
实时采集与消费
功能:
通过ECS、容器、移动端、开源软件、JS等接⼊实时⽇志数据(例如Metric、Event、BinLog、TextLog、Click等)。
提供实时消费接⼝,与实时计算及服务对接。
⽤途:数据清洗(ETL)、流计算(StreamCompute)、监控与报警、机器学习与迭代计算。
查询分析
实时索引、查询分析数据。
查询:关键词、模糊、上下⽂、范围。
统计:SQL聚合等丰富查询⼿段。
可视化:Dashboard+报表功能。
对接:Grafana、JDBC/SQL92。
⽤途:DevOps/线上运维,⽇志实时数据分析,安全诊断与分析,运营与客服系统。
消费投递
稳定可靠的⽇志投递。将⽇志中枢数据投递⾄存储类服务进⾏存储。⽀持压缩、⾃定义Partition、以及⾏列等各种存储⽅式。
⽤途:数据仓库+数据分析、审计、推荐系统与⽤户画像。
告警
⽇志服务的告警功能基于仪表盘中的查询图表实现。在⽇志服务控制台查询页⾯或仪表盘页⾯设置告警规则,并指定告警规则的配置、检查
条件和通知⽅式。设置告警后,⽇志服务定期对仪表盘的查询结果进⾏检查,检查结果满⾜预设条件时发送告警通知,实现实时的服务状态
监控。
最佳实践
阿⾥云的⽇志服务功能相当强⼤,想⽤好⽇志服务可以参考:
3.2ELK通⽤⽇志解决⽅案
ELK是Elasticsearch、Logstash、Kibana三⼤开源框架⾸字母⼤写简称。市⾯上也被成为ElasticStack。其中Elasticsearch是⼀个
基于Lucene、分布式、通过Restful⽅式进⾏交互的近实时搜索平台框架。像类似百度、⾕歌这种⼤数据全⽂搜索引擎的场景都可以使⽤
Elasticsearch作为底层⽀持框架,可见Elasticsearch提供的搜索能⼒确实强⼤,市⾯上很多时候我们简称Elasticsearch为es。
Logstash是ELK的中央数据流引擎,⽤于从不同⽬标(⽂件/数据存储/MQ)收集的不同格式数据,经过过滤后⽀持输出到不同⽬的地(⽂
件/MQ/Redis/Elasticsearch/Kafka等)。Kibana可以将Elasticsearch的数据通过友好的页⾯展⽰出来,提供实时分析的功能。
4、实践说明
普通格式⽇志
2019-11-2615:01:03.332|1543|INFO|[example-server-book-service,28f019d57b8336ab,630697c7f34ca4fa,105,45982043|XNIO-1task-42]|
普通⽇志前缀是固定的,可以固定分词索引,⽅便更快的查询分析。
特定格式⽇志
以access⽇志为例
2019-11-2615:01:03.332|1543|INFO|[example-server-book-service,28f019d57b8336ab,630697c7f34ca4fa,105,45982043|XNIO-1task-42]|g
特定格式⽇志可按格式创建索引更⽅便聚焦查询分析和告警,如根据take-time,http-status,biz-code等值。
参考⽂献
Java⽇志记录最佳实践:/p/546e9aace657)
别在Java代码⾥乱打⽇志了,这才是打印⽇志的正确姿势!:/s/hJvkRlt9xQbWhYy1G7ZDsw
阿⾥云⽇志服务:/product/?spm=a2c4g.11186623.3.1.7cfd735dv8i1pB
SpringBootLogging:/spring-boot/docs/E/reference/html/spring-boot-
#boot-features-logging
SpringCloudSleuth:/spring-cloud/spring-cloud-sleuth
Opentracing:/opentracing