Spring Boot Log4j2 日志学习

简介

Java 中比较常用的日志工具类,有:

  • Log4j、
  • SLF4j、
  • Commons-logging(简称jcl)、
  • Logback、
  • Log4j2(Log4j 升级版)、
  • Jdk Logging

Spring Boot 默认使用 Logback,但相比较而言,Log4j2 在性能上面会更好。SpringBoot 高版本都不再支持 log4j,而是支持 log4j2。log4j2,在使用方面与 log4j 基本上没什么区别,比较大的区别是 log4j2 不再支持 properties 配置文件,支持 xml、json 格式的文件。

  • Github-apache/logging-log4j2
  • 官方文档

log4j2 安装

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions>            <!-- 去掉logback配置 --><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency>        <!-- 引入log4j2依赖 --><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency>
</dependencies>

log4j2 使用

// 这几种写法都 OK
private static final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final Logger logger = LogManager.getLogger(UserController.class); 
//...
logger.debug("this is debug");
logger.info("this is info");

log4j2.xml 示例

配置文件的主要结构如下:

  • Appenders:
    • Appender
      • Filter
      • Layout
      • Policies
      • Strategy
  • Loggers
    • Logger
    • RootLogger

Github 配置地址:SpringBoot-Note/mybatis-demo/src/main/resources/log4j2.xml

Appender

简单说 Appender 就是一个管道,定义了日志内容的去向(保存位置)。

  • 配置一个或者多个Filter。
  • 配置 Layout 来控制日志信息的输出格式。
  • 配置 Policies 以控制日志何时(When)进行滚动。
  • 配置 Strategy 以控制日志如何(How)进行滚动。

Appender 官宣

注意点:

  • 多个 appender 不能指向同一个日志文件,否则会报错:Configuration has multiple incompatible Appenders pointing to the same resource 'logs/mybatis-demo-warn.log'
  • ImmediateFlush=true,一旦有新日志写入,立马将日志写入到磁盘的文件中。当日志很多,这种频繁操作文件显然性能很低下
  • immediateFlush:log4j2接收到日志事件时,是否立即将日志刷到磁盘。默认为true。
  • BufferedIO: 文件流写出是否使用缓冲,true表示使用,默认值为false即不使用缓冲。测试显示,即使在启用immediateFlush的情况下,设置bufferedIO=true也能提高性能。
  • 一个 LogConfig 可以使用多个 appender,一个 appender 也可以被多个 LogConfig 使用

Filter

Filters 决定日志事件能否被输出。过滤条件有三个值:ACCEPT(接受)DENY(拒绝)NEUTRAL(中立)

常用的Filter实现类有:

  • LevelRangeFilter
  • TimeFilter
  • ThresholdFilter

简单说就是log4j2中的过滤器 ACCEPTDENY 之后,后续的过滤器就不会执行了,只有在 NEUTRAL 的时候才会执行后续的过滤器

<Console name="Console"><!--设置 onMismatch="NEUTRAL" 可以让日志经过后续的过滤器最后一个过滤器建议设置 onMismatch="DENY", 不然日志就输出了。--><Filters><!-- 从大到小:error, warn, info, debug, trace --><LevelRangeFilter minLevel="error" maxLevel="info" onMatch="ACCEPT" onMismatch="NEUTRAL" /><!-- 只允许在每天的 8点~8点半 之间输出日志 --><TimeFilter start="08:00:00" end="08:30:00" onMatch="ACCEPT" onMismatch="DENY" /></Filters><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
</Console>

LevelRangeFilter 对它们进行了 ACCEPT,而剩下的 trace Msgdebug Msg 则会经过下一个过滤器

PatternLayout

这是常用的日志格式化类,其它日志格式化类很少用。

<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>

常用说明:

%d{HH:mm:ss.SSS} 表示输出到毫秒的时间
%t 输出当前线程名称
%-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
%logger 输出logger名称,因为Root Logger没有名称,所以没有输出
%msg 日志文本
%n 换行其他常用的占位符有:
%F 输出所在的类文件名,如Client.java
%L 输出行号
%M 输出所在方法名
%l  输出语句所在的行数, 包括类名、方法名、文件名、行数

关于 pattern 的格式点击:

  • http://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout

Policy

Policy & Strategy

  • Policy 是用来控制日志文件何时(When)进行 Rolling/滚动的;
  • Strategy是用来控制日志文件如何(How)进行 Rolling/滚动的。

所谓「日志滚动」就是当达到设定的条件后,日志文件进行切分。比如:工程师想让系统中的日志按日进行切分,并且按月归档。

Rolling 的意思是当满足一定条件后,就重命名原日志文件用于备份,并重新生成一个新的日志文件。例如需求是每天生成一个日志文件,但是如果一天内的日志文件体积已经超过 1G,就重新生成。两个条件满足一个即可

Policy常用的实现类:

  • SizeBasedTriggeringPolicy,根据日志文件的大小进行滚动。单位有:KBMBGB
  • CronTriggeringPolicy,使用 Cron 表达式进行日志滚动,很灵活
  • TimeBasedTriggeringPolicy,这个配置需要和filePattern结合使用,注意 filePattern 中配置的文件重命名规则。滚动策略依赖于 filePattern 中配置的最具体的时间单位,根据最具体的时间单位进行滚动。这种方式比较简洁。CronTriggeringPolicy 策略更强大
    • TimeBasedTriggeringPolicy 标签中加上了 modulate 属性并设置为 true,该属性的意思是是否对日志生成时间进行调制。若为 true,则日志时间将以 0 点为边界进行偏移计算。例如第一次日志保存时间是 3 点,modulatetrueinterval4h。那么下次生成日志时间是 4点,08:00,12:00……。
<Appenders><RollingRandomAccessFile name="File" fileName="logs/app.log"filePattern="logs/$${date:hh-mm}/%d{hh-mm-ss}.app.%i.log" ><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/><Policies><!-- 每 5s 翻滚一次 --><!--<CronTriggeringPolicy schedule="0/5 * * * * ?" />--><!--filePattern中最具体的时间单位是 秒。这里用 TimeBasedTriggeringPolicy 替换 CronTriggeringPolicy注意:modulate属性是指从启动时间开始算5秒,还是从0秒开始算5秒,运行一下就明白了。modulate: true(默认值) // 会从启动时间开始算 5秒modulate: false // 从 0秒开始算--><TimeBasedTriggeringPolicy interval="5" modulate="true"/><SizeBasedTriggeringPolicy size="10 MB"/></Policies><DefaultRolloverStrategy max="10" /></RollingRandomAccessFile>
</Appenders>

Strategy

Strategy常用的实现类:

  • DefaultRolloverStrategy
  • DirectWriteRolloverStrategy

这两个 Strategy 都是控制如何进行日志滚动的。

DefaultRolloverStrategy 默认的 max为 7。

<DefaultRolloverStrategy max="7"/>

max 参数指定了计数器的最大值。一旦计数器达到了最大值,过旧的文件将被删除。

注意:不要认为 max 参数是需要保留的日志文件的最大数目。

max 参数是与 filePattern 中的计数器 %i 配合起作用的,其具体作用方式与 filePattern 的配置密切相关。

1.如果filePattern中仅含有date/time pattern,每次rollover时,将用当前的日期和时间替换文件中的日期格式对文件进行重命名。max参数将不起作用。

如,filePattern="logs/app-%d{yyyy-MM-dd}.log"

2.如果 filePattern 中仅含有整数计数器(即%i ),每次 rollover 时,文件重命名时的计数器将每次加1(初始值为1),若达到 max 的值,将删除旧的文件。

如,filePattern="logs/app-%i.log"

3.如果 filePattern 中既含有 date/time pattern,又含有 %i,每次 rollover 时,计数器将每次加 1,若达到 max 的值,将删除旧的文件,直到 data/time pattern 不再符合,被替换为当前的日期和时间,计数器再从1开始。

如,filePattern="logs/app-%d{yyyy-MM-dd HH-mm}-%i.log"

Appender 类型

FileAppender(File)、RandomAccessFileAppender(RandomAccessFile)

  • 相同点:写入日志信息到文件
  • 不同点:使用的 I/O 实现类不同,前者使用 FileOutputStream,后者使用 RandomAccessFile

官方文档说是在 bufferedIO=true (默认是 true )的情况下,性能提升 20% ~ 200%

常用属性:

  • fileName:来指定文件位置,文件或目录不存在则会自动创建。
  • immediateFlush:是否每次写入都要立刻刷新到硬盘中。默认 true,如果使用默认值可能会影响性能。

RollingFileAppender(RollingFile)、RollingRandomAccessFileAppender(RollingRandomAccessFile)

  • 相同点:写入日志信息到文件
  • 不同点:使用的 I/O 实现类不同,前者使用 FileOutputStream,后者使用 RandomAccessFile
  • 上一对的实现类不能进行「日志滚动」,而带有 rolling 字样的 appender 就可以实现「滚动」功能。有「滚动」,会判断是否满足封存文件的要求,执行日志存档操作。

RollingRandomAccessFile Appender,相比 RollingFileAppender有很大的性能提升,官网宣称是20-200%

<RollingRandomAccessFile name="File" fileName="logs/app.log"filePattern="logs/$${date:hh-mm}/%d{hh-mm-ss}.app.%i.log" ><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/><Policies><!-- 每 5s 翻滚一次 --><CronTriggeringPolicy schedule="0/5 * * * * ?" /><SizeBasedTriggeringPolicy size="10 MB"/></Policies><DefaultRolloverStrategy max="10" />
</RollingRandomAccessFile>
</Appenders>

常用属性:

  • filePattern:指定当发生Rolling时,文件的转移和重命名规则。至于其中的 $${date:hh-mm} 是表示了一个文件夹(以 小时-分钟)命名。
  • DefaultRolloverStrategy 指定了如何(How)进行翻滚,并且指定了最大翻滚次数(影响%i参数值),超过次数之后会按照相应的规则删除旧日志。
  • Policies: 这里就是规定了何时进行滚动(When),可以有多个Policy。
    • CronTriggeringPolicy,比如设置每 5s 进行一次翻滚
    • SizeBasedTriggeringPolicy 指定当文件体积大于size指定的值时,触发Rolling。例如,如果当前文件超过了 10MB,但是文件的名字还没有进行翻滚(建立新文件),那么就会用%i的方式进行翻滚。

如果配置的是 RollingFileRollingRandomAccessFile,则必须配置一个 Policy

翻滚理解

假设计数器次数设为2次 <DefaultRolloverStrategy max="2" />filePattern 中既含有 date/time pattern,又含有 %i

当满足翻滚触发条件时(时间间隔到了 OR 文件大小超了),就会启动 Rolling

app.log

第一次翻滚:app.log app.1.log // app.log -> app.1.log
第二次翻滚:app.log app.1.log app.2.lop // app.log -> app.2.log

一个循环结束,到达了最大保存数 2 了,那么,app1.log 会被删除,下一个 app3.log 就会覆盖 app2.logapp2.log会改名为app1.log

第三次翻滚:app.log app.2.lop app.3.lop // app.log -> app.3.log
第四次翻滚:app.log app.3.lop app.4.lop // app.log -> app.4.log

理解:编号最近的一次也就是最新的一次 log,而采取了 Policy 方式的日志,fileName 中保存的日志将不会是全量的日志,而是根据你 Policy 的条件切分后的最近一次的日志内容。

  • 博客园-Log4j2中RollingFile的文件滚动更新机制 滚动机制介绍的很详细
  • CSDN-log4j2教程【RollingFileAppender】

一个 Appender 示例

按月归档日志,按日进行切分,限制单文件大小为 500MB, 一天最多生成20个文件,也就是(20 * 500)MB大小的日志

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30"><Appenders><RollingRandomAccessFile name="File" fileName="logs/app.log"filePattern="logs/$${date:yyyy-MM}/%d{yyyy-MM-dd}.app.%i.log" ><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/><Policies><TimeBasedTriggeringPolicy interval="1" modulate="false"/><SizeBasedTriggeringPolicy size="500MB"/></Policies><DefaultRolloverStrategy max="20" /></RollingRandomAccessFile></Appenders><Loggers><Root level="info"><AppenderRef ref="File"/></Root></Loggers></Configuration>

Logger

简单说Logger就是一个路由器,指定类、包中的日志信息流向哪个管道,以及控制他们的流量(日志级别)

Logger 部分为两个Logger:

  • Root(必须配置)
  • Logger

注意:Logger中也可以加过滤器的

日志重复打印问题

如果 Root 中的日志包含了 Logger 中的日志信息,并且 AppenderRef 是一样的配置,则日志会打印两次。

这时候我们需要使用一个 Logger 的属性来解决,那就是 additivity,其默认值为 true,需要配置为false

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30"><Appenders><Console name="Console"><PatternLayout><Pattern>%d %p %c{1.} [%t] %m%n</Pattern></PatternLayout></Console></Appenders><Loggers><Logger name="me.master.snail.log.LogMain" level="info" additivity="false"><AppenderRef ref="Console"/></Logger><Root level="trace"><AppenderRef ref="Console"/><Filters><LevelRangeFilter minLevel="error" maxLevel="info" onMatch="ACCEPT" onMismatch="DENY" /></Filters></Root></Loggers>
</Configuration>
  • Root Logger 只能有一个,普通的 Logger 可以定义多个,可以细致到给某个类定义;
  • 多个 Logger 配置重复了,在日志文件中会重复;
  • 每一个 Logger 对应的 name 是包路径,含义是,在 name 包下的类使用 AppenderRef 指向的日志模板来输出日志。
  • 不同的LogConfig之间其实是有继承关系的,子LogConfig 会继承 parent 的属性,而所有 LogConfig 都继承自 Root LogConfig。所以即使只配置了root logger,你一样可以在任何地方通过 LoggerFactory.getLogger 获取一个 logger 对象,记录日志
  • 先配置一个root,让所有需要使用日志的logger继承,然后对有特别需要的logger进行特殊的配置,比如我们希望org.springframework包只记录error以及warn级别的log,再比如,我们希望能显示mybatis执行的sql的日志,都可以进行个性化的配置

Logger 等级实验

<logger name="org.springframework" level="INFO" additivity="true"><AppenderRef ref="InfoLog"/>
</logger><Root level="ERROR" additivity="true"><AppenderRef ref="Console"/><AppenderRef ref="InfoLog"/><AppenderRef ref="WarnLog"/><AppenderRef ref="ErrorLog"/>
</Root>
  • ROOT 等级设为 ERROR 时,org.springframework Logger 等级设为 OFF 时,发现原来的 warn.loginfo.log 文件中,都只有级别大于或等于 ERROR 的日志信息了;
  • ROOT 等级设为 ERROR 时,org.springframework Logger 等级设为 INFO 时,发现info.log 文件中,增加了 org.springframework 包的相关 INFO 级别的日志信息了;

总结

  • Logger 日志等级和 appender 日志等级的关系:logger 日志等级和 appender 日志登记,谁「高」听谁的;
  • 普通 Logger 的优先级高

参考

  • 掘金-zdran-Spring Boot 学习笔记(二) 整合 log4j2 博主写了Spring Boot 教程
  • 王磊的博客-Spring Boot(十)Logback和Log4j2集成与日志发展史 介绍了 SpringBoot 和 log4j2 的结合
  • 博客园-蜗牛大师-浅谈Log4j2日志框架及使用 介绍的非常详细,推荐
  • CSDN-Log4j2使用 介绍的很详细
  • 博客园-Log4j2之Appenders 对 appender 介绍详细
  • SpringBoot + Log4j2使用配置 异步日志介绍的比较多
  • CSDN-详解log4j2(下) - Async/MongoDB/Flume Appender 按日志级别区分文件输出 介绍了一下不常用的用法,比如将日志存到数据库中

