百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

6000字讲明白大型网站架构技术细节:后端架构规整化java日志框架

ccwgpt 2024-10-01 08:12 43 浏览 0 评论

日志

对于一个大型网站系统而言,日志是后端应用程序必须要引入的模块,日志有利于追查Bug和记录用户操作。每个编程语言都有很多现成的日志框架,而Java的日志框架也有很多,如log4j2、Logback、SLF4J等,这些日志框架都可以让后端应用程序按照一定的规则输出日志文件。

在大型网站系统当中,仅仅把日志记录下来是远远不够的。大型网站系统需要一个完整的日志系统,日志系统除了需要收集日志并将其记录下来以外,还需要做日志筛选、用户行为记录追溯及风险预警等工作。

不过,在项目中前期,或者单独调试某个后端应用程序时,仍然需要使用日志模块。这里以log4j2为例,通过日志模块引入和规范化日志记录两个方面对日志模块的使用进行介绍。

1.日志模块引入

引入日志模块的具体操作步骤如下:

(1)引入log4j2依赖包。需要在工程配置文件(build.gradle)中添加log4j2依赖包,如代码4.32所示。需要注意的是,如果不去除Spring Boot中原有日志模块的话,那么新引入的日志模块与原有日志模块会产生冲突。

代码4.32 build.gradle中添加log4j2依赖包

configurations {

//去除Spring Boot中原有的日志模块

all*.exclude group: 'org.springframework.boot', module: 'spring-boot

starter-logging'

}

dependencies {

//dependencies中添加log4j2的依赖包

implementation 'org.springframework.boot:spring-boot-starter-log4j2'

…}

(2)同步工程配置。修改完build.gradle文件后,log4j2的依赖包在同步工程配置后才会被下载和引入。在IntelliJ IDEA中单击“同步”按钮即可同步工程配置,如图4.59所示。

(3)创建log4j2的配置文件log4j2.xml,其内容及设置说明如代码4.33所示,更详细的说明请参考官方说明(https://logging.apache.org/log4j/2.x/manual/index.html)。另外,日志配置文件一般与后端应用程序的配置文件放在一起,如图4.60所示。

说明:图4.60中的“配置文件目录位置”和“后端应用程序配置文件名”都不是默认设置。关于“配置文件目录位置”和“后端应用程序配置文件名”的设置可参考前面小节中的讲解。

代码4.33 log4j2.xml文件的内容及其配置

<?xml version="1.0" encoding="UTF-8"?>

<!-- monitorInterval:检查更新的时间间隔,单位为s。

在程序运行期间,log4j2能够自动检测日志配置文件是否有更新,如果有更新则自动加载新设

置-->

<configuration monitorInterval="1800">

<!--配置变量,变量会被后续设置使用-->

<properties>

<!-- 设置日志格式的变量:%d:获取日期时间;

%level:日志等级;

%msg:日志消息,如ERROR、INFO、DEBUG等

%n:换行符-->

<property name="LOG_PATTERN"

value="[%d{yyyy-MM-dd}][%d{HH:mm:ss}][%level]%msg%n" />

<!-- 设置日志存储路径的变量 -->

<property name="FILE_PATH" value="D:/logs/backend/demo" />

</properties>

<!-- 设置日志输出源,如设置日志输出格式、设置日志文件名等 -->

<appenders>

<!-- 设置Console(控制台)输出日志格式,一般在开发工具调试时使用 -->

<console name="Console" target="SYSTEM_OUT">

<!--输出日志的格式,采用properties中设置的LOG_PATTERN变量-->

<patternLayout pattern="${LOG_PATTERN}"/>

</console>

<!-- 设置记录INFO和DEBUG日志等级的日志文件,当符合存档策略时在(<policies></policies>中设置),则会自动压缩并另存为存档文件。

fileName:日志文件名,使用properties中设置的FILE_PATH变量。在此例中,输出文件名为D:/logs/backend/demo/web-info.log。

immediateFlush:接收到日志后,是否立即输出到文件中。这个一般设置为false,设置为true会严重影响接口的并发能力。

filePattern:存档文件名,在此例中,归档文件名为(以2020-2-23为例)D:/logs/backend/demo/web-info/web-info-2020-2-23_1.log.gz-->

<rollingFile name="RollingFileInfo" fileName="${FILE_PATH}/web

info.log"

immediateFlush="false"

filePattern="${FILE_PATH}/web-info/web-info-%d{yyyy-MM

dd}_%i.log.gz">

