日志搞不定?手把手教你如何使用Log4j2
系列文章目录
从零开始,手把手教你搭建Spring Boot后台工程并说明
Spring框架与SpringBoot的关联与区别
SpringBean生成流程详解 —— 由浅入深(附超精细流程图)
Spring监听器用法与原理详解
Spring事务畅谈 —— 由浅入深彻底弄懂 @Transactional注解
面试热点详解 ——BeanFactory 和 FactoryBean 的关联与区别
忽视日志吃大亏,手把手教你学习Spring Boot日志
日志搞不定?手把手教你如何使用Log4j2
- 系列文章目录
- 一、引入依赖
- 二、配置文件示例
- 三、PatternLayout
- 四、Appenders
- 五、Console
- 1. Filter 过滤器
- 六、RollingFile
- 1. filePattern 文件样板
- 2. Policies 文件原则
- 3. DefaultRolloverStrategy
- 七、Loggers
- 八、异步日志
- 1. 基础使用
- 2. 详细配置
- 总结

上一次我们介绍了Springboot 下的几种常用日志插件,今天我们就专注讲解其中一个最年轻的日志插件,即Log4j2。Log4j2目前应用非常广泛,各方面较之前辈Log4j 都有不小的提升,不过要想利用好他,还得经过一定的学习,尤其是搞清楚它的种种配置,下面就让我们开始今天的进阶之旅。
📕作者简介:战斧,从事金融IT行业,有着多年一线开发、架构经验;爱好广泛,乐于分享,致力于创作更多高质量内容
📗本文收录于 Spring全家桶 专栏,有需要者,可直接订阅专栏实时获取更新
📘高质量专栏 云原生、RabbitMQ、Spring全家桶 等仍在更新,欢迎指导
📙Zookeeper Redis kafka docker netty等诸多框架,以及架构与分布式专题即将上线,敬请期待
一、引入依赖
我们可以使用以下依赖为我们项目引入Log4j2框架
<!--Log4j2自带的日志门面-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.13.3</version>
</dependency>
<!--Log4j2具体的日志实现-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.13.3</version>
</dependency>
当然,像上期所说,我更建议直接使用以下log4j-slf4j-impl包
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.13.3</version>
</dependency>
该包包含Log4j2的实现,也能对接SLF4J,更适合在项目中应用。

