日志文件必须输出到控制台才香对吗

news/2025/3/19 15:35:33/文章来源:https://www.cnblogs.com/Nuwa/p/18781144

在实际工作中发现很多人喜欢将日志输出到控制台,有的甚至直接只是输出到控制台,都不输出到日志文件中。

这种操作看似人畜无害,实际上直接影响着系统的性能,很多时候还难以排查,这里我从实际举例都背后原因来分析为什么这么做并不香。

通常的日志配置

这里我们使用经常使用的logback来举例即可,很多项目的配置如下所示:这里定义了两个输出到指定文件的appender ,然后一个默认的输出到控制台的ConsoleAppender,默认的配置无论是root还是自定义的输出都会同时写入到控制台、日志文件。项目上线的时候,大概率也是这样就配置了,可能连级别都懒得改。

<configuration scan="true" scanPeriod="1 hours" debug="false"><contextName>sample</contextName><property name="log.path" value="../logs/sample" /><!--输出到控制台--><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d %contextName [%t] %level %logger{36} - %msg%n</pattern><charset>UTF-8</charset></encoder></appender><appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${log.path}/sample.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level  %class{36} %L - %msg%n</pattern><charset>UTF-8</charset> <!-- 设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志归档 --><fileNamePattern>${log.path}/%d{yyyy-MM-dd}/sample-%d{yyyy-MM-dd}.%i.log</fileNamePattern><maxFileSize>20MB</maxFileSize><maxHistory>7</maxHistory><totalSizeCap>10GB</totalSizeCap><cleanHistoryOnStart>true</cleanHistoryOnStart></rollingPolicy></appender><appender name="coolFile" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/cool.log</file><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level  %class{36} %L - %msg%n</pattern><charset>UTF-8</charset></encoder><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>${log.path}/%d{yyyy-MM-dd}/cool-%d{yyyy-MM-dd}.%i.log</fileNamePattern><maxFileSize>20MB</maxFileSize><maxHistory>7</maxHistory><totalSizeCap>10GB</totalSizeCap><cleanHistoryOnStart>true</cleanHistoryOnStart></rollingPolicy></appender><logger name="o.s" level="WARN" additivity="false"><appender-ref ref="RollingFile"/></logger><logger name="log.cool" level="info" additivity="false"><appender-ref ref="STDOUT"/><appender-ref ref="coolFile"/></logger><root level="info"><appender-ref ref="STDOUT"/><appender-ref ref="RollingFile"/></root>
</configuration>

不良反应

按照上面的默认配置,然后倘若使用的window服务器,直接控制台模式运行Tomcat服务,那么日志肯定是会展示在控制台(好炫酷、好直观是不是,咱也不知道给谁看),因为在Linux服务器下,默认就是使用后台运行,不会给你一个黑框一直打印和展示日志(除非又有聪哥非得前台运行,还特意配置一下...)

那么假设此时有些代码疯狂的输出了很多日志,各种前端接受的参数输出,请求三方接口的参数输出、给前端响应之前的输出等等,总之日志量很大。那么就会发现系统偶发性的卡顿,没响应,吞吐量下降,但是服务器的CPU、内存、线程都很健康,好了,你无法给客户解释了。只能默默地说,肯定是网络抖动不骗你的~~~~

而实际上的罪魁祸首就是 日志输出到了控制台,尤其是大量日志输出到控制台。但是如果你不使用工具去从代码端检测,比如arthas之类的,你就不会知道尽然是输出日志导致整个服务卡。

用事实说话

这里随便整了一个Demo,用的配置就是上面的logback的日志配置,然后简单压测,来看效果。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;@WebServlet("/console/*")
public class ConsoleServlet extends HttpServlet {//模拟上面两个日志配置,输出到不同的文件而已private static final Logger log = LoggerFactory.getLogger(ConsoleServlet.class);private static final Logger log2 = LoggerFactory.getLogger("log.cool");@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String uri = req.getRequestURI();if (uri.endsWith("root")) {logRoot();} else if (uri.endsWith("each")) {logEach();}}private void logRoot() {String content = getContent();log.info("root:{}",content);}private void logEach() {log.info("each");log2.info("each");}public String getContent() {//模拟输出大量日志,下手很重的,比如几百K到几M这种。try {try(BufferedReader br = new BufferedReader(new FileReader("C:\\m3.log"));) {StringBuilder sb = new StringBuilder();String line =null;while ((line = br.readLine())!=null) {sb.append(line);}return sb.toString();}} catch (IOException e) {throw new RuntimeException(e);}}
}