<!-- 输出日志的格式,采用properties中设置的LOG_PATTERN变量 -->

<patternLayout pattern="${LOG_PATTERN}"/>

<!-- 筛选接收的日志等级,接收INFO和DEBUG等级的日志 -->

<filters>

<thresholdFilter level="error" onMatch="DENY" onMismatch=

"NEUTRAL"/>

<thresholdFilter level="info" onMatch="ACCEPT" onMismatch=

"DENY"/>

<thresholdFilter level="debug" onMatch="ACCEPT" onMismatch=

"DENY"/>

</filters>

<!-- 设置存档策略,此例为:每天自动存档,日志文件超过20MB也会存档 -->

<policies>

<!-- 设置时间的存档策略,interval的时间精度与filePattern的时间精

度一致,因为filePattern只设置到日期,所以这里的interval="1"指的是1天-->

<timeBasedTriggeringPolicy interval="1"/><!-- 设置文件大小的存档策略-->

<sizeBasedTriggeringPolicy size="20MB"/>

</policies>

<!-- 设置保留多少个日志文件,日志文件个数超过max的值会自动覆盖 -->

<defaultRolloverStrategy max="15"/>

</rollingFile>

<!-- 设置记录error日志等级的日志文件,配置格式与上面“设置记录INFO和DEBUG

日志等级的日志文件”相同,这里不展开介绍。

在此例子中,ERROR日志的日志文件名为D:/logs/backend/demo/web-error.log,归档文件名为(以2020-2-23为例)

D:/logs/backend/demo/web-error/web-error-2020-2-23_1.log.gz-->

<rollingFile name="RollingFileError" fileName="${FILE_PATH}/web

error.log"

immediateFlush="false"

filePattern="${FILE_PATH}/web-error/web-error-%d{yyyy-MM

dd}_%i.log.gz">

<patternLayout pattern="${LOG_PATTERN}"/>

<filters>

<thresholdFilter level="error" onMatch="ACCEPT" onMismatch=

"DENY"/>

</filters>

<policies>

<timeBasedTriggeringPolicy interval="1"/>

<sizeBasedTriggeringPolicy size="20MB"/>

</policies>

<defaultRolloverStrategy max="15"/>

</rollingFile>

</appenders>

<!-- 设置日志源,需要在这里关联日志输出源(<appenders></appenders>)才能输出到对

应文件当中 -->

<loggers>

<!-- 设置输出日志等级,默认情况下,不会输出比该日志等级低的日志。

在此例中,只输出FATAL、ERROR、WARN、INFO的日志。

日志级别以及优先级排序为OFF > FATAL > ERROR > WARN > INFO > DEBUG >

TRACE > ALL,其中OFF是不输出所有日志-->

<root level="info">

<!-- 关联输出源,其中ref中的值需要与<rollingFile></rollingFile>中

的name对应 -->

<!-- 在非调试环境下,需要关闭控制台的日志输出(去掉下面第一行就可以了) -->

<AppenderRef ref="Console" />

<AppenderRef ref="RollingFileInfo" />

<AppenderRef ref="RollingFileError" />

</root>

</loggers>

</configuration>

(4)引入日志配置文件。在后端应用程序配置文件中指定日志配置文件路径后,才能生效。在如图4.60所示的工程目录结构中,需要在demo.properties文件中添加日志配置文件的路径,如代码4.34所示,其中,classpath:log4j2.xml为具体的路径。

代码4.34 在后端应用程序的配置文件中添加日志配置文件的路径

#设置日志配置文件的路径

logging.config=classpath:log4j2.xml

(5)程序中记录日志,如代码4.35所示。对应的日志输出结果如代码4.36所示,其中,由于log4j2.xml(日志配置文件)设置的日志输出等级为INFO,所以DEBUG等级的日志没有被记录下来。

说明:日志配置文件的相关说明请参照代码4.33,其中,设置输出日志等级的位置为<root level="info">…</root>。

代码4.35 代码中记录日志

//需要引入的日志依赖类

import org.apache.logging.log4j.LogManager;

import org.apache.logging.log4j.Logger;

