Spring Boot日志:SLF4J和Logback
日志的分类
SpringBoot中的日志库分为两种:
- 实现库:提供具体的日志实现,例如日志级别的控制、打印格式、输出目标等。
- 外观库:自身不提供日志实现,而是对其他日志库进行封装,从而方便使用。基于外观模式实现。
关于外观库的出现,可设想一下:现在有多种日志库,每一种接口都不同。于是我们在不同项目中往往需要调用不同的API。于是希望,无论哪一种日志库,我们都在外面套一层壳,使用时调用这层壳的接口,这样就统一了。这层壳就是外观库。
对于这两种库,常见的有:
- 实现库:
Log4j、Log4j2、Logback - 外观库:
Slf4j、Apache Commons Logging
通常只需要引入Slf4j依赖,然后通过Slf4j来调用Logback或Log4j。
对应地,配置文件除了对Slf4j进行配置,也可以对Logback或Log4j进行配置。
Logback相比于Log4j,性能提高了10倍以上的性能,占用的内存也变小了,并且文档十分详细。推荐使用Slf4j+Logback。
官网:
https://logback.qos.ch/
官方文档:
https://logback.qos.ch/documentation.html
Slf4j、Logback和Log4j是同一个作者,使用了相同的设计,因此Slf4j直接可调用Logback和Log4j。而对于其他日志实现库,例如java.util.logging等,需要使用一个适配器模块来将Apache Commons Logging的接口转换为Slf4j的可调用接口。
Spring Boot日志
依赖
Spring Boot默认使用Logback。只需要引入spring-boot-starter或spring-boot-starter-web就会默认包含,不需要再单独引入。
lombok中默认包含了Slf4j,因此只要引入了lombok就无需再单独引入Slf4j:
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version>
</dependency>
日志对象
要打印,就要先获取日志对象。
通常地,使用Slf4j包中的LoggerFactory来得到日志对象,然后打印:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class LoggerController {// 得到日志对象private Logger logger = LoggerFactory.getLogger(LoggerController.class);@RequestMapping("/logger")public String logger() {// 日志打印logger.trace("日志级别: trace");logger.debug("日志级别: debug");logger.info("日志级别: info");logger.warn("日志级别: warn");logger.error("日志级别: error");return "logger";}
}
如果项目中引入了lombok组件,则可在类前添加@Slf4j注解,这样就可在类中直接使用log日志对象,而无需通过LoggerFactory来获取。
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j // 通过lombok的`@Slf4j`得到日志实例
public class LoggerController {@RequestMapping("/logger")public String logger() {// 日志打印log.trace("日志级别: trace");log.debug("日志级别: debug");log.info("日志级别: info");log.warn("日志级别: warn");log.error("日志级别: error");return "logger";}
}
日志级别及配置
日志级别
日志有8个级别:
- ALL: 开启所有日志
- TRACE: 追踪,程序每次执行一步都输出。级别较低,通常不会使用
- DEBUG: 调试日志,细粒度
- INFO: 普通日志,粗粒度
- WARN: 警告日志
- ERROR: 错误日志,但不影响系统同运行
- FATAL: 严重错误日志,会导致系统退出
- OFF: 关闭所有日志
对于这8个级别,级别依次递增。级别越高,打印的日志越少。
通常地,只使用TRACE、DEBUG、INFO、WARN、ERROR这5个等级。
Spring Boot会打印指定级别及更高级的日志。
例如日志设置为INFO级,那么Spring Boot会打印INFO、WARN、ERROR。
配置文件
logback的配置可在application.yml和xml中配置,其中xml配置更加灵活。
若多个配置同时存在,则其加载顺序为:
logback.xml → application.yml → logback-spring.xml
后加载的会覆盖先加载的。因此application.yml会覆盖logback.xml中的相同配置。即后加载的优先级更高。
同理,若要使用 application.yml 中定义的变量,应使用 logback-spring.xml 。官方推荐使用 logback-spring.xml 。
logback.xml
加载logback.xml时,默认会在classpath查找以下文件: 用户自定义xml > logback-test.xml > logback.groovy > logback.xml 。
如果上述4个配置文件都不存在,那么logback会调用BasicConfigurator来创建一个最小化配置将日志输出到控制台。最小化配置会构造一个父为<ROOT>的<ConsoleAppender>,PatternLayoutEncoder为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n。
其中用户自定义xml是在application.yml中通过logging.config=classpath:logging-config.xml配置的。
yml配置
logging:config: classpath:logback.xml # 自定义配置文件pattern:file: "%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15} - %msg %n)" # 文件输出console: "%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15} - %msg %n)" # 控制台输出file:name: E:\logs\test.log # 日志文件输出路径level:root: INFO # root输出级别com.spring: WARN # 特定包的输出级别
xml结构
<?xml version="1.0" encoding="UTF-8"?>
<configuration><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"></appender><appender name="FILE" class="ch.qos.logback.core.FileAppender"></appender><appender name="ROLLING" class="ch.qos.logback.core.RollingFileAppender"></appender><root></root><logger></logger>
</configuration>
注意区分属性和配置的区别:
- 属性:标签自身的属性,直接写在标签标签内,例如
<appender name ="CONSOLE">,name就是`标签的属性。 - 配置:标签的功能设置,使用子标签进行配置,例如
<appender><file>testFile.log</file></appender>,<file>就是<appender>标签的配置。
最外层标签<configuration>
<configuration>是最外层的标签,所有其他标签都要定义在该标签内。
<configuration>可设置多个属性:
- scan: 启用扫描。默认为true。当为true时,若配置文件发生变更,将会被重新加载。
- scanPeriod: 配置文件扫描时间间隔。默认单位是毫秒,可修改。当scan为true时,此属性生效。默认时间间隔为1分钟。
- debug: 默认为false。若设为true,将实时打印出logback内部日志信息。
例如:
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<contextName>上下文名称
为了避免同一服务器上多个web应用的logger产生上下文冲突,可使用<contextName>标签设置上下文的名称,确保每个xml的<contextName>唯一即可。
例如:
<contextName>Test</contextName>
<appender>标签
<appender>标签负责写日志。定义了日志的输出位置、触发策略、格式等。注意只负责写,不负责日志等级。
一个<appender>标签负责一种日志。由于一个系统中往往包含多种日志,因此通常同时使用多个<appender>标签。
<appender>包含2个属性:
name: 指定<appender>的名称,可自定义。需在root标签中引用。class: 指定所使用实现库中Appender类的含路径名称。
关于class,有以下几个常用取值:
ch.qos.logback.core.ConsoleAppender: 输出到控制台。ch.qos.logback.core.FileAppender: 输出到静态记录文件。ch.qos.logback.core.rolling.RollingFileAppender: 输出到滚动日志文件。
ConsoleAppender
ConsoleAppender最为简单,直接输出到控制台。通常用于开发调试。
ConsoleAppender常用的设置为:
<encoder>: 对记录事件进行格式化。
例如:
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>console %d %p - %m%n</pattern></encoder>
</appender>
FileAppender
FileAppender会将所有的日志信息都写入到一个文件中。因此若append属性为true,则日志文件会越来越大。
FileAppender常用的设置为:
<file>: 文件名。无默认值,必须设置。其路径可以为绝对或相对。若上级目录不存在则会自动创建。<append>: 是否开启追加。默认为true。若为true,则新的日志会追加到文件末尾;若为false,则写新日志前会清空日志文件。<encoder>: 对记录事件进行格式化。<prudent>: 是否安全写入文件。默认为false。效率低。
例如:
<appender name="FILE" class="ch.qos.logback.core.FileAppender"><file>testFile.log</file><append>true</append><encoder><pattern>%d %p - %m%n</pattern></encoder>
</appender
RollingFileAppender
RollingFileAppender会将日志滚动记录,即写入一个文件,当满足条件时,再写入下一个文件。例如设置文件大小为30MB,则当写入的日志文件达到30MB时会再新建一个日志文件进行写入。达到条件这个行为称为滚动。
RollingFileAppender常用的设置为:
<file>: 文件名。无默认值,必须设置。其路径可以为绝对或相对。若上级目录不存在则会自动创建。<append>: 是否开启追加。默认为true。若为true,则新的日志会追加到文件末尾;若为false,则写新日志前会清空日志文件。<rollingPolicy>: 滚动策略。通常包含:滚动条件(例如最大日志文件大小),文件命名格式,日志文件保存期限,等等。<layout>: 日志输出格式。
例如:
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>testFile.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_HOME}/${app-name}-%d{yyyy-MM-dd}-%i.log</fileNamePattern><MaxHistory>30</MaxHistory><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>30MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><!-- 日志输出格式: --><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %-5level ] [ %thread ] - [ %logger{50} : %line ] - %msg%n</pattern></layout>
</appender
<rollingPolicy>滚动策略
<rollingPolicy>使用class属性指定滚动策略,不同的策略有不同的配置。
- 基于时间的滚动策略:
ch.qos.logback.core.rolling.TimeBasedRollingPolicy。
该策略根据时间来判定是否触发滚动。最常用。
常用配置为:
<fileNamePattern>: 文件名格式,必须。可以使用%d转换符来接收一个java.text.SimpleDateFormat对象,例如%d{yyyy-MM-dd HH:mm:ss.SSS}。<maxHistory>: 日志文件保留天数。
- 基于文件大小的滚动策略:
ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy。
该策略会监视日志文件大小,当文件超过指定大小时触发滚动。
<maxFileSize>: 日志文件大小,默认为10MB。<prudent>: 是否安全写入文件。若为true,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,但此时必须满足:1不支持也不允许文件压缩;2不能设置file属性。<triggeringPolicy>: 告知 RollingFileAppender 何时激活滚动。
<root>标签
<root>标签有2个作用:
- 定义日志的输出等级。
- 指定哪些
<appender>启用。
<root>其name属性固定为ROOT,且没有上级,故而<root>只有一个属性:
level: 最重要,指定日志的打印等级,默认为DEBUG。其值可以为:ALL,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF。不可以设置为INHERITED或NULL`。
可以在<root>下配置多个<appender-ref>,标识对应的<appender>被添加到<root>下,从而可以继承<root>的属性和内容。
例如:
<root level="INFO"><appender-ref ref="CONSOLE"/><appender-ref ref="FILE" /><appender-ref ref="ROLLING" />
</root>
其中<appender-ref>的ref属性即为<appender>或<logger>的name属性。
<root>也是一个特殊的<logger>,且为所有<logger>的最上级父。<root>的parent属性为null。
<logger>标签
<logger>标签非必须,用于对某些日志等级进行单独定义。
例如设置<root level="INFO">,但希望springframework的等级为WARN,此时就可添加一个<logger>标签:
<logger name="org.springframework" level="WARN"/>
<logger>常用属性为:
name: 指定需单独设置的组件类名。level: 设置组件的日志等级。其值可以为:ALL,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF。可设置为INHERITED或NULL`。若不设置,则默认继承上级的level。additivity: 是否向上级<logger>传递打印信息。默认为true。若为true,则本<logger>打印一次,然后传递给上级<logger>,上级<logger>会再打印一次,这样就会造成重复打印。因此通常都设置为false。
例如:
<logger name="TEST_PARENT" level="INFO" additivity="false"><appender-ref ref="TEST_CHILD"/>
</logger>
这样就形成了父子关系,TEST_CHILD的上级为TEST_PARENT。
<logger>有一个parent属性,指向其父<logger>。每个<logger>都有parent,只有<root>除外,<root>是根节点。因此<root>和所有的<logger>会形成一棵树。注意父子关系是由parent属性形成的,而非类继承这样的关系。
定义变量
变量有两种:本地变量和配置文件变量。
本地变量<property>
在xml中定义一个变量,然后在整个xml文件中引用。格式为:
通过${变量名}格式使用变量。例如:
<!-- 定义变量 -->
<property name="LOG_HOME" value="E:/logs/" /><!-- 使用变量 -->
<appender><file>${LOG_HOME}/${app-name}.log</file>
</appender>
配置文件变量<springProperty>
若希望从yml或.properties中读取变量值在xml中使用,则需要使用<springProperty>。
例如,在application.yml中定义了一个logback_app_property.level变量:
logback_app_property:level: INFO
现在希望引用这个level变量,则可在xml中添加<springProperty>标签,并令其source属性指向logback_app_property.level变量。
<springProperty name="YML_LEVEL" source="logback_app_property.level" defaultValue="INFO"/>
同样是通过${变量名}格式使用变量:
<root level="${YML_LEVEL}">
</root>
实例
首先在yml中定义
logging:# 指定 logback-app.xml 作为logback的配置文件config: classpath:logback-app.xml# 用于 logback-app.xml 文件配置的参数值
logback_app_property:# TRACE < DEBUG < INFO < WARN < ERRORlevel: INFO
然后在logback-app.xml中定义:
<?xml version="1.0" encoding="UTF-8"?>
<configuration><contextName>Application</contextName><jmxConfigurator/><!-- 定义日志的保存目录 --><!-- <property name="LOG_HOME" value="/app/logs/" /> --><property name="LOG_HOME" value="E:/logs/" /><property name="app-name" value="test-admin"/><property name="filename" value="server"/><!-- 编码设置 --><property name="ENCODER_PATTERN" value="%red(%date{ISO8601}]) %highlight(%-5level) %green([%10.10thread]) [%X{X-B3-TraceId}] %boldMagenta(%logger{20}) - %cyan(%msg%n)" /><!-- 控制台输出 --><appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${ENCODER_PATTERN}</pattern><charset>UTF-8</charset></encoder></appender><!-- 滚动输出到文件:将日志记录到指定文件,满足条件时,再写入下一个文件 --><appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 指定日志文件的名称 --><file>${LOG_HOME}/${app-name}.log</file><!-- TimeBasedRollingPolicy: 基于时间的滚动策略,该策略根据时间来判定是否触发滚动。 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动%i:当文件大小超过maxFileSize时,按照i进行文件滚动--><fileNamePattern>${LOG_HOME}/${app-name}-%d{yyyy-MM-dd}-%i.log</fileNamePattern><!--可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假如设置每天滚动,maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件时那些为了归档而创建的目录也会被删除。--><MaxHistory>30</MaxHistory><!--当日志文件超过maxFileSize指定的大小时,根据上面提到的%i进行日志文件滚动。注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>30MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><!-- 日志输出格式: --><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %-5level ] [ %thread ] - [ %logger{50} : %line ] - %msg%n</pattern></layout></appender><!-- error信息输出 --><appender name="errorLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_HOME}/${app-name}-error.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_HOME}/${app-name}-error-%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>90</maxHistory></rollingPolicy><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %-5level ] [ %thread ] - [ %logger{50} : %line ] - %msg%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>WARN</level></filter></appender><!-- 这些日志不要打印debug,不然数量太多 --><!-- 选择使用的框架进行配置 --><logger name="org.springframework" level="WARN"/><logger name="org.spring" level="WARN"/><logger name="org.hibernate" level="WARN"/><logger name="io.grpc.netty" level="OFF"/><logger name="org.eclipse.jetty" level="WARN"/><logger name="jndi" level="WARN"/><logger name="redis.clients" level="WARN"/><logger name="application" level="WARN"/><logger name="springfox.documentation" level="WARN"/><logger name="com.netflix" level="WARN"/><logger name="org.reflections" level="WARN"/><logger name="org.apache" level="WARN"/><logger name="io.grpc.internal.ClientCallImpl" level="OFF"/><logger name="org.springframework.amqp.rabbit" level="ERROR"/><logger name="com.baomidou.dynamic.datasource.DynamicRoutingDataSource" level="WARN"/><logger name="com.zaxxer.hikari.pool.HikariPool" level="WARN"/><logger name="org.quartz.core.QuartzSchedulerThread" level="ERROR"/><logger name="io.lettuce.core.protocol.RedisStateMachine" level="INFO"/><logger name="io.lettuce.core.RedisChannelHandler" level="INFO"/><logger name="io.lettuce.core.protocol.CommandHandler" level="INFO"/><logger name="io.lettuce.core.protocol.CommandEncoder" level="INFO"/><logger name="io.lettuce.core.protocol.DefaultEndpoint" level="INFO"/><logger name="io.lettuce.core.protocol.ConnectionWatchdog" level="INFO"/><logger name="io.lettuce.core.RedisClient" level="INFO"/><logger name="org.mybatis.spring.mapper.ClassPathMapperScanner" level="INFO"/><logger name="com.baomidou.mybatisplus.core.MybatisConfiguration" level="INFO"/><!-- 配置文件变量 --><springProperty name="LEVEL" source="logback_app_property.level" defaultValue="INFO"/><root level="${LEVEL}"><appender-ref ref="consoleAppender"/><appender-ref ref="appLogAppender" /><appender-ref ref="errorLogAppender" /></root>
</configuration>
在程序中动态更改日志级别
在程序中使用代码来动态修改日志级别,首先导入库:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
然后获取<ROOT>并修改level:
String loggerName = "ROOT";
String loggerLevel = "DEBUG";
// 获取日志上下文
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
// 获取ROOT
ch.qos.logback.classic.Logger logger = loggerContext.getLogger(loggerName);
// 修改日志等级
logger.setLevel(Level.valueOf(loggerLevel));
注意这里的loggerName与JMX不同,是大小写不敏感的,例如ROOT可以写作root。
Java管理扩展 JMX(Java Management Extensions)
JMX是一个带窗口的应用程序,为运行中的应用提供管理功能。JMX是跨平台的,与具体应用无关。配置logback开启JMX后即可通过JMX查看logback信息。
配置logback开启JMX,需在xml中添加一行标签:
<configuration><jmxConfigurator />
</configuration>
当应用运行后,打开系统的CMD窗口,在其中输入命令:
jconsole
然后就会打开JMX的应用窗口,并提示新建连接。设应用程序运行在本地,则在本地进程的列表中选择对应的应用程序,然后双击或点连接。

之后可能弹出安全连接失败。是否以不安全的方式重试?,选择不安全的连接即可。

在上方的标签列表中点击MBean标签,然后在列表中找到ch.qos.logbak.classic,依次点击进入default→ch.qos.logback.classic.jmx.JMXConfigurator,其下会列出属性和操作两个列表。在其下分别包含不同的功能。
对于属性,包含LoggerList和Statuses。其中LoggerList列出了所有的Logger。特殊地,<root>标签其对应名称为ROOT,会列在第一行。

对于操作,可进行:
- 重新加载logback的默认配置文件。
- 通过指定的URL重新加载配置文件。
- 通过指定的文件重新加载配置文件。
- 设置指定logger级别,可设为
null。 - 获取指定logger级别,可返回
null。 - 指定logger的有效级别。

点击属性下的LoggerList,即可看到一个Logger数组。第一个就是ROOT。
点击操作下的getLoggerLevel,需输入一个参数p1,该参数就是LoggerList中的一个Logger。输入ROOT,然后点击按钮getLoggerLevel,即可看到ROOT的等级。注意这里的p1是大小写敏感的,例如ROOT不可以写作root。
点击操作下的setLoggerLevel,需输入两个参数p1和p2,p1为Logger名称,p2为level值。例如p1输入ROOT,p2输入DEBUG,然后点击按钮setLoggerLevel,即可将ROOT变更为DEBUG等级。
同理,更改应用的配置文件 logback.xml ,然后点击 操作 下的reloadDefaultConfiguration,并点击右侧的reloadDefaultConfiguration按钮,即可重新加载配置文件。
但在实际测试中,若配置文件为 logback.xml ,则表现相对正常;而若配置文件为其他名称,例如 logback-spring.xml ,则重载配置文件后原控制台将不再进行打印,且对配置文件的修改也不会生效。似乎reloadDefaultConfiguration只识别 logback.xml 。
另外,修改配置文件只能是更改 logback.xml 本身。如果修改的是application.yml中的logback_app_property.level变量,reloadDefaultConfiguration时并不会重新读取该变量并应用到 logback.xml 中。
一般来说,通过JMX来修改的只有ROOT,以及logback.xml中配置的多个<logger>。
相关文章:
Spring Boot日志:SLF4J和Logback
日志的分类 SpringBoot中的日志库分为两种: 实现库:提供具体的日志实现,例如日志级别的控制、打印格式、输出目标等。外观库:自身不提供日志实现,而是对其他日志库进行封装,从而方便使用。基于外观模式实…...
[C++] C++入门第二篇 -- 引用 -- 内联函数inline -- auto+for
目录 1、引用 -- & 1.1 引用的概念 1.2 引用特性 1.3 常引用 -- 权限问题 1.4 引用的使用场景 1.4.1 做参数 1.4.2 做返回值 注意 1.5 传值、传引用的效率比较 1.6 引用和指针的区别 2、内联函数 2.1 概念 转存失败重新上传取消编辑转存失败重新上传取消编…...
Latex | 将MATLAB图并导入Latex中的方法
一、问题描述 用Latex时写paper时,要导入MATLAB生成的图进去 二、解决思路 (1)在MATLAB生成图片的窗口中,导出.eps矢量图 (2)把图上传到overleaf的目录 (3)在文中添加相应代码 三…...
JSON格式Python,Java,PHP等封装根据关键词搜索获取淘宝商品列表数据API
淘宝是一个网上购物平台,售卖各类商品,包括服装、鞋类、家居用品、美妆产品、电子产品等。要用关键词搜索获取淘宝天猫商品列表,您可以通过开放平台的接口或者直接访问淘宝天猫商城的网页来获取商品列表详细信息。以下是两种常用方法的介绍&a…...
MySQL MHA高可用配置及故障切换
文章目录 一.MySQL MHA1.什么是MHA2.MHA的组成2.1MHA Node (数据节点)2.2MHA Manager (管理节点) 3.MHA的特点4.…...
PHP8知识详解:PHP8开发工具VS Code的安装
作为PHP8的开发工具有很多,具有IDE功能的有phpstorm、Visual Studio Code、Sublime Text、NetBeans、Eclipse、Codelobster、PHP Designer等,当然还有很多轻量的工具,比如Notepad、Editplus等。本文给你介绍的是万能编辑器Visual Studio Code…...
Sui Move与标准Move的有哪些区别和根本性创新
Sui网络将Sui Move作为其本地编程语言,使用Sui Move编写的apps利用Sui的共识机制,实现了令人印象深刻的交易性能。 然而,熟悉Move编程语言的开发者在探索Sui文档时可能会感到困惑,因为该文档着重介绍了对象和一些指令,…...
构建自己的ChatGPT:从零开始构建个性化语言模型
🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~ἳ…...
【react】react18的学习(十二)– 底层原理(二)之 迭代器 iterator
迭代器iterator 是一种 ES6 规范,具有这种机制的数据结构才可以使用for of循环:返回每一项的值; 原型链具有Symbol.iterator属性的数据结构都具备;如数组、部分类数组、字符串等; 普通对象就不能用; for-…...
一遍过JavaSE基础知识
文章目录 前言安装Java Development Kit (JDK)安装jdk配置开发环境验证是否安装配置成功 编写第一个Java程序hello world运行Java程序的流程 数据类型和变量数据类型变量 程序逻辑控制条件语句循环语句跳转语句 数组声明和创建数组访问数组元素数组长度遍历数组多维数组 面向对…...
【云原生】Kubernetes之ConfigMap
ConfigMap ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时, Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件 ConfigMap 将你的环境配置信息和 容器镜像 解耦,便于应用配置的修改 说明:…...
8.python设计模式【组合模式】
内容:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。角色: 抽象组建(component)叶子组建(Leaf)复合组建(Composite)客户端 (Client) UML 图 举个例子 需求…...
tkinter制作任意图形窗口
import tkinter from PIL import Image, ImageTkdog tkinter.Tk() # 设置图片描绘的坐标,注意乘号是字母x dog.geometry(500x500200100) # 不允许修改大小 dog.resizable(False, False) # 不显示标题栏 dog.overrideredirect(True) # 设置白色透明色,这…...
视频监控综合管理平台EasyCVR多分屏默认播放协议的配置优化
视频监控综合管理平台EasyCVR具备视频融合汇聚能力,TSINGSEE青犀视频平台基于云边端一体化架构,可支持多协议、多类型设备接入,包括:NVR、IPC、视频编码器、无人机、车载设备、智能手持终端、移动执法仪等。国标GB28181视频平台Ea…...
2023杭电多校第三场 1012.Noblesse Code
传送门:Vjudge 前题提要:一道挺有意思的数论题.赛时对于这道题没什么想法,但是赛后细品之后其实感觉也就那么一回事.但是这种 更相损减术与辗转相除法 相转化的题目还是有点典的,需要好好消化一下. 首先看完题目.我们需要考虑的是 ( A , B ) (A,B) (A,B)与 ( a , b ) (a,b) (…...
ubuntu qt 环境变量配置
ubuntu设置qt环境变量 qt 安装路径为:/home/ljn/Qt5.12 包含bin等目录的路经:/home/ljn/Qt5.14.2/5.14.2/gcc_64 环境变量配置 打开配置文件: sudo gedit /etc/profile在底部添加: export PATH"/home/ljn/Qt5.14.2/Tool…...
按照Vue写WPF(0):功能实现
文章目录 前言VUE具有的功能如何专业到WPF上面 前言 我最近学了WPF之后我终于知道为什么WPF学习曲线那么陡峭了。因为WPF没有组件化的思想,或者说没有按照Vue一样去模板化开发。 为什么我推荐Vue的想法呢。因为Vue最大的特点就是模板化,让Vue工程师去写…...
vb+ACCESS教师管理系统设计设计与实现
--------------前言-------------- 教师管理系统是一个企事业单位不可缺少的部分,它的内容对于企事业单位的决策者和管理者来说都至关重要,所以教师管理系统应该能够为用户提供充足的信息和快捷的查询手段。但一直以来人们使用传统人工的方式管理文件信息,这种管理方式存在…...
C++笔记之对指针类型的变量进行+1操作
C笔记之对指针类型的变量进行1操作 在C中,对指针类型的变量进行"1"操作会根据指针的数据类型而有所不同。这涉及到指针的算术运算,C中的指针算术运算是根据指针所指向的数据类型的大小来进行的。 code review! 文章目录 C笔记之对指针类型的…...
第六章 游标
游标 本文内容较短,我们只是为了更容易的实现b树,简单地重构一下。 我们将添加一个Cursor 表示表中对象的位置。Cursor应提供如下几个方面的能力: 在表的开头创建游标在表的末尾创建游标访问游标指向的行将游标前进到下一行 这是本文我们…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