就这么简单个代码,我们配置内存-Xmx2G -Xms2G,然后启动Tomcat来进行压测,这里使用jmeter压测,

  1. 创建了两个ThreadGroup,分别请求地址http://192.168.1.10:8108/sample/console/each和http://192.168.3.36:8108/sample/console/root
  2. 每个ThreadGroup并发3、LoopCount为20,也就是每个60次。每个都创建一个Summary Report来观察结果,同时观察JVM情况,确保不是JVM资源吃紧导致卡顿
  3. 越热两次后,观察JVM的使用率各项指标平稳后,开始正式测试并观察统计结果。

两个都启动,都给控制台和文件同时输出日志时JVM就指标无异常(其他情况更无异常):

image

  • 都输出到控制台和日志文件的时的测试结果:
    image

  • 只输出到文件时的测试结果:
    image

结果说明:

可以看到只要日志输出到控制台,无论是大日志本身的请求,还是其他的请求,只要使用到 日志输出,都会导致耗时飙升、吞吐量下降,比只输出到日志文件满了100倍,耗时更是不忍直视,注意上面是20个/每分钟,下面是26个/每秒。

解决办法

  • 线上尽可能使用日志级别为error,毕竟如果重要日志,肯定是要写库吧。
  • 异步日志处理器,但会占用更多的线程、更多资源【尤其是多个异步处理器,不信你试试朋友,CPU使用率biu一下子就起来了】,同时配置队列满了照样丢失日志。即使这样输出到控制台,一样卡。
  • 限制日志输出的字符串长度,大多日志框架都支持,比如上面的logback你可以配置%.-Nlogger。对应上面就是%.-500msg,意思是输出前500个字符,剩下都会消失哦
  • 重要的事情一遍就要记住:不要打印大日志<<我见过直接打印word文档的,打印几十万条数据的、打印Excel所有内容的,各种奇怪的姿势>>

分享一个异步的配置,就是改造下上面,拿去测吧,效果不见得多好,吃资源哟:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="1 hours" debug="false"><contextName>sample</contextName><property name="log.path" value="../logs/sample" /><!--输出到控制台--><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d %contextName [%t] %level %logger{36} - %msg%n</pattern><charset>UTF-8</charset></encoder></appender><appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${log.path}/sample.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level  %class{36} %L - %msg%n</pattern><charset>UTF-8</charset> <!-- 设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志归档 --><fileNamePattern>${log.path}/%d{yyyy-MM-dd}/sample-%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- each file should be at most 2MB, keep 7 days worth of history, but at most 10 GB --><maxFileSize>20MB</maxFileSize><maxHistory>7</maxHistory><totalSizeCap>10GB</totalSizeCap><cleanHistoryOnStart>true</cleanHistoryOnStart></rollingPolicy><!-- 此日志文件只记录debug级别的 --><!--<filter class="ch.qos.logback.classic.filter.LevelFilter"><level>debug</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter>--></appender><appender name="coolFile" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/cool.log</file><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level  %class{36} %L - %msg%n</pattern><charset>UTF-8</charset></encoder><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>${log.path}/%d{yyyy-MM-dd}/cool-%d{yyyy-MM-dd}.%i.log</fileNamePattern><maxFileSize>20MB</maxFileSize><maxHistory>7</maxHistory><totalSizeCap>10GB</totalSizeCap><cleanHistoryOnStart>true</cleanHistoryOnStart></rollingPolicy></appender><appender name="ASYNC_ROLLING" class="ch.qos.logback.classic.AsyncAppender"><queueSize>512</queueSize> <!-- 可选,队列大小 --><discardingThreshold>0</discardingThreshold> <!-- 可选,避免丢弃日志 --><includeCallerData>true</includeCallerData> <!-- 解决类名丢失 --><appender-ref ref="RollingFile"/></appender><appender name="ASYNC_COOL" class="ch.qos.logback.classic.AsyncAppender"><queueSize>512</queueSize><discardingThreshold>0</discardingThreshold><includeCallerData>true</includeCallerData><appender-ref ref="coolFile"/></appender><logger name="o.s" level="WARN" additivity="false"><appender-ref ref="RollingFile"/></logger><logger name="log.cool" level="info" additivity="false"><appender-ref ref="STDOUT"/><appender-ref ref="ASYNC_COOL"/></logger><root level="debug"><appender-ref ref="STDOUT"/><appender-ref ref="ASYNC_ROLLING"/></root>
</configuration>

原因分析

其实很简单,默认的配置都是输出到控制台ch.qos.logback.core.ConsoleAppender,这里虽然是logback,而实际上其他的日志框架也是一样的道理,默认输出到控制台,其实就等于是标准输出,也就是System.out。
这里也就是为什么很多编码规范中不允许使用System.out去输出日志是一个道理,输出控制台到线上有何作用,刷屏那么快给谁看,而且这个标准输出必然是单线程的。就和有时候发现卡机了,原因是有人上去操作服务器,把控制台用鼠标点了一下,卡在那里了(整个因为这个卡了,你就说这不是闹呢么)