public class XX{

//获取日志对象

private static final Logger LOGGER = LogManager.getLogger();

public void Function(){

//输出INFO级别的日志

LOGGER.info("info log test.");

//输出ERROR级别的日志

LOGGER.error("error log test.");

//输出DEBUG级别的日志

LOGGER.debug("debug log test.");

}

}

代码4.36 日志输出结果

[2020-04-20][17:46:17][INFO]info log test.

[2020-04-20][17:46:17][ERROR]error log test.

(6)如果实现了4.3.3小节中的后端应用程序与配置文件分离,那么日志配置文件也应该从后端应用程序中分离出来。按常理来说,只要在配置文件中设置日志配置文件路径就可以了(如logging.config=/home/tomcat/appconfig/log4j2.xml),但是由于某种冲突,这样的配置是不起作用的。

因此,要想实现后端应用程序引用外部的日志配置文件,需要通过“添加启动参数”和“修改代码(ServletInitializer.java)”才能实现。具体做法是,“添加启动参数”需要在Tomcat目录下的/conf/catalina.properties文件中添加如代码4.37所示的设置,修改后的代码如代码4.38所示,其中,xxx_log4j2.xml为日志配置文件名。最终,配置文件和日志配置文件集中管理的效果如图4.61所示。

代码4.37 设置日志配置文件所在目录

#后端应用程序的外部配置文件所在目录,详见4.3.3节的介绍

spring.config.location=${catalina.home}/appconfig/

#新增代码,设置日志配置文件所在目录,一般与后端应用程序的外部配置文件目录相同

logging.config=${catalina.home}/appconfig/

代码4.38 修改后的ServletInitializer.java文件