二、配置文件示例
我们看如下一份配置文件:log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG" monitorInterval="30"><!-- 输出格式 --><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} - [%t] %-5level %logger{36} - %msg%n"/><Appenders><!-- 控制台输出 --><Console name="Console" target="SYSTEM_OUT"><!-- 过滤器 --><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/><!-- 缓冲bufferSize,默认值为256,可调整至1-256之间 --><Buffered mode="ONCE" bufferSize="256"/></Console><!-- 文件输出 --><RollingFile name="RollingFile" fileName="/path/to/logs/test.log" filePattern="/path/to/logs/$${date:yyyy-MM}/test-%d{yyyy-MM-dd}-%i.log"><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/><!-- 输出格式 --><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/><!-- 触发策略 --><Policies><!-- 按时间滚动 --><TimeBasedTriggeringPolicy/><!-- 按文件大小滚动,这里设置10MB --><SizeBasedTriggeringPolicy size="10MB"/></Policies><!-- 滚动策略 --><DefaultRolloverStrategy max="10"/></RollingFile></Appenders><Loggers><!-- 默认日志 --><Root level="INFO"><AppenderRef ref="Console"/><AppenderRef ref="RollingFile"/></Root><!-- 指定包名的日志 --><Logger name="com.example" level="DEBUG" additivity="false"><AppenderRef ref="Console"/><AppenderRef ref="RollingFile"/></Logger></Loggers>
</Configuration>
三、PatternLayout
PatternLayout是一种日志输出格式,用于设置日志输出的布局格式。该格式使用一系列特定的占位符,每个占位符都代表一种日志信息,例如输出日志事件的时间、线程名、日志级别、日志内容等
- %d:输出日志事件的时间戳
- %t:输出线程名
- %-5level:输出日志级别,包括5个字符位置(例如,DEBUG、INFO、WARN、ERROR、FATAL),将小于5个字符的级别左对齐
- %logger{36}:输出日志记录器的名称,最多36个字符
- %msg:输出日志内容
- %n:输出平台相关的行分隔符
- %M:输出产生日志事件的方法名
- %L:输出语句在源文件中出现的行号
- %l:输出语句的精确位置,包含类、方法和行号
- %c:输出日志事件相关的类名
我们举两个例子,假如我们配置文件这么写
<PatternLayout pattern="%d{ISO8601} [%t] %-5level %logger{36} - %msg%n" />
输出的结果就为
2023-08-23T19:34:26,068 [main] INFO com.zhanfu.Main - This is a Main info message.
如果配置文件如下
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%t] %c{1.} %M [%L] -| %msg%n"/>
输出的结果就为
2023-08-23 20:18:06.833 |-INFO [main] c.z.Main main [11] -| This is a Main info message.
其中,%c{1.} 表示输出日志事件相关的类名,只取最右边的类名,前面的层级,只输出一个字母;如果配置文件如下
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%t] %c{2.} [%l] -| %msg%n"/>
输出的结果就为
2023-08-23 20:21:00.753 |-INFO [main] co.zh.Main [com.zhanfu.Main.main(Main.java:11)] -| This is a Main info message.
四、Appenders
Appenders是指日志输出的目的地。它定义了日志事件要被输出到哪些地方,例如控制台、文件、数据库等,其下可以配置不同类型的Appender,我们罗列其中常见的一些:
- Console:向控制台输出日志。
- File:将日志记录到文件中。
- RollingFile:将日志记录到指定大小的文件中,并支持文件滚动。
- Socket:将日志发送到远程 Socket 服务器
- Kafka:将日志发送到 Kafka 消息队列。
- Redis:将日志发送到 Redis 服务器。
- Jdbc:将日志记录到数据库中
对于单机应用来说,Console 与 File 是最为常用的,但对于集群,通常需要进行日志采集,此时也会通过Socket或Kafka等将日志发送至其他位置
五、Console
Console标签是用于将日志输出到控制台的标签,其有以下可配置的属性:
- name:配置Console标签的名称。
- target:配置Console标签输出的目标,可以是System.out或System.err,默认为System.out。
- follow:配置Console标签,控制台是否跟随日志进行滚动,默认为false。
- immediateFlush:配置Console标签的输出是否立即刷新,默认为true
Console标签可以包含以下子标签
- PatternLayout:上文已经介绍过,可以自定义输出的格式。此标签有以下可配置的属性:pattern。
- ThresholdFilter:指定哪些日志级别应该被输出或被拒绝。此标签有以下可配置的属性:
- level - 匹配的可用的日志级别。
- onMatch - 当匹配或高于指定日志等级时的动作,可以为 ACCEPT、DENY、NEUTRAL 之一,默认为 NEUTRAL
- onMismatch - 当低于指定日志等级时的动作,可以为 ACCEPT、DENY、NEUTRAL ,默认为 DENY
- RegexFilter:使用正则表达式来计算哪些日志消息应该被过滤。
1. Filter 过滤器
比如我们可以做出如下配置
<Console name="console" target="SYSTEM_OUT"><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/><ThresholdFilter level="WARN" onMatch="NEUTRAL " onMismatch="DENY"/><RegexFilter regex=".*\[(main|AsyncLogger)\].*"/>
</Console>
我们可以发现 ThresholdFilter 的设置为 level=“WARN” onMatch="NEUTRAL " onMismatch=“DENY”,这种设置的意思就是当日志等级高于或等于 WARN 时,就通过了级别过滤器,但还需要经过后续过滤器如的进一步筛选。而低于 WARN 则直接拒绝打印。
比如我们有这样的过滤设置
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="DENY"/>
第一个过滤器否决打印error级别,第二个过滤器接收大于info级别,并且否决低于info级别的,那最终只能打印info 和 warn 级别了。此处的第三个过滤器实际上没被用到

六、RollingFile
RollingFile标签用于配置滚动日志输出到文件的方式,什么叫滚动日志,就是日志输出在一个文件中,当某个事件发生时(如文件达到一定大小),会把这个文件进行归档封存,然后新建一个文件,再向新建的文件中输出日志,有点像现代的饮料生产线,灌满一瓶立即灌下一瓶