就是要输出到控制台,就是要用控制台模式启动服务,我有自己的哲学和道理,最少我要知道这个东西它什么时候启动了把,你别管,卡了你解决就是了,你给我控制台输出就行

—某领导霸气的说。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/901556.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#通过FTP获取服务端文件

一、简介实际需求是在前端修改了配置文件后,由上位机统一分发给所有设备,因为下位机支持FTP协议,因此选用FTP来实现文件传输功能。 二、准备工作 1、FTP服务搭建FTP服务端选用FileZilla Server,免费开源,简单好用,可以下载中文版的。下载地址:下载 - FileZilla中文网,也…

20242942 2024-2025-2 《网络攻防实践》实验三

1.实验内容 (1)动手实践tcpdump 使用tcpdump开源软件对在本机上访问www.tianya.cn网站过程进行嗅探,回答问题:你在访问www.tianya.cn网站首页时,浏览器将访问多少个Web服务器?他们的IP地址都是什么? (2)动手实践Wireshark 使用Wireshark开源软件对在本机上以TELNET方式…

Oracle OCP认证没落了吗?

Oracle OCP认证没落了吗? Oracle的OCP认证是数据库领域必考的一个认证,但随着国产化的发展,国内很多企业开发了自己的数据库产品,这种情况对很多人造成了错误的认识:OCP被淘汰了吗?不然,从行业需求、技术趋势、认证体系变化等角度综合分析,Oracle OCP证书并未完全“没…

查看dll文件的publicKeyToken

输入: SN -T "C:\Program Files (x86)\Kingdee\K3Cloud\WebSite\bin\log4net.dll"

把 DeepSeek 接入电话系统后,不知疲倦的智能客服向我们走来了

我们基于deepseek和Freeswitch做了一个智能电话客服。 它会基于给定的FAQ知识库来回答问题,自动进行语音识别和语音合成。 语音识别我们采用的是开源的FunAsr,语音合成采用的是第三方商用的API接口。我们接下来介绍下它的内部组成部分。【 第一阶段】当电话拨通电话后,电话服…

Seata的工作模式

Seata的分布式模型中各个角色的作用: 1.TM(事务管理器)是分布式事务的发起方,负责定义全局事务的边界(开始,提交,回滚),并于TC交互协调事务状态。 核心职责: ​ 通过@GlobalTransctional注解标记全局事务的起点。 ​ 向TC注册全局事务 ​ 根据业务逻辑决定全局事务的提交或回滚…

dify升级

一、需求 从0.14.2升级到0.15.3,要求模型供应商,创建的应用数据等等,不能丢失。二、安装0.14.2 下载dify代码cd /optgit clone https://github.com/langgenius/dify.gitcd dify/切换到tag 0.14.2git checkout 0.14.2git pull origin 0.14.2运行difycd dockercp .env.example…

机器人弧焊电源气体省气装置的工作原理是什么?

焊接机器人节气装置,作为一种在焊接流程中至关重要的气体调控设备,其核心宗旨在于在确保焊接作业品质卓越的同时,通过高度精确的气体流量调控机制,有效缩减气体的消耗量,进而达成显著的节气效益。该装置详尽地集成了气体质量流量控制器、适配的电源适配器、精准的电流传感…

分享一次利用无问AI进行应急响应

前情摘要:最近勒索病毒真的太猖獗了,光一星期我就接连处理了两起。不过黑客的攻击方式都是大同小异,处理完报告之后,特来分享其中一起。各位可得小心小心在小心,千万不要中招,不然就只有重装的份了。 事件概述: 最近,某公司机房数据库中了勒索病毒,工作人员上班发现后…

广告子包边切割焊接工艺-代加工-外协加工-委外加工-激光代加工-河南郑州-芯晨微纳(河南)

一、普通焊接 普通焊接主要针对大型标识,如银行或家具城的大型广告字。这种焊接方式使用焊机进行,具体流程包括: (1)切割:根据所需的字体大小,使用不同的激光切割机进行切割处理。这一步不仅包括字面的切割,还包括围边的切割。 (2)焊接包边:在焊接过程中,需要非常精…

什么!你还不会写Vue组件,编写《功能级权限》匹配公式组件

说明该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发)。该系统文章,我会尽量说的非常详细,做到不管新手、老手都能看懂。说明:OverallAuth2.0 是一个简单、易懂、功能强大的权限+可视化流程管理系统。 友情提醒:本篇文章是属于系列文章,…

基于策略的强化学习

基于策略的强化学习 1. 策略函数近似(Policy Function Approximation) 策略函数 π(a∣s)策略函数是一个概率密度函数,它根据当前状态 s 输出在该状态下采取每个可能动作 a 的概率。 在有限的状态和动作空间中,可以直接学习这个函数。但在连续动作空间或状态空间非常大时,…