public class ServletInitializer extends SpringBootServletInitializer {

@Override

protected SpringApplicationBuilder configure(SpringApplicationBuilder

application) {

//新增代码,补全启动参数(logging.config)的文件名

String loggingConfig = System.getProperty("logging.config");

if(!loggingConfig.isEmpty()) {

System.setProperty("logging.config", loggingConfig+"xxx_

log4j2.xml");

}

//设置后端应用程序的外部配置文件名,详见4.3.3小节的介绍

return application

.properties("spring.config.name=xxx")

.sources(xxApplication.class);

//初始工程的代码,需要去除

//return application.sources(xxApplication.class);

}

}

2.规范化日志记录

日志记录是一个十分开放的行为,原则上,只要记录的日志能“定位问题发生的位置”和“记录某些重要的用户操作”就可以了。但是,在实际项目当中,由于日志对功能的实现是不产生影响的,所以日志通常都是通过一次次问题修复而得到补充的。而这种“补丁型”日志,通常是混乱的。混乱的日志对于“定位问题发生的位置”和“追查某些重要的用户操作”都不能起到很好的作用,因此,日志记录需要规范化。

注意:日志记录的规则需要在项目前期定好,在开发过程中吸收规整化日志的工作量。项目后期再整理日志是很不理智的行为,因为后期整理日志需要花费大量的时间去梳理和理解业务代码,而这部分工作量是很难预估且十分枯燥的,最后整理日志的结果往往也是不尽人意的。

规范化日志记录可以从限制日志等级、明确日志记录位置、添加日志跟踪码等方面进行考虑。

(1)限制日志等级。日志模块一般都有等级划分,以log4j2为例,其日志等级有6种,分别为TRACE(追踪调试)、DEBUG(调试)、INFO(信息)、WARNING(警告)、ERROR(错误)和FATAL(致命错误)。每个日志等级看上去都有相对明确的分工和含义,但是在实际应用当中,这些日志等级的具体用途其实相当模糊,很多时候,都很难界定一个日志应该归类为哪一个等级。一旦出现这种模糊规则,就会出现一人一个样的做法,最后导致“五花八门”的日志等级划分原则。

因此,规整化日志需要限制日志等级。一般情况下,后端应用程序使用DEBUG、INFO和ERROR三个日志等级就足够了,这三个日志等级的分工和协助如图4.62所示。

其中,DEBUG日志在运行时不生效,需要打开调试模式(修改日志输出等级)后,才能记录DEBUG日志。

(2)明确日志记录位置。在限制了日志等级后,需要解决“补丁式日志”的问题,避免日志不够全面的情况。而解决“补丁式日志”的关键,是明确日志记录位置。但是,明确日志记录位置是一件很难实现的事情,因为接口程序与接口程序之间很难找到共性。

不过,如果实现了4.3.2小节中介绍的“限制函数调用层级”“公共模块”和“错误机制”,那么接口程序会变成流水线式的处理方式。在流水线式的接口程序中明确日志记录位置是相对容易的,如图4.63所示。

其中,每个接口只需要在“接收请求”“数据库操作”和“返回结果”这三部分添加日志就可以了,其余日志都在公共模块里,而且公共模块里的日志是一次添加全局有效的。

说明:Dao层(数据库操作)其实也可以做成一个公共模块,这样可以省掉一些日志工作量。另外,虽然数据库本身可以自动记录日志,但是数据库自身的日志不能包含用户身份信息,即不能追溯用户操作,所以Dao层(数据库操作)的日志是有必要记录的。

(3)添加日志跟踪码。即使日志被记录得十分详细,分析日志也是一件很麻烦的事情。同一时刻,后端应用程序可能会同时处理多个请求,以至于多个请求的日志是混合在一起的,在不经过特殊处理的情况下,根本没法分辨哪几条日志是属于同一个请求的。像这种无法区分请求的日志,被记录下来也是浪费资源。

因此,需要在每条日志中添加日志跟踪码,标记同一请求的日志。跟踪码的本质,就是同一请求输出日志时,都多加一个相同的字符串。如果使用的是log4j2日志模块,可以在不改变原有日志输出代码的前提下,添加日志跟踪码。

首先,需要修改日志配置文件中的日志输出格式,如代码4.39所示,其中[%X{requestId}]为新增的跟踪码格式。

代码4.39 修改日志输出格式

<!-- 此段设置截取自代码4.33的,单独设置是不起作用的 -->

<!-- 设置日志格式的变量:

%d:获取日期时间;

%level:日志等级;

%X{requestId}:跟踪码;

%msg:日志消息,如ERROR、INFO、DEBUG等;

%n:换行符-->

<property name="LOG_PATTERN"

value="[%d{yyyy-MM-dd}][%d{HH:mm:ss}][%level][%X{requestId}]

%msg%n" />

修改完日志配置文件之后,需要在每个接口程序中添加“生成跟踪码”的代码,如代码4.40所示,其中,代码中的函数为Controller层中的接口的入口函数,requestId对应代码4.39中的跟踪码标识。

代码4.40 添加“生成跟踪码”的代码

@Controller

@RequestMapping(value="…",method = RequestMethod.POST)

@ResponseBody

public JSONObject XXX(@RequestBody String requestParam, HttpServlet

Response response) {

//在每个接口的入口函数都需要添加以下“生成跟踪码”代码

ThreadContext.put("requestId", UUID.randomUUID().toString());

}

…修改日志跟踪码后,能清晰地识别不同请求的日志,日志输出结果如代码4.41所示,其中,62e3300c-e0a0-40cd-be80-4320d40ddc2c和00000000-0000-0000-0000-000000000000是日志追踪码。

代码4.41 添加“日志跟踪码”后的日志输出结果

[2020-04-20][17:46:17][INFO][62e3300c-e0a0-40cd-be80-4320d40ddc2c]info

log test_1.

[2020-04-20][17:46:17][INFO][00000000-0000-0000-0000-000000000000]info

log test_1.

[2020-04-20][17:46:17][INFO][00000000-0000-0000-0000-000000000000]info

log test_2.

[2020-04-20][17:46:17][INFO][62e3300c-e0a0-40cd-be80-4320d40ddc2c]info

log test_2.

[2020-04-20][17:46:17][INFO][62e3300c-e0a0-40cd-be80-4320d40ddc2c]info

log test_3.

本文给大家讲解的内容是大型网站架构的技术细节:后端架构规整化java日志框架

