别小看 Log 日志,它难住了我们组的架构师
Slf4j
slf4j 的全称是 Simple Loging Facade For Java,它仅仅是一个为 Java 程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如 JDBC 一样,只是一种规则而已。所以单独的 slf4j 是不能工作的,必须搭配其他具体的日志实现方案,比如 apache 的 org.apache.log4j.Logger,jdk 自带的 java.util.logging.Logger 等。
简单语法
SLF4J 不及 Log4J 使用普遍,因为许多开发者熟悉 Log4J 而不知道 SLF4J,或不关注 SLF4J 而坚持使用 Log4J。我么先看下 Log4J 示例:
Logger.debug("Hello " + name);
由于字符串拼接的问题,使用以上语句会先拼接字符串,再根据当前级别是否低于 debug 决定是否输出本条日志,即使不输出日志,字符串拼接操作也会执行,所以许多公司强制使用下面的语句,这样只有当前处于 DEBUG 级别时才会执行字符串拼接:
if (logger.isDebugEnabled()) {
LOGGER.debug(“Hello ” + name);
}
它避免了字符串拼接问题,但有点太繁琐了是不是?相对地,SLF4J提供下面这样简单的语法:
LOGGER.debug("Hello {}", name);
它的形式类似第一条示例,而又没有字符串拼接问题,也不像第二条那样繁琐。
日志等级 Level
Slf4j 有四个级别的 log level 可供选择,级别从上到下由低到高,优先级高的将被打印出来。
Debug:简单来说,对程序调试有利的信息都可以debug输出 info:对用户有用的信息 warn:可能会导致错误的信息 error:顾名思义,发生错误的地方
使用
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Test {
private static final Logger logger = LoggerFactory.getLogger(Test.class);
// ……
}
配置方式
yml
文件,因为 yml 文件的层次感特别好,看起来更直观,但是 yml 文件对格式要求比较高,比如英文冒号后面必须要有个空格,否则项目估计无法启动,而且也不报错。用 properties 还是 yml 视个人习惯而定,都可以。logging:
config: classpath:logback.xml
level:
com.bowen.dao: trace
logging.config
是用来指定项目启动的时候,读取哪个配置文件,这里指定的是日志配置文件是classpath:logback.xml
文件,关于日志的相关配置信息,都放在logback.xml
文件中了。logging.level
是用来指定具体的 mapper 中日志的输出级别,上面的配置表示com.bowen.dao
包下的所有 mapper 日志输出级别为 trace,会将操作数据库的 sql 打印出来,开发时设置成 trace 方便定位问题,在生产环境上,将这个日志级别再设置成 error 级别即可。Log4j
组成架构
日志等级 Level
off:关闭日志,最高等级,任何日志都无法输出 fatal:灾难性错误,在能够输出日志的所有等级中最高 error:错误,一般用于异常信息 warn:警告,一般用于不规范的引用等信息 info:普通信息 debug:调试信息,一般用于程序执行过程 trace:堆栈信息,一般不使用 all:打开所有日志,最低等级,所有日志都可使用
使用
<dependency>
<groupId>org.log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.9</version>
</dependency>
配置方式
log4j.rootLogger=debug,cons
log4j.appender.cons=org.apache.log4j.ConsoleAppender
log4j.appender.cons.target=System.out
log4j.appender.cons.layout=org.apache.log4j.PatternLayout
log4j.appender.cons.layout.ConversionPattern=%m%n
# 配置日志等级, 指定生效的Appender名字, AppenderA是定义的Appender的名字
log4j.rootLogger=日志等级,AppenderA,AppenderB,...
# ---------------- 定义一个appender------------------------
# 定义一个appender, appender名字可以是任意的,
# 如果要使该appender生效, 须加入到上一行rootLogger中, 后面为对应的Appender类
log4j.appender.appender名字=org.apache.log4j.ConsoleAppender
log4j.appender.appender名字.target=System.out
# 定义Appender的布局方式
log4j.appender.appender名字.layout=org.apache.log4j.SimpleLayout
Logback
模块组成
Logback 组件
Logger:日志的记录器;把他关联到应用对应的context上;主要用于存放日志对象;可以自定义日志类型级别 Appender:用于指定日志输出的目的地;目的地可以是控制台,文件,数据库等 Layout:负责把事件转换成字符串;格式化的日志信息的输出;在logback中Layout对象被封装在encoder中
Logback 优点
同样的代码路径,Logback 执行更快 更充分的测试 原生实现了 SLF4J API(Log4J 还需要有一个中间转换层) 内容更丰富的文档 支持 XML 或者 Groovy 方式配置 配置文件自动热加载 从 IO 错误中优雅恢复 自动删除日志归档 自动压缩日志成为归档文件 支持 Prudent 模式,使多个 JVM 进程能记录同一个日志文件 支持配置文件中加入条件判断来适应不同的环境 更强大的过滤器 支持 SiftingAppender(可筛选 Appender) 异常栈信息带有包信息
标签属性
configuration:配置的根节点
scan:为ture时,若配置文件属性改变会被扫描并重新加载,默认为true scanPeriod:监测配置文件是否有修改的时间间隔,若没给出时间单位,默认单位为毫秒;默认时间为1分钟;当scan="true"时生效 debug:为true时,将打出logback的内部日志信息,实时查看logback运行状态;默认值为false contextName:上下文名称,默认为“default”,使用此标签可设置为其它名称,用于区分不同应用程序的记录;一旦设置不能修改 appender:configuration 的子节点,负责写日志的组件,有name和class两个必要属性 name:addender的名称 class:appender的全限定名,就是对应的某个具体的Appender类名,比如ConsoleAppender、FileAppender append:为true时,日志被追加到文件结尾,如果是flase,清空现存的文件,默认值为true
配置方式
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<property resource="logback.properties"/>
<appender name="CONSOLE-LOG" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
</layout>
</appender>
<!--获取比info级别高(包括info级别)但除error级别的日志-->
<appender name="INFO-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
</encoder>
<!--滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>${LOG_INFO_HOME}//%d.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<appender name="ERROR-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
</encoder>
<!--滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>${LOG_ERROR_HOME}//%d.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<root level="info">
<appender-ref ref="CONSOLE-LOG" />
<appender-ref ref="INFO-LOG" />
<appender-ref ref="ERROR-LOG" />
</root>
</configuration>
ELK
Elasticsearch:是一个基于 Lucene 的、支持全文索引的分布式存储和索引引擎,主要负责将
日志索引并存储起来,方便业务方检索查询。Logstash:是一个日志收集、过滤、转发的中间件,主要负责将各条业务线的各类日志统一收
集、过滤后,转发给 Elasticsearch 进行下一步处理。Kibana:是一个可视化工具,主要负责查询 Elasticsearch 的数据并以可视化的方式展现给业
务方,比如各类饼图、直方图、区域图等。Filebeat:隶属于 Beats,是一个轻量级的日志收集处理工具。目前 Beats 包含四种工具: Packetbeat
(搜集网络流量数据)、Topbeat
(搜集系统、进程和文件系统级别的 CPU 和内存使用情况等数据)、Filebeat
(搜集文件数据)、Winlogbeat
(搜集 Windows 事件日志数据)
主要特点
收集:能够采集多种来源的日志数据 传输:能够稳定的把日志数据传输到中央系统 存储:如何存储日志数据 分析:可以支持 UI 分析 警告:能够提供错误报告,监控机制
应用场景
分布式日志数据集中式查询和管理; 系统监控,包含系统硬件和应用各个组件的监控; 故障排查; 安全信息和事件管理; 报表功能;
配置方式
#输入源,可以写多个
filebeat.input:
- type: log
enabled: true
#输入源文件地址
path:
- /data/logs/tomcat/*.log
#多行正则匹配,匹配规则 例:2020-09-29,不是这样的就与上一条信息合并
multiline:
pattern: '\s*\['
negate: true
match: after
#起个名字
tags: ["tomcat"]
#输出目标,可以把logstash改成es
output.logstash:
hosts: [172.29.12.35:5044]
.conf
为后缀的文件,或者打开 config 文件夹下的 .conf 文件,这里的配置文件是可以同时启动多个的,而且还有一个功能强大的filter
功能,可以过滤原始数据,如下:#输入源(必须)
input {
#控制台键入
stdin {}
#文件读取
file {
#类似赋予的名字
type => "info"
#文件路径,可以用*代表所有
path => ['/usr/local/logstash-7.9.1/config/data-my.log']
#第一次从头开始读,下一次继续上一次的位置继续读
start_position => "beginning"
}
file {
type => "error"
path => ['/usr/local/logstash-7.9.1/config/data-my2.log']
start_position => "beginning"
codec=>multiline{
pattern => "\s*\["
negate => true
what => "previous"
}
}
#与filebates配合使用
beats{
port => 5044
}
}
#输出目标(必须)
output {
#判断type是否相同
if [type] == "error"{
#如果是,就写入此es中
elasticsearch{
hosts => "172.29.12.35:9200"
#kibana通过index的名字进行查询,这里的YYYY是动态获取日期
index => "log-error-%{+YYYY.MM.dd}"
}
}
if [type] == "info"{
elasticsearch{
hosts => "172.29.12.35:9200"
#kibana通过index的名字进行查询
index => "log-info-%{+YYYY.MM.dd}"
}
}
#这里判断的是filebates中赋予的tags是否是tomcat
if "tomcat" in [tags]{
elasticsearch{
hosts => "172.29.12.35:9200"
#kibana通过index的名字进行查询
index => "tomcat"
}
}
#控制台也会打印信息
stdout {
codec => rubydebug {}
}
}
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️