转载于:https://www.cnblogs.com/michael-xiang/p/10582300.html

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

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

相关文章

学习java过程中

今天看了一个java的代码&#xff0c;结果出现Class bytes found but defineClass()failed for的错误&#xff0c;在网上google了一把&#xff0c;找到原因是&#xff1a;我的编译环境的jdk和代码的jdk不一致&#xff0c;比原来的jdk要高。换一下就解决了。转载于:https://www.c…

linux系统远程教程,Linux下实现远程协助

一、检查系统是否安装有tcl和expect这2个软件包[rootlocalhost:~]$ rpm -qa | grep tcltcl-8.4.7-2tclx-8.3.5-4[rootlocalhost:~]$ rpm -qa | grep expectexpect-5.42.1-1二、检查是否有kibitz命令[rootlocalhost:~]$ whereis kibitzkibitz: /usr/bin/kibitz /usr/share/man/m…

图片相似度对比原理_设计原理:对比和相似性的应用

图片相似度对比原理You know why you are able to read this article right now apart from the availability of your eyes, internet, device, etc.? What is the font color of this text you’re reading? — Black. What is the background color of this page you’re …

学习尤雨溪写的 Vue3 源码中的简单工具函数

大家好&#xff0c;我是若川。最近组织了源码共读活动。每周读 200 行左右的源码。很多第一次读源码的小伙伴都感觉很有收获&#xff0c;感兴趣可以加我微信ruochuan12&#xff0c;拉你进群学习。初学者也能看懂的 Vue3 源码中那些实用的基础工具函数本文是纪年小姐姐源码共读第…

APK 本地化

一个APK反编译利器Apktool(android汉化)2010-07-19 18:52转载自&#xff1a;http://blog.sina.com.cn/s/blog_5752764e0100kv34.html APK 本地化 [http://www.andmoto.com/viewthread.php?tid3873]说起APK的汉化&#xff0c;目前大部分教程都是让用Hex Workshop或者Android R…

Linux manjaro系统安装后无法连接wifi,解决方案

2019独角兽企业重金招聘Python工程师标准>>> 笔记本为联想 thinkpad E480 首先通过命令lspci -k看一下原因是否为缺少wifi驱动&#xff0c;如下&#xff0c;如果没有Kernel driver in use&#xff0c;说明缺少驱动。05:00.0 Network controller: Realtek Semiconduc…

检测输入路径是否存在错误_为什么存在用户输入错误

检测输入路径是否存在错误Errors are a fact of life when using almost any type of software. Forms are the worst though. Nothing is more frustrating than filling out a form and getting a robotic message from the computer telling you that you have failed, plea…

若川邀你进 源码共读 群~长期交流学习

大家好&#xff0c;我是若川。这是一个愉快的周六~估计还是有很多读者不知道我。若川名字由来是取自&#xff1a;上善若水&#xff0c;海纳百川。顺便放两篇文章。我读源码的经历&#xff0c;跟各位读者朋友分享下公众号运营策略加我微信进 源码共读 群最近组织了近200人每周源…

2005 打开 2010 项目经验总结

下面是网上的直接复制粘贴&#xff1a;网址为 http://hi.baidu.com/zealot886/blog/item/7364d4266a2a1555ac34dea6.html/cmtid/65ff140a660e02246159f3db 这里是我自己的总结 &#xff08; 1、用vs2010 将该解决方案的所有 项目都改为 net 2.0&#xff08;方法&#xff0c;右击…

读取linux的运行状态,Linux下安装使用sar工具来获取系统运行状态