以下是RollingFile标签的常用属性:
- name:指定RollingFile的名称,用于标识该Appender。
- fileName:指定日志文件的路径和名称。
- filePattern:指定日志文件备份的命名模式,使用%符号指定日期、数字等占位符。
RollingFile 可以包含下列子标签
- policies:指定日志滚动策略,包括时间、大小等,可以使用TimeBasedTriggeringPolicy、SizeBasedTriggeringPolicy等标签。
- PatternLayout:指定日志输出格式,上面已经说过。
我们可以以下面的配置为例子进行进一步解释
<Appenders><RollingFile name="RollingFile"fileName="/logs/app.log"filePattern="logs/app-%d{yyyy-MM-dd-HH}-%i.log"><PatternLayout><pattern>%d %p %c{1.} [%t] %m%n</pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy interval="1" modulate="true"/><SizeBasedTriggeringPolicy size="10 MB"/></Policies><DefaultRolloverStrategy max="5"/></RollingFile>
</Appenders>
该示例配置了一个名为“RollingFile”的RollingFile Appender,将日志记录到“/logs/app.log”文件中。文件名模式为“/logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log.gz”
1. filePattern 文件样板
filePattern是RollingFile Appender的一个属性,用于指定根据何种模式生成归档滚动日志文件的名称。filePattern可以使用一些特定的占位符,以便在滚动时自动生成新的日志文件名
filePattern占位符:
- %d - 日期格式化器的格式化日期。例如:2018-09-19
- %i - 滚动文件的索引编号,从1开始。
- %n - 系统的行分隔符
- %m - 文件名
比如我们设置的
filePattern="logs/app-%d{yyyy-MM-dd-HH}-%i.log"
filePattern中的时间占位符%d在滚动时会自动更新为当前时间,索引号%i也会自动递增以避免覆盖先前的日志,所以最终会生成以下的归档文件名
app-2021-08-30-15-1.log
app-2021-08-30-15-2.log
app-2021-08-30-15-3.log
2. Policies 文件原则
Policies用于定义何时触发滚动动作以生成新的日志文件,可以定义TimeBased和SizeBased两种类型的触发策略。
-
TimeBasedTriggeringPolicy
TimeBasedTriggeringPolicy是基于时间的触发策略,当指定的时间间隔过去时,将会触发滚动动作。
属性:- interval:时间间隔,单位为filePattern中的最小时间单位,如%d{yyyy-MM-dd-HH}代表最小时间为小时,默认数值为1。
- modulate:是否调节,即使用0时0分作为起点,当开启时,下一次生成日志文件的时间为0时0分 + n 个interval
-
SizeBasedTriggeringPolicy
SizeBasedTriggeringPolicy是基于日志文件大小的触发策略,当日志文件大小达到指定的大小时,将会触发滚动动作
属性:
-size:指定的日志文件大小,单位为字节,默认值为10MB
需要注意的是,
1.若modulate=true, 则封存时间将以0点为边界进行偏移计算。比如,modulate=true,interval=4hours,那么假设上次封存日志的时间为03:00,则下次封存日志的时间为04:00,之后的封存时间依次为08:00,12:00,16:00…如果modulate=false,则代表不调整,封存时间严格遵循离上次间隔4小时,则封存时间依次为07:00,11:00,15:00…
2.Policies可以同时指定多个触发策略,从而满足多种情况下的滚动需求,例如
<RollingFile name="example"fileName="logs/logfile.log"filePattern="logs/logfile-%d{yyyy-MM-dd_HH}.log"><Policies><TimeBasedTriggeringPolicy interval="5" modulate="true"/><SizeBasedTriggeringPolicy size="100 MB"/></Policies><DefaultRolloverStrategy max="5"/>
</RollingFile>
以上配置将在每隔五小时或100M时将生成一个新的日志文件,文件名会包含时间戳
3. DefaultRolloverStrategy
DefaultRolloverStrategy是一个滚动策略,可以设置滚动文件数量以及删除策略,它有以下几个属性:
- max:最大日志文件数量,默认为7。
- min:最小日志文件数量,默认为1。
- fileIndex:日志文件序号的格式,默认为%s。
- compressionLevel:压缩级别,默认为6
同时,它也支持以下子标签:
- Delete:用于配置何时删除过期的日志文件。
- CustomDelete:用于自定义删除过期日志文件的行为。
比如以下设置
<DefaultRolloverStrategy max="10" min="2" fileIndex="max"><Delete basePath="logs" maxDepth="2"><IfFileName glob="*.log" /><IfLastModified age="14d" /></Delete>
</DefaultRolloverStrategy>
这代表日志文件最多保留10个,最少保留2个,日志文件序号格式为max,同时配置了Delete子标签,用于删除14天前的logs文件夹下,两层路径以内的所有.log文件。其中,Delete子标签中的IfFileName和IfLastModified子标签用于指定删除条件,只有同时满足两个条件才进行删除操作
需要注意的是,文件的多少与我们设置的 filePattern 也有关系,其限制的其实是%i的最大数值:
比如
filePattern="logs/app-%d{yyyy-MM-dd-HH}-%i.log
这代表文件归档最小单位为小时,如果我们的 DefaultRolloverStrategy max="10" ,那么一个小时内最多保留10个文件,
当产生第11个文件时,将会删除本小时最早的日志文件
七、Loggers
Loggers和Logger标签是用来配置日志记录器的。Loggers标签是定义所有Logger的容器标签,而Logger标签则是具体的记录器配置,Logger 标签有以下属性:
- name:指定Logger的名称,名称可以是类名、包名或自定义的名称。例如,如果设置为com.example.MyLogger,则日志内容将包含该名称。
- level:指定Logger的日志级别,如果不指定则将继承Loggers标签中的默认级别。例如,如果设置为INFO,则只会记录INFO及以上级别的日志。
- additivity:指定Logger是否继承它的父Logger的Appender。如果不指定,则将继承Loggers标签中的默认值。
- includeLocation:指定是否包含日志输出的调用位置信息。如果设置为true,则输出的日志将包括文件名、方法名和行号等信息。
- suppressExceptions:指定是否屏蔽日志输出中的异常信息。如果设置为false,则将在日志输出中包含异常信息
Logger标签还包括AppenderRef子标签,用于指定Logger所要使用的Appender。可以在Logger标签中指定多个AppenderRef,这样就可以将同一个Logger的日志输出到多个Appender中
下面是一个典型的Loggers与Logger的例子:
<Appenders><RollingFile name="MyAppender"fileName="/logs/app.log"filePattern="logs/app-%d{yyyy-MM-dd-HH}-%i.log"><PatternLayout><pattern>%d %p %c{1.} [%t] %m%n</pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy interval="1" modulate="true"/><SizeBasedTriggeringPolicy size="10 MB"/></Policies><DefaultRolloverStrategy max="5"/></RollingFile><Console name="Console" target="SYSTEM_OUT"><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/><Buffered mode="ONCE" bufferSize="256"/></Console>
</Appenders>
<Loggers><Logger name="com.example.MyLogger" level="info"><AppenderRef ref="MyAppender"/></Logger><Root level="error"><AppenderRef ref="Console"/></Root>
</Loggers>
这个例子中定义了两个Logger:com.example.MyLogger和Root。com.example.MyLogger的级别是INFO,只会记录INFO及以上级别的日志,并且使用MyAppender作为输出目的地;Root的级别是ERROR,将所有ERROR及以上级别的日志输出到Console中
八、异步日志
1. 基础使用
我们上面讲解的其实都是同步输出的日志,而Log4j2 的一大特性就是其异步输出能力,我们可以参考如下配置
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><File name="File" fileName="logs/app.log"><PatternLayout><Pattern>%d %p %C{1.} [%t] %m%n</Pattern></PatternLayout></File><Async name="Async"><AppenderRef ref="File"/></Async></Appenders><Loggers><Logger name="com.example" level="debug"/><Root level="info"><AppenderRef ref="Async"/></Root></Loggers>
</Configuration>
- 首先,定义了一个文件输出的Appender,文件名为logs/app.log,并且在PatternLayout中指定了日志输出的格式。
- 接着,定义了一个异步的Appender,它的AppenderRef指向了文件输出的Appender。
- 最后,将Root Logger的级别设为info,并让它的AppenderRef指向了异步的Appender。
这个配置可以实现将日志异步输出到logs/app.log文件中,并且可以指定日志输出的格式
我们可以做一个测试,分别用同步和异步的方式进行运行:
public static void main(String[] args) {long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000; i++) {log.info("This is a debug message. Count is "+i);}long endTime = System.currentTimeMillis();log.info("Total time: "+(endTime - startTime)+"ms");}