  1. 下篇文章给大家讲解的内容是大型网站架构的技术细节:后端架构规整化自研框架Once
  2. 感谢大家的支持!

相关推荐

自己动手写Android数据库框架_android开发数据库搭建

http://blog.csdn.net/feiduclear_up/article/details/50557590推荐理由关于Android数据库操作,由于每次都要自己写数据库操作,每次还得去...

谷歌开源大模型评测工具LMEval,打通谷歌、OpenAI、Anthropic

智东西编译|金碧辉编辑|程茜智东西5月28日消息,据科技媒体TheDecoder5月26日报道,当天,谷歌正式发布开源大模型评测框架LMEval,支持对GPT-4o、Claude3.7...

工信部:着力推动大模型算法、框架等基础性原创性的技术突破

工信部新闻发言人今日在发布会上表示,下一步,我们将坚持突出重点领域,大力推动制造业数字化转型,推动人工智能创新应用。主要从以下四个方面着力。一是夯实人工智能技术底座。通过科技创新重大项目,着力推动大模...

乒乓反复纠结“框架不稳定”的三个小误区

很多球友由于对框架的认知不清晰,往往会把“框架不稳定”当成一种心理负担,从而影响学球进度,其典型状态就是训练中有模有样,一旦进入实战,就像被捆住了手脚。通过训练和学习,结合“基本功打卡群”球友们交流发...

前AMD、英特尔显卡架构师Raja再战GPU,号称要全面重构堆栈

IT之家8月5日消息,知名GPU架构师拉贾科杜里(RajaKoduri)此前曾先后在AMD和英特尔的显卡部门担任要职。而在今日,由Raja创立的GPU软件与IP初创企...

三种必须掌握的嵌入式开发程序架构

前言在嵌入式软件开发,包括单片机开发中,软件架构对于开发人员是一个必须认真考虑的问题。软件架构对于系统整体的稳定性和可靠性是非常重要的,一个合适的软件架构不仅结构清晰,并且便于开发。我相...

怪不得别人3秒就知道软考案例怎么做能50+

软考高级统一合格标准必须三科都达到45分,案例分析也一直是考生头疼的一门,但是掌握到得分点,案例能不能50+还不是你们说了算吗?今天就结合架构案例考点,分享实用的备考攻略~一、吃透考点,搭建知识框架从...

UML统一建模常用图有哪些,各自的作用是什么?一篇文章彻底讲透

10万+爆款解析:9大UML图实战案例,小白也能秒懂!为什么需要UML?UML(统一建模语言)是软件开发的“蓝图”,用图形化语言描述系统结构、行为和交互,让复杂需求一目了然。它能:降低沟通成本避...

勒索软件转向云原生架构,直指备份基础设施

勒索软件组织和其他网络犯罪分子正越来越多地将目标对准基于云的备份系统,对久已确立的灾难恢复方法构成了挑战。谷歌安全研究人员在一份关于云安全威胁演变的报告中警告称,随着攻击者不断改进数据窃取、身份泄露和...

ConceptDraw DIAGRAM:释放创意,绘就高效办公新未来

在当今数字化时代,可视化工具已成为提升工作效率和激发创意的关键。ConceptDrawDIAGRAM,作为一款世界顶级的商业绘图软件,凭借其强大的功能和用户友好的界面,正逐渐成为众多专业人士的首选绘...

APP 制作界面设计教程:一步到位_app界面设计模板一套

想让APP界面设计高效落地,无需繁琐流程,掌握“框架搭建—细节填充—体验优化”三步法,即可一步到位完成专业级设计。黄金框架搭建是基础。采用“三三制布局”:将屏幕横向三等分,纵向保留三...

MCP 的工作原理:关键组件_mcp部件

以下是MCP架构的关键组件:MCP主机:像ClaudeDesktop、GitHubCopilot或旅行助手这样的AI智能体,它们希望通过MCP协议访问工具、资源等。MCP主机会...

软件架构_软件架构师工资一般多少

软件架构师自身需要是程序员,并且必须一直坚持做一线程序员。软件架构应该是能力最强的一群程序员,他们通常会在自身承接编程任务的同时,逐渐引导整个团队向一个能够最大化生产力的系统设计方向前进。软件系统的架...

不知不觉将手机字体调大!老花眼是因为“老了吗”?

现在不管是联系、交友,还是购物,都离不开手机。中老年人使用手机的时间也在逐渐加长,刷抖音、看短视频、发朋友圈……看手机的同时,人们也不得不面对“视力危机”——老花眼,习惯眯眼看、凑近看、瞪眼看,不少人...

8000通用汉字学习系列讲座(第046讲)

[表声母字]加(续)[从声汉字]伽茄泇迦枷痂袈笳嘉驾架咖贺瘸(计14字)嘉[正音]标准音读jiā。[辨形]上下结构,十四画。会意形声字,从壴从加,加也表声。注:从壴,字义与鼓乐有关;从加,字义与...

取消回复欢迎 发表评论: