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应提供如下几个方面的能力: 在表的开头创建游标在表的末尾创建游标访问游标指向的行将游标前进到下一行 这是本文我们…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