可以看到,异步确实能节约很多时间。
2. 详细配置

我们注意到最后一个阻塞队列的信息,其实不难想象,这里的日志异步依赖的就是高性能队列,不仅log4j2,像Netty等对性能有极高要求的框架,在队列的选择上都是精益求精的。log4j2支持四种阻塞队列,如下:

比如我们如果想指定一种阻塞队列,可以这么设置:
<Configuration name="LinkedTransferQueueExample"><Appenders><List name="List"/><Async name="Async" bufferSize="262144"><AppenderRef ref="List"/><AsyncQueueFullPolicy type="Discard"/><!-- 指定阻塞队列 --><LinkedTransferQueue/></Async></Appenders><Loggers><Root><AppenderRef ref="Async"/></Root></Loggers>
</Configuration>
当然,如果你想使用 DisruptorBlockingQueue,那还需要引入 disruptor 包,注意版本兼容,log4j-2.17.1 对应了disruptor-3.4.0
<dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.4.0</version>
</dependency>
总结
本次我们比较详细的介绍了Log4j2框架的种种配置,虽然没有讲原理,但解释了不少配置并给出示例,相信即便是新手,阅读完也能应对大部分使用场景了。希望大家能够举一反三,灵活运用。当然,有问题也可以直接去官方文档进行查阅和学习,点此直达log4j2 手册
相关文章:
日志搞不定?手把手教你如何使用Log4j2
系列文章目录 从零开始,手把手教你搭建Spring Boot后台工程并说明 Spring框架与SpringBoot的关联与区别 SpringBean生成流程详解 —— 由浅入深(附超精细流程图) Spring监听器用法与原理详解 Spring事务畅谈 —— 由浅入深彻底弄懂 Transactional注解 面试热点详解…...
基于Googlenet深度学习网络的交通工具种类识别matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ....................................................................................% 获…...
R语言04-R语言中的列表
概念 在R语言中,列表(List)是一种复杂的数据结构,用于存储不同类型的元素,包括向量、矩阵、数据框、函数等。列表是一种非常灵活的数据结构,可以将不同类型的数据组合在一起,类似于Python中的字…...
[Linux]进程概念
[Linux]进程概念 文章目录 [Linux]进程概念进程的定义进程和程序的关系Linux下查看进程Linux下通过系统调用获取进程标示符Linux下通过系统调用创建进程-fork函数使用 进程的定义 进程是程序的一个执行实例,是担当分配系统资源(CPU时间,内存…...
GEE/PIE遥感大数据处理与应用
随着航空、航天、近地空间等多个遥感平台的不断发展,近年来遥感技术突飞猛进。由此,遥感数据的空间、时间、光谱分辨率不断提高,数据量也大幅增长,使其越来越具有大数据特征。对于相关研究而言,遥感大数据的出现为其提…...
● 647. 回文子串 ● 516.最长回文子序列
647. 回文子串 class Solution { public:int countSubstrings(string s) {vector<vector<bool>>dp(s.size(),vector<bool>(s.size(),false));int res0;for(int is.size()-1;i>0;i--){for(int ji;j<s.size();j){if(s[i]s[j]){if(j-i<1){res;dp[i][…...
Mysql group by使用示例
文章目录 1. groupby时不能查询*2. 查询出的列必须在group by的条件列中3. group by多个字段,这些字段都有索引也会索引失效,只有group by单个字段索引才能起作用4. having条件必须跟group by相关联5. 用group by做去重6. 使用聚合函数做数量统计7. havi…...
淘宝商品详情采集接口item_get-获得淘宝商品详情(可高并发线程)
获得淘宝商品详情页面数据采集如下: taobao.item_get 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)注册key账号接入secretString是调用密钥api_nameString是API接口名称(包括在请求地址中࿰…...
uniapp写公众号h5开发 附件上传 下载功能
一。 uni-app实现文件上传功能 目前,找到一款第三方插件 文件上传插件地址 https://ext.dcloud.net.cn/plugin?id=1015 将插件下载并导入项目中直接拿来使用,插件市场也有对改插件用法的描述。 用法: 1. 以下代码写于根目录下第一个view顶部或跟在自定义导航栏后面 // 以…...
机器学习基础09-审查分类算法(基于印第安糖尿病Pima Indians数据集)
算法审查是选择合适的机器学习算法的主要方法之一。审查算法前并 不知道哪个算法对问题最有效,必须设计一定的实验进行验证,以找到对问题最有效的算法。本章将学习通过 scikit-learn来审查六种机器学习的分类算法,通过比较算法评估矩阵的结果…...
C++ sort与优先队列排序的区别
int main() {vector<int> data{3, 1, 2};cout << "从小到大排序" << endl;sort(data.begin(), data.end(), std::less<int>());printContainer(data);auto cmp1 [](int x, int y) { return x < y; };sort(data.begin(), data.end(), cmp…...
【Rust】Rust学习 第十九章高级特征
现在我们已经学习了 Rust 编程语言中最常用的部分。在第二十章开始另一个新项目之前,让我们聊聊一些总有一天你会遇上的部分内容。你可以将本章作为不经意间遇到未知的内容时的参考。本章将要学习的功能在一些非常特定的场景下很有用处。虽然很少会碰到它们…...
C++ 纯虚函数和虚函数的区别
在 C 中,虚函数(Virtual Function)和纯虚函数(Pure Virtual Function)都是用于实现多态性的机制,但它们之间有一些关键的不同。 虚函数(Virtual Function) 定义:在基类…...
Go中的有限状态机FSM的详细介绍 _
1、FSM简介 1.1 有限状态机的定义 有限状态机(Finite State Machine,FSM)是一种数学模型,用于描述系统在不同状态下的行为和转移条件。 状态机有三个组成部分:状态(State)、事件(…...
Python入门教程 | Python3 基本数据类型
赋值 Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。 在 Python 中,变量就是变量,它没有类型,我们所说的"类型"是变量所指的内存中对象的类型。 等号(ÿ…...
STM32移植u8g2玩转oled 用软件iic实现驱动oled
移植u8g2到stm int fputc(int ch,FILE *f) {ITM_SendChar(ch);return (ch); }void delay_us(uint32_t time) {uint32_t i8*time;while(i--); }uint8_t STM32_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {//printf("%s:msg %d,arg_int …...
C++ 学习系列 -- string 实现
string是C标准库的重要部分,主要用于字符串处理。这里我们自己实现一个简单版本的 string. 一 思路 string 类中应该包含如下: 1. 类成员变量:char* m_data,利用 char* 指针存放字符串 2. 成员函数: 2.1 size(…...
C语言小练习(三)
🌞 “也许你感觉自己与周遭格格不入,但正是那些你一人度过的时光,让你变得越来越有意思,等有天别人终于注意到你的时候,他们就会发现一个比他们想象中更酷的人。”-《生活大爆炸》 Day03 📝 一.选择题&…...
2023 js逆向爬虫 有道翻译 代码
前置条件:nodejs环境、安装 crypto 和 python3环境 js.js文件: const crypto require("crypto")function decode(resp_data) {g_o ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHlg_n ydsecre…...
【物联网无线通信技术】NFC从理论到实践(FM17XX)
NFC,全称是Near Field Communication,即“近场通信”,也叫“近距离无线通信”。NFC诞生于2004年,是基于RFID非接触式射频识别技术演变而来,由当时的龙头企业NXP(原飞利浦半导体)、诺基亚以及索尼联合发起。NFC采用13.5…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
网页端 js 读取发票里的二维码信息(图片和PDF格式)
起因 为了实现在报销流程中,发票不能重用的限制,发票上传后,希望能读出发票号,并记录发票号已用,下次不再可用于报销。 基于上面的需求,研究了OCR 的方式和读PDF的方式,实际是可行的ÿ…...
