五年Java经验,面试还是说不出日志该怎么写更好?
connygpt 2024-10-12 16:58 6 浏览
概览
各位小伙伴大家好,我又回来更新了,上一篇我们讨论了为什么要使用日志框架,这次我们深入问题的根源,为什么我们需要日志,日志具体如何使用?
大多数开发人员会纠结日志该怎么输出,什么时候输出,输出了会不会有人看等问题,让我们跳出开发人员的局限来考虑这个问题:谁需要日志?日志有几种?日志都需要输出什么?如何输出日志?
谁需要日志?
- 开发者 开发人员在开发过程中需要输出一些变量方便调试,正确的做法是使用日志来输出(使用 System.out来输出,一不小心发布到线上,会被项目经理痛批);其次线上问题很难重放,用户的表述一般都会失真,况且很多用户发现 bug 就删 app 关网页走人了
- 运维人员 整个系统大部分时间都是运维人员来维护,日志可以帮助运维人员来了解系统状态(很多运维系统接入的也是日志),运维人员发现日志有异常信息也可以及时通知开发来排查
- 运营人员 没错,就是运营人员,比如电商的转化率、视频网站的完播率、普通PV数据等都可以通过日志进行统计,随着大数据技术的普及,这部分日志占比也越来越高
- 安全人员 虽然大多数企业不重视安全,但是安全也可以通过日志来进行预警,比如某个用户突然大额转账、再比如数据库突然出现大量无条件分页查库(拖库)等等
日志有几种?
- 调试日志 用于开发人员开发或者线上回溯问题。
- 诊断日志 一般用于运维人员监控系统与安全人员分析预警。
- 埋点日志 一般用于运营决策分析,也有用作微服务调用链路追踪的(运维、调试)。
- 审计日志 与诊断日志类似,诊断日志偏向运维,审计日志偏向安全。
日志都需要输出什么?
注:日志级别会在下面讲解
- 调试日志 DEBUG 或者 TRACE 级别,比如方法调用参数,网络连接具体信息,一般是开发者调试程序使用,线上非特殊情况关闭这些日志 INFO 级别,一般是比较重要却没有风险的信息,如初始化环境、参数,清理环境,定时任务执行,远程调用第一次连接成功 WARN 级别,有可能有风险又不影响系统继续执行的错误,比如系统参数配置不正确,用户请求的参数不正确(要输出具体参数方便排查),或者某些耗性能的场景,比如一次请求执行太久、一条 SQL 执行超过两秒,某些第三方调用失败,不太可能被运行的if分支等 ERROR 级别,用于程序出错打印堆栈信息,不应该用于输出程序问题之外的其他信息,需要注意打印了日志异常(Exception)就不应该抛(throw)了
- 诊断日志 一般输出 INFO 级别,请求响应时间,内存占用等等,线上接入监控系统时打开,建议输出到独立的文件,可以考虑 JSON 格式方便外部工具分析
- 埋点日志 业务按需定制,比如上文提到的转化率可以在用户付款时输出日志,完播率可以在用户播放完成后请求一次后台输出日志,一般可输出 INFO 级别,建议输出到独立的文件,可以考虑JSON格式方便外部工具分析
- 审计日志 大多 WARN 级别或者 INFO 级别,一般是敏感操作即可输出,登陆、转账付款、授权消权、删除等等,建议输出到独立的文件,可以考虑JSON格式方便外部工具分析
一般调试日志由开发者自定义输出,其他三种应该根据实际业务需求来定制。
日志的其他注意点
- 线上日志应尽量谨慎,要思考:这个位置输出日志能帮助排除问题吗?输出的信息与排查问题相关吗?输出的信息足够排除问题吗?做到不少输出必要信息,不多输出无用信息(拖慢系统,淹没有用信息)
- 超级 SessionId 与 RequestId,无论是单体应用还是微服务架构,应该为每个用户每次登陆生成一个超级 SessionId,方便跟踪区分一个用户;RequestId,每次请求生成一个 RequestId,用于跟踪一次请求,微服务也可以用于链路追踪
- 日志要尽量单行输出,一条日志输出一行,否则不方便阅读以及其他第三方系统或者工具分析
- 公司内部应该制定一套通用的日志规范,包括日志的格式,变量名(驼峰、下划线),分隔符(“=”或“:”等),何时输出(比如规定调用第三方前后输出INFO日志),公司的日志规范应该不断优化、调整,找到适合公司业务的最佳规范
OK,理论就聊到这里,接下来让我们回到技术层面。
使用概念
如果要想要学会使用日志框架,先要理解几个简单概念,Logger、Appenders、Layout、日志级别与级别继承(Level Inheritance)
Logger(日志实例)
用于输出日志,调用一次org.slf4j.LoggerFactory#getLogger(java.lang.Class<?>)或org.slf4j.LoggerFactory#getLogger(java.lang.String)就会产生一个日志实例,相同参数会共用同一个实例。
Appenders
日志输出器,logback 预定义了输出到控制台、文件、Socket 服务器、MySQL、PostgreSQL、Oracle 和其他数据库、JMS 和 UNIX Syslog 系统调用等实现,通过配置文件配置即可使用,当然我们常用的只有控制台和文件两种。
Layout
用于控制日志输出格式,前文所说的”自动输出日志相关信息,如:日期、线程、方法名称等等“就可以用 Layout 来控制,实际使用很简单,写一个 Layout 格式定义表达式(pattern)即可,使用方法类似于Java 的SimpleDateFomat。
日志级别
RFC 5424 (page 11)规定了 8 种日志级别,但是SLF4j 只定义了 5 种日志级别,分别是 ERROR、WARN、INFO、DEBUG、TRACE 这五个级别从高到低,配置级别越高日志输出就越少,如下图
我们看到滑动条上五个点正好对应五个级别,滑动指示器可以左右移动,指示器作为分界点,指示器左侧都可以输出,右侧都不能输出,左右调整指示器就可以调整日志的输出,滑倒右侧就可以全部输出,滑倒左侧就可以减少输出,那么是否能够彻底关闭输出呢?答案是可以的,配置文件中还可以配置为 ALL 与 OFF,分别对应所有(等价于TRACE)与关闭。
级别继承
理解了日志级别,让我们来考虑如下场景:
- 某些重要业务输出 INFO 级别,其他业务输出WARN级别的日志,同时关闭所有库、框架的日志
有需求就会有解决方案,其实很简单,logback 与 log4j 都支持按照日志实例来配置,现在问题解决了,但是新的问题又来了,如果线上所有日志都输出 INFO 级别,难道要一个一个配置吗?这时候就就要请出我们上面所提到的级别继承,如果 Java 一样,logback 与 log4j 中也都是单根继承模型,Java 中是 Object,日志中是 ROOT,如下图:
有了继承机制,我们只需要将 ROOT 调整到 INFO 级别,再按照需求细化调整我们业务对应的 logger 实例级别即可满足绝大多数场景。
Codding 实战
问:把大象装冰箱分几步?分三步:1、引入依赖,2、编码输出日志,3、调整配置文件。前文已经讲过步骤一,如果没有看过的读者请移步公众号查看往期回顾,这里直接进入步骤二。
步骤二
如果项目中使用了Lombok,那么可以直接在类上面加@Slf4j注解既可获得日志实例,否则可以使用static final org.slf4j.Logger logger = LoggerFactory.getLogger(TestLog.class);来获取日志实例
具体日志输出方法如下:
logger.trace("A TRACE Message");
logger.debug("A DEBUG Message");
logger.info("An INFO Message");
logger.warn("A WARN Message");
logger.error("An ERROR Message");
复制代码
这里有个注意点,尽量使用参数占位而不要手动拼接字符串,如下
String level = "Trace";
// 反例
logger.trace("A " + level + " Message");
// 正确的做法
logger.trace("A {} Message", level);
复制代码
这样做可以提高效率,如果不输出日志,第一种情况也会拼接字符串造成性能损耗,第二种就不会有此问题(阿里巴巴Java开发手册(华山版)这里表述有问题,占位符效率更高是因为尽量延迟进行字符串处理,如果不需要输出的日志就不处理了,下一篇原理分析会展开),另外我们也不需要if (logger.isTraceEnabled())来进行判断了(性能损耗不高,但是代码好看多了)。
步骤三
配置文件需要区分 logback 与 log4j2,两种框架在配置文件上有差别但很相似,来看具体配置文件。
logback 配置文件位置
logback 支持 xml 与 groovy 脚本两种配置方式,logback 查找配置文件位置规则如下(后续文章会讲如何修改位置)
- logback 尝试在类路径中找到一个名为 logback-test.xml 的文件。
- 如果找不到此类文件,则 logback 会尝试在类路径中找到名为 logback.groovy 的文件。
- 如果找不到这样的文件,它将在类路径中检查文件 logback.xml。
- 如果找不到此类文件,则通过查找文件 META-INF\services\ch.qos.logback.classic.spi.Configurator,如果有这个文件且内容是 com.qos.logback.classic.spi.Configurator 实现类的全类名,直接加载这个实现类。
- 如果以上方法均不能成功执行,则 logback 会使用 BasicConfigurator 自动进行自我配置,会将日志输出到控制台。
这段长长的文字其实不用看,我们就把 logback.xml 放入 Classpath 根目录就可以了。。
logback 配置文件编写规则
logback 配置文件主要分为三类,一个或多个 Appender,用于定义输出位置(不同文件位置,或者网络又或者数据库);一个或多个 Logger,用于细化配置不同 logger 的输出级别以及位置;一个 ROOT,是一个特殊的logger,用于配置根 Logger。
我们一起来看下面的配置文件实例
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" debug="false">
<!-- 定义日志文件的存储地 -->
<property name="LOG_PATH" value="/var/log"/>
<property name="CONSOLE_LOG_PATTERN"
value="%d{HH:mm:ss.SSS} %-5level [%10.10thread] %-30.30logger{29}\(%4L\\) - %msg%n"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<!-- 文件日志格式(打印日志,不打印行号) -->
<property name="FILE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%10.10thread] %-30.30logger{29} - %msg%n"/>
<appender name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${LOG_PATH}/log.log</file>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- yyyy-MM-dd 按日滚动 -->
<fileNamePattern>${LOG_PATH}/log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 单个文件最大50M -->
<maxFileSize>50MB</maxFileSize>
<!-- 最多占用5G磁盘空间,500个文件(总共不能超过该5G) -->
<maxHistory>500</maxHistory>
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>
<!-- 追加方式记录日志 -->
<append>true</append>
<!-- 日志文件的格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<!-- 日志输出级别 STDOUT:控制台;FILE_ALL:文件 -->
<root level="warn">
<appender-ref ref="STDOUT"/>
</root>
<logger name="druid.sql" level="warn" additivity="true"/>
<logger name="druid.sql.ResultSet" level="warn" additivity="true"/>
<logger name="com.alibaba.druid.pool.DruidDataSource" level="debug" additivity="true">
<appender-ref ref="FILE_ALL"/>
</logger>
</configuration>
复制代码
上面配置文件定义了两个 Appender,一个输出控制台,另一个输出到文件并且自动滚动。需注意的是property标签相当于定义一个变量,可以使用${xxx}进行引用,CONSOLE_LOG_PATTERN 与 FILE_LOG_PATTERN 定义了控制台与文件打印格式,具体编写方式类似于 Java 的SimpleDateFomat就不在此展开了,具体可以参考
- logback:logback.qos.ch/manual/layo…
- log4j:logging.apache.org/log4j/2.x/m…
log4j2 配置文件位置
log4j2 支持 XML、JSON、YAML 或者 properties 格式的配置文件,具体查找方式如下:
- 检查“ log4j.configurationFile”系统属性,如果有,尝试使用与文件扩展名匹配的ConfigurationFactory加载配置。
- 如果未设置系统属性,则属性 ConfigurationFactory 将在类路径中查找 log4j2-test.properties。
- 如果找不到此类文件,则 YAML ConfigurationFactory将在类路径中查找 log4j2-test.yaml或log4j2-test.yml。
- 如果找不到此类文件,则 JSON ConfigurationFactory 将在类路径中查找 log4j2-test.json或log4j2-test.jsn。
- 如果找不到这样的文件,XML ConfigurationFactory 将在类路径中查找 log4j2-test.xml。
- 如果找不到测试文件,则属性 ConfigurationFactory 将在类路径上查找 log4j2.properties。
- 如果找不到属性文件,则 YAML ConfigurationFactory 将在类路径上查找 log4j2.yaml或log4j2.yml。
- 如果无法找到 YAML 文件,则 JSON ConfigurationFactory 将在类路径上查找 log4j2.json或log4j2.jsn。
- 如果无法找到 JSON 文件,则 XML ConfigurationFactory 将尝试在类路径上找到 log4j2.xml。
- 如果找不到配置文件,使用 DefaultConfiguration 自动配置,将日志输出到控制台。
这段更长的文字当然也不用看,我们就把 log4j2.xml 放入 Classpath 根目录就可以了
log4j2 配置文件编写
log4j 也是 Logger 与 Appender 配置项,也有一个ROOT的特殊 Logger,Appender 比logback支持更多输出位置,如kafka、Cassandra、Flume等。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" strict="true">
<!-- 定义变量,可以被${xxx}引用 -->
<Properties>
<Property name="baseDir">logs</Property>
</Properties>
<!-- 定义 Appenders 用来指定输出位置 -->
<Appenders>
<!-- 日志滚动 ${date:yyyy-MM}:按月滚动文件夹 按小时、文件序号滚动,每次滚动都使用gz压缩 -->
<RollingFile name="RollingFile" fileName="${baseDir}/log.log"
filePattern="${baseDir}/${date:yyyy-MM}/log-%d{yyyy-MM-dd-HH}-%i.log.gz">
<!-- 日志格式 -->
<PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
<Policies>
<!-- 时间滚动(按月滚动目录,按小时滚动文件) -->
<TimeBasedTriggeringPolicy/>
<!-- 文件大小滚动(1小时内超过250M,强制滚动一次) -->
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
<!-- 每天最多100个文件 -->
<DefaultRolloverStrategy max="100">
<!-- 删除策略,超过三十天删除,如果总文件小于100G,文件数量小于10个,则不会被删除 -->
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*/app-*.log.gz">
<IfLastModified age="30d">
<IfAny>
<IfAccumulatedFileSize exceeds="100 GB"/>
<IfAccumulatedFileCount exceeds="10"/>
</IfAny>
</IfLastModified>
</IfFileName>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<Loggers>
<!-- 多个logger -->
<Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
<AppenderRef ref="RollingFile"/>
</Logger>
<!-- 一个ROOT -->
<Root level="trace">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
复制代码
可以看得出 log4j2 与 logback 配置文件书写大同小异,甚至同样需要注意additivity="true"时导致的日志重复输出问题,毕竟 log4j1 与 logback 都是 Ceki大神都作品。
总结
得益于 Ceki 大佬的努力,日志使用几乎没有有差异(Logback 与 Log4j2,Google 于 2018年4月开源了流式(fluent)日志框架 Flogger,Slf4j 也将在 2.0 版本支持,而 log4j2 再次落后,不过笔者认为 log4j2 有很多优点,更多内容请关注后续文章)。关于日志如何输出本人也是经验之谈,免不了纰漏,欢迎补充指正,另外每个公司都有不同的应用场景,具体应该遵守公司统一规范。
本篇更多倾向基础使用,接下来的文章将展开对比、原理以及扩展日志框架,敬请各位小伙伴们期待。
作者:鸡员外链接:https://juejin.im/post/5e1472776fb9a047f42e659c
相关推荐
- 3分钟让你的项目支持AI问答模块,完全开源!
-
hello,大家好,我是徐小夕。之前和大家分享了很多可视化,零代码和前端工程化的最佳实践,今天继续分享一下最近开源的Next-Admin的最新更新。最近对这个项目做了一些优化,并集成了大家比较关注...
- 干货|程序员的副业挂,12个平台分享
-
1、D2adminD2Admin是一个完全开源免费的企业中后台产品前端集成方案,使用最新的前端技术栈,小于60kb的本地首屏js加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助...
- Github标星超200K,这10个可视化面板你知道几个
-
在Github上有很多开源免费的后台控制面板可以选择,但是哪些才是最好、最受欢迎的可视化控制面板呢?今天就和大家推荐Github上10个好看又流行的可视化面板:1.AdminLTEAdminLTE是...
- 开箱即用的炫酷中后台前端开源框架第二篇
-
#头条创作挑战赛#1、SoybeanAdmin(1)介绍:SoybeanAdmin是一个基于Vue3、Vite3、TypeScript、NaiveUI、Pinia和UnoCSS的清新优...
- 搭建React+AntDeign的开发环境和框架
-
搭建React+AntDeign的开发环境和框架随着前端技术的不断发展,React和AntDesign已经成为越来越多Web应用程序的首选开发框架。React是一个用于构建用户界面的JavaScrip...
- 基于.NET 5实现的开源通用权限管理平台
-
??大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!??今天小编推荐一款基于.NE...
- StreamPark - 大数据流计算引擎
-
使用Docker完成StreamPark的部署??1.基于h2和docker-compose进行StreamPark部署wgethttps://raw.githubusercontent.com/a...
- 教你使用UmiJS框架开发React
-
1、什么是Umi.js?umi,中文可发音为乌米,是一个可插拔的企业级react应用框架。你可以将它简单地理解为一个专注性能的类next.js前端框架,并通过约定、自动生成和解析代码等方式来辅助...
- 简单在线流程图工具在用例设计中的运用
-
敏捷模式下,测试团队的用例逐渐简化以适应快速的发版节奏,大家很早就开始运用思维导图工具比如xmind来编写测试方法、测试点。如今不少已经不少利用开源的思维导图组件(如百度脑图...)来构建测试测试...
- 【开源分享】神奇的大数据实时平台框架,让Flink&Spark开发更简单
-
这是一个神奇的框架,让Flink|Spark开发更简单,一站式大数据实时平台!他就是StreamX!什么是StreamX大数据技术如今发展的如火如荼,已经呈现百花齐放欣欣向荣的景象,实时处理流域...
- 聊聊规则引擎的调研及实现全过程
-
摘要本期主要以规则引擎业务实现为例,陈述在陌生业务前如何进行业务深入、调研、技术选型、设计及实现全过程分析,如果你对规则引擎不感冒、也可以从中了解一些抽象实现过程。诉求从硬件采集到的数据提供的形式多种...
- 【开源推荐】Diboot 2.0.5 发布,自动化开发助理
-
一、前言Diboot2.0.5版本已于近日发布,在此次发布中,我们新增了file-starter组件,完善了iam-starter组件,对core核心进行了相关优化,让devtools也支持对IAM...
- 微软推出Copilot Actions,使用人工智能自动执行重复性任务
-
IT之家11月19日消息,微软在今天举办的Ignite大会上宣布了一系列新功能,旨在进一步提升Microsoft365Copilot的智能化水平。其中最引人注目的是Copilot...
- Electron 使用Selenium和WebDriver
-
本节我们来学习如何在Electron下使用Selenium和WebDriver。SeleniumSelenium是ThoughtWorks提供的一个强大的基于浏览器的开源自动化测试工具...
- Quick 'n Easy Web Builder 11.1.0设计和构建功能齐全的网页的工具
-
一个实用而有效的应用程序,能够让您轻松构建、创建和设计个人的HTML网站。Quick'nEasyWebBuilder是一款全面且轻巧的软件,为用户提供了一种简单的方式来创建、编辑...
- 一周热门
- 最近发表
- 标签列表
-
- kubectlsetimage (56)
- mysqlinsertoverwrite (53)
- addcolumn (54)
- helmpackage (54)
- varchar最长多少 (61)
- 类型断言 (53)
- protoc安装 (56)
- jdk20安装教程 (60)
- rpm2cpio (52)
- 控制台打印 (63)
- 401unauthorized (51)
- vuexstore (68)
- druiddatasource (60)
- 企业微信开发文档 (51)
- rendertexture (51)
- speedphp (52)
- gitcommit-am (68)
- bashecho (64)
- str_to_date函数 (58)
- yum下载包及依赖到本地 (72)
- jstree中文api文档 (59)
- mvnw文件 (58)
- rancher安装 (63)
- nginx开机自启 (53)
- .netcore教程 (53)