sar 找出系统瓶颈的利器sar是System Activity Reporter(系统活动情况报告)的缩写。sar工具将对系统当前的状态进行取样&#xff0c;然后通过计算数据和比例来表达系统的当前运行状态。它的 特点是可以连续对系统取样&#xff0c;获得大量的取样数据&#xff1b;取样数据和分析的…

说说 Spring 的事务同步管理器

Spring 将 JDBC 的 Connection、Hibernate 的 Session 等访问数据库的连接或者会话对象统称为资源&#xff0c;这些资源在同一时刻是不能多线程共享的 。 为了让 DAO 或 Service 类可以实现单例模式&#xff0c; Spring 的事务同步管理类 org.springframework.transaction.supp…

错过校招_我们在用户测试中容易错过的事情

错过校招What makes a tool well designed? As a designer, I’ve thought about this question for a long time, and over the past few years I’ve developed a system that I now use with every new project I approach, from small startups to large companies like L…

这些 JS 中强大的操作符,总有几个你没听说过

大家好&#xff0c;我是若川。今天推荐一篇相对简单些的文章。大家应该都知道了我最近组织了源码共读活动&#xff0c; 有小伙伴表示读源码上瘾&#xff0c;也很有收获。工作0-5年都可以参与。感兴趣可以加我微信 ruochuan12 私信 源码 进群。1. 数值分割符 _2. 逗号运算符 ,3.…

Class 创建性能大比拼(反射,泛型反射,泛型创建,缓存Emit,非缓存Emit)

一说到反射&#xff0c;很多人都想到了性能&#xff0c;更有甚者直接说“慎用反射&#xff0c;遗患无穷”&#xff0c;“用反射&#xff0c;感觉怎么像是退步啊&#xff5e;”&#xff0c;看到这种言论&#xff0c;直接把反射妖魔化了&#xff0c;如果这种言论长此以往&#xf…

es6冲刺01

1、let/const 1)作用域&#xff1a;es5中有全局作用域、函数作用域。es6中新增了块级作用域 2&#xff09;let定义的变量在所在块级作用域外失效&#xff0c;严格模式下失效后直接报错&#xff0c; 且不允许重复声明同名变量 3)const用于声明常量&#xff0c;声明时必须赋值&am…

linux网卡固件名,修改CentOS7网卡名称为传统名称eth0格式

使用CentOS7以前系统的小伙伴装完CentOS7以后发现了一个问题&#xff0c;那就是网卡名改变为了“en016777736”&#xff0c;而不是以前的eth0的简易模式了&#xff0c;如图&#xff1a;以往的CentOS7以前的系统网卡命名虽然简单方便&#xff0c;但也会带来一些问题&#xff0c;…

Baymard Institute:基于UX的最佳实践的光荣的,循证的工具

重点 (Top highlight)I realized I wanted to write this piece when I mentioned the Baymard Institute to a User Researcher with 10 years of experience and they had no idea what I was talking about. They aren’t alone! I’ve gotten plenty of raised eyebrows on…

Vue 3.2 发布了,那尤雨溪是怎么发布 Vue.js 的?

1. 前言大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12&#xff0c;长期交流学习。之前写的《学习源码整体架构系列》 包含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4十篇源码文章。…

wireshark使用教程 linux,Linux入门教程:ubuntu下安装wireshark(以及配置非root),这个强大的工具可以捕...

Linux入门教程:ubuntu下安装wireshark(以及配置非root),这个强大的工具可以捕Wireshark是世界上最流行的网络分析工具。这个强大的工具可以捕捉网络中的数据&#xff0c;并为用户提供关于网络和上层协议的各种信息。与很多其他网络工具一样&#xff0c;Wireshark也使用pcap net…

IronPython和C#执行速度对比

其实我自己对执行速度这个问题本来并没有什么兴趣&#xff0c;因为以前的经验告诉我&#xff1a;除非是运算密集型的程序&#xff0c;否则脚本语言和编译型语言使用起来速度没有多大差别。但是我们公司有个人知道我的想法以后&#xff0c;天天在我耳边嚷嚷脚本运行速度太慢&…