【java开发】一文理清 Java 日志框架的来龙去脉

news/2024/11/28 10:35:58/文章来源:https://www.cnblogs.com/o-O-oO/p/18571310
一、引言二、日志概念三、日志框架的作用四、日志框架的发展历程4.1 早期阶段(1996年以前)4.2 Log4j的诞生(1996年)4.3 JUL的推出(2002年)4.4 JCL的推出(2002年)4.5 SLF4J和Logback的创建(2006年)4.6 Log4j 2的推出(2012年)五、主流日志框架5.1 日志门面5.2  日志实现六、日志框架的使用6.1 日志框架的集成6.2 日志框架的桥接七、JCL 门面框架示例7.1  JCL+JUL7.2 JCL+log4j 1.x7.3 JCL+SLF4J八、SLF4J 门面框架示例8.1 SLF4J+logback8.2 SLF4J+log4j28.3 SLF4J+reload4j8.4 SLF4J+JUL8.5 SLF4J+simple九、日志规约十、总结

一、引言

在Java开发中,日志记录是不可或缺的一部分。它不仅可以帮助开发者调试程序,还能监控应用程序的运行状态,及时发现和解决问题。本文将详细介绍Java日志框架的基本概念、主流框架以及使用方法,帮助读者更好地理解和应用日志框架。

二、日志概念

日志,简单来说就是记录。它用来记录程序运行时发生的事情,比如程序启动、执行某个操作、遇到问题等等。这些记录信息对于开发者来说非常重要,因为它们提供了程序运行时的详细情况和状态。

在电商网站上,日志可以记录用户的登录、浏览、购买行为,监控交易过程,及时发现异常交易;通过分析日志,还可以实现精准推送等功能。服务器日志则可以记录服务器的启动、运行、关闭状态,以及发生的各种错误,帮助管理员及时发现并解决问题。

三、日志框架的作用

在没有日志框架时,我们可能会使用 System.out.println 或 e.printStackTrace() 来打印程序的运行状态和错误信息。这种方式简单直接,但存在一些缺点,如灵活性差、性能问题、不易管理等。因此,我们需要引入功能强大的日志框架进行日志管理。

日志框架由日志门面和日志实现构成。日志门面提供了一套标准的日志记录接口,而具体的日志记录工作则由不同的日志框架来完成。这样做的好处是,可以在不修改代码的情况下,通过配置来切换不同的日志框架。

注意:虽然日志实现也可以单独使用,但实际开发中一般采用日志门面和日志实现的方式。

【图】日志门面和日志实现

四、日志框架的发展历程

Java日志框架的发展历程是一个不断演进和改进的过程,以下是按时间顺序对Java日志框架的主要发展历程的介绍:

4.1 早期阶段(1996年以前)

在Java发展的早期阶段,开发人员主要依赖System.out和System.err来记录日志。这些方法虽然简单,但存在许多局限性,如缺乏灵活性、无法配置不同的日志级别、不能轻松地将日志保存到文件等。

4.2 Log4j的诞生(1996年)

1996年,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。这个API最终发展成为一个十分受欢迎的Java日志软件包,即Log4j。

Log4j提供了灵活的日志级别配置,允许开发人员根据需要记录不同级别的日志。它还提供了多种日志输出方式,包括控制台、文件和数据库。
Log4j后来成为Apache基金会项目中的一员,并近乎成了Java社区的日志标准。
Log4j[1]在2015年宣布不在维护了。
reload4j[2] 是 Apache log4j版本1.2.17的一个分支,旨在解决log4j的安全问题。Reload4j是log4j版本1.2.17的直接替代品,所以想继续使用log4j 1.x的框架,推荐使用slf4j-reload4j进行替代。

4.3 JUL的推出(2002年)

2002年,随着Java 1.4的发布,Sun推出了自己的日志库JUL(Java Util Logging)。JUL基本模仿了Log4j的实现,但性能和可用性在JDK 1.5以后才有所提升。

由于Log4j比JUL更成熟且好用,所以Log4j在选择上占据了一定的优势。

4.4 JCL的推出(2002年)

2002年8月,Apache推出了Jakarta Commons Logging(JCL,一个简单的日志门面框架)。JCL旨在成为Java日志领域的标准抽象层,为各种日志框架提供统一的接口。

JCL支持运行时动态加载日志组件的实现,也就是说,在应用代码里只需调用Commons Logging的接口,底层实现可以是Log4j,也可以是Java Util Logging。
然而,JCL在实际使用中存在一些问题,如效率低下、容易引发混乱以及在特定情况下引发内存泄露等。
JCL最初是由Apache软件基金会的Jakarta项目组开发的,当时它是Apache Jakarta项目的一部分。在2011年,Jakarta 项目组重新组织并成为Apache Commons项目。因此,JCL目前被称为Apache Commons Logging。

4.5 SLF4J和Logback的创建(2006年)

2006年,Log4j的主要贡献者Ceki Gülcü因不适应Apache的工作方式而离开,并创建了SLF4J[3](Simple Logging Facade for Java,一个简单的日志门面框架)和Logback[4]两个项目。

SLF4J是一个日志门面接口,类似于JCL,但提供了更简单和优雅的日志记录方法。它允许开发人员在不修改代码的情况下切换不同的日志框架。

Logback是SLF4J的一个实现,它提供了可靠、快速且灵活的日志记录功能。Logback在功能完整度和性能上超越了当时已有的日志标准库。

4.6 Log4j 2的推出(2012年)

2014年,Apache推出了Log4j 2[5],这是一个全新的日志框架,不兼容Log4j 1.x。

Log4j 2全面借鉴了SLF4J和Logback的设计,并提供了更丰富的功能和配置选项。它支持异步日志打印、多种输出方式、日志级别、过滤器等。
Log4j 2还提供了各种桥接包,以便与SLF4J和其他日志框架进行集成。

五、主流日志框架

如上面介绍的,当前Java项目使用日志采用的是日志门面+日志实现框架的方式。下面分别介绍。

5.1 日志门面

目前流行的有两种日志门面框架JCL和SLF4J。

SLF4J:一个非常流行的日志门面,提供了一套简单的日志记录接口,并且可以与多种日志框架(如Log4j、Logback等)配合使用。

JCL(Jakarta Commons Logging):早期的一个日志门面,目前在一些老项目中仍然在使用。

Log4j2:Log4j2提供了门面功能,这意味着它可以作为一个中间层,与不同的日志实现进行集成。然而,在实际应用中,开发者更倾向于使用专门的日志门面框架(如SLF4J)来与Log4j2等日志实现进行集成。

5.2 日志实现

下面对主要的日志实现框架进行介绍。

JUL(Java Util Logging)

Java自带的日志框架,功能相对基础,性能一般,但对于简单的日志需求来说足够用了。主要包括Logger、Handler和Formatter三个核心组件。Logger是日志记录器,用于生成日志记录;Handler是日志处理器,负责将日志信息输出到不同的目的地,如控制台、文件等;Formatter是日志格式化器,负责定义日志的输出格式。

Log4j

一个老牌的日志框架,功能非常强大,可以自定义很多日志的细节,比如日志级别、输出格式、输出目的地等。它使用Layout来控制日志内容的输出格式,常用的有HTMLLayout、SimpleLayout和PatternLayout等。其中,PatternLayout是最强大的格式化器,允许自定义输出格式。log4j 1.x版本2005年已经不再维护,建议升级到Log4j 2.x。如果一定要使用,推荐使用reload4j,详见下面介绍。

reload4j

reload4j是Apache log4j版本1.2.17的分支,旨在解决安全问题,提供相似功能和API并进行了安全修复与改进,可作为log4j 1.x的直接替代品并与SLF4J等日志门面集成使用。

Logback

由Log4j的原开发者之一主导开发,Spring Boot默认日志框架,轻量级,性能优秀,功能也比较全面。Logback是一个轻量级、性能优秀的日志框架,默认配置下与Spring Boot配合良好。它支持编程式配置和配置文件配置两种方式,可以灵活控制日志的输出格式、级别和目的地。

Log4j2

Log4j2是Log4j的升级版,参考了Logback的一些优秀设计,修复了一些bug,性能和功能都带来了极大提升。它支持异步日志记录,可以显著减少日志记录对主程序性能的影响,尤其是在高并发场景下。

slf4j-simple

slf4j-simple是一个轻量级的日志框架,作为SLF4J的简单实现,提供基本的日志记录功能并默认将日志输出到控制台。

六、日志框架的使用

在实际开发中,我们通常使用日志门面(JCL或者SLF4J)来记录日志,而不是直接使用具体的日志实现框架。这样做的好处是,可以在不修改代码的情况下,通过配置来切换不同的日志实现框架。虽然现在主流使用的是SLF4J,这里我也会介绍一下JCL的使用。

6.1 日志框架的集成

下面是SLF4J官网给出的原理图:

【图】SLF4J原理图

1、没有绑定任何日志实现,门面技术SLF4J是不能够实现任何功能的。包括自带的slf4j-simple官方提供的也必须导入相应的jar包配合使用。

2、 logback和simple(包括nop-禁止打印日志)都是SLF4J出现之后提供的日志实现,API完全遵循SLF4J设计,使用只需要导入对应的jar包即可。

3、 log4j和JUL时间线是在slf4j之前的,要配合使用就必须通过适配桥接技术(slf4j-log4j12和slf4j-jdk14),完成与日志门面的连接。

4、 当导入slf4j-nop后将不会使用任何日志框架

注意: 在SLF4J环境下,若同时导入多个日志实现框架,默认使用先导入的。在实际应用中,一般只集成一种日志实现。

6.2 日志框架的桥接

如果你的项目中已经使用了JCL、Log4j 1.x等老的日志框架,但想迁移到使用SLF4J的API,这时候你可以使用桥接器[6]来平滑过渡而不用修改原有代码,只需要修改依赖即可完成。

下面是SLF4J桥接旧版日志框架的原理图:

【图】SLF4J桥接旧版日志框架的原理图

上图表明了对旧版的框架log4j 1.x、JCL和java.util.logging(JUL)的API的调用迁移到SLF4J的API,需要引入如下包:

log4j-over-slf4j: 允许Log4j 1.x用户(但不允许Log4j 2.x)将现有应用程序/库迁移到SLF4J,而无需更改原有代码,只需将log4j.jar文件替换为log4j-over-slf4j.jar
jcl-over-slf4j.jar:为了简化从JCL到SLF4J的迁移,SLF4J发布了文件jcl-over-slf4j.jar。这个jar文件实现了 JCL的公共API,但在底层使用了SLF4J,因此命名为 “JCL over SLF4J”。
jul-to-slf4j.jar:它路由所有传入的JUL记录到SLF4j API。

注意:

1、log4j-over-slf4j.jar、slf4j-reload4j.jar和slf4j-log4j.jar不能同时存在,否则会导致无限循环。
2、jul-to-slf4j.jar和slf4j-jdk14.jar不能同时存在,否则会导致无限循环。

七、JCL 门面框架示例

JCL有两个基本的抽象类:Log(日志记录器)、LogFactory(日志工厂,负责创建Log实例)。公共测试代码如下:

package org.log.demo;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public class App {public static void main(String[] args) {Log logger = LogFactory.getLog(App.class);logger.info("Hello World");}
}

7.1 JCL+JUL

JCL自带JUL实现。 增加依赖:

 <!--引入commons-logging日志接口-->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version>
</dependency>

运行结果:

11月 26, 2024 9:03:48 下午 org.log.demo.App main
信息: Hello World

查看堆栈,调用了JDK自带的java.util.logging.Logger#getLogger(java.lang.String)方法获取Logger。

【图】JUL的Logger

7.2 JCL+log4j 1.x

JCL支持log4j。直接添加依赖并配置日志格式,即可调用。这里需要注意的是,log4j 1.x只支持到1.2.17版本,更安全的版本是使用reload4j。下面分别给出两种使用方式,区别在于引入的依赖不同。

首先,需要增加配置文件log4j.properties,内容如下:

#配置日志级别,引用控制器
log4j.rootLogger=INFO,console
#配置控制台输出器
log4j.appender.console=org.apache.log4j.ConsoleAppender
#配置自定义格式器
log4j.appender.console.layout=org.apache.log4j.PatternLayout
#配置自定义转换模式
log4j.appender.console.layout.conversionPattern=[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5p] [%t] [%-4rms] [%c#%M-%L] %m%n

然后,增加依赖。 使用log4j 1.2.17及以前的版本。依赖如下:

<!--引入JCL日志接口-->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version>
</dependency>
<!-- log4j日志框架 -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>

使用更安全的版本reload4j。依赖如下:

<!--引入JCL日志接口--><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>2.0.16</version></dependency>

可以看到slf4j-log4j12的2.0.16版本中pom会重定位到slf4j-reload4j依赖


也就是说上面引入slf4j-log4j12,等价于:

<!--引入JCL日志接口-->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version>
</dependency>
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-reload4j</artifactId><version>2.0.16</version>
</dependency>

运行结果:

[2024-11-26 21:20:20.285] [INFO ] [main] [0   ms] [org.log.demo.App#main-10] Hello World

通过下面调用栈可以看到调用了log4j 1.2.17包的org.apache.log4j.Category#log(String,Priority, Object, Throwable)打印日志。

【图】log4j 1.2.17包

通过下面调用栈可以看到调用了reload4j 1.2.22包的org.apache.log4j.Category#log(String,Priority, Object, Throwable)打印日志。

【图】reload4j 1.2.22包

7.3 JCL+SLF4J

为了减少代码改动,即仍然使用JCL的API,但是又想依赖SLF4J的日志实现,如logback,可以通过引入桥接包jcl-over-slf4j实现。
步骤如下:

不直接引入 JCL 的核心包(比如 commons-logging)。
引入 jcl-over-slf4j,这是将JCL调用桥接到SLF4J的库。
引入 slf4j-api,这是SLF4J的API包。
引入你选择的日志实现,比如 logback-classic、log4j2等SLF4J的实现框架。
配置logback配置文件logback.xml
测试代码

依赖如下:

 <!--引入slf4j日志接口-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.16</version>
</dependency>
<!-- 使用logback实现slf4j日志-->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.12</version>
</dependency>
<!--引入jcl日志桥接器,将jcl日志接口桥接到slf4j日志接口上-->
<dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>2.0.16</version>
</dependency>

需要在resources下增加logback配置文件logback.xml,内容如下:

<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="debug"><appender-ref ref="STDOUT" /></root>
</configuration>

调用logback的ch.qos.logback.classic.Logger#log(org.slf4j.Marker, java.lang.String, int, java.lang.String, java.lang.Object[], java.lang.Throwable)输出日志。

【图】logback-classic包

如果将依赖改为log4j2,需要修改pom.xml如下:

<!--引入jcl日志桥接器,将jcl日志接口桥接到slf4j日志接口上--><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>2.0.16</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency><!--添加log4j2与slf4j的桥接器--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.24.2</version></dependency>

需要在resources下增加log4j2配置文件log4j2.xml,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><!-- 定义一个控制台输出的Appender --><Console name="Console" target="SYSTEM_OUT"><!-- 定义日志输出的格式 --><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><!-- 定义一个根Logger,它接收所有的日志记录 --><Root level="info"><!-- 将根Logger的日志输出到之前定义的Console Appender --><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>

调用log4j2-core的org.apache.logging.log4j.core.Logger#logMessage输出日志。

2024-11-26 22:16:39 [main] INFO  org.log.demo.App - Hello World

【图】log4j2-core包

八、SLF4J 门面框架示例

SLF4J通过两个核心组件Logger和LoggerFactory来输出日志。 公共测试代码如下:

package org.log.demo;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class App {public static void main(String[] args) {Logger logger = (Logger) LoggerFactory.getLogger(App.class);logger.info("Hello World");}
}

8.1 SLF4J+logback

logback直接实现了SLF4J的接口。 添加依赖:


<!--引入slf4j日志接口-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.16</version>
</dependency>
<!-- 使用logback实现slf4j日志-->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.12</version>
</dependency>

配置文件logback.xml,内容如下:

<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="debug"><appender-ref ref="STDOUT" /></root>
</configuration>

输出日志:

2024-11-26 22:25:05 [main] INFO  org.log.demo.App - Hello World

调用logback的ch.qos.logback.classic.Logger#info(java.lang.String)打印日志。

【图】logback-classic包

8.2 SLF4J+log4j2

添加依赖:

<!--添加log4j2与slf4j的桥接器--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.24.2</version></dependency>

需要在resources下增加log4j2配置文件log4j2.xml,内容如下:


<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><!-- 定义一个控制台输出的Appender --><Console name="Console" target="SYSTEM_OUT"><!-- 定义日志输出的格式 --><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><!-- 定义一个根Logger,它接收所有的日志记录 --><Root level="info"><!-- 将根Logger的日志输出到之前定义的Console Appender --><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>

输出日志:

2024-11-26 22:28:13 [main] INFO  org.log.demo.App - Hello World

调用log4j-api包的org.apache.logging.log4j.spi.AbstractLogger#logMessage(java.lang.String, org.apache.logging.log4j.Level, org.apache.logging.log4j.Marker, java.lang.String)输出日志。

【图】log4j-api包

8.3 SLF4J+reload4j

添加依赖:

<!--引入slf4j日志接口--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.16</version></dependency>
<!-- 使用reload4j实现slf4j日志--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-reload4j</artifactId><version>2.0.16</version></dependency>

添加配置文件log4j.properties,内容如下:

log4j.rootLogger=INFO,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5p] [%t] [%-4rms] [%c#%M-%L] %m%n

输出日志:

[2024-11-26 22:32:02.847] [INFO ] [main] [0   ms] [org.log.demo.App#main-9] Hello World

调用reload4j包的org.apache.log4j.Category#log(java.lang.String, org.apache.log4j.Priority, java.lang.Object, java.lang.Throwable)输出日志。

【图】reload4j包

该种方式与如下配置log4j 1.x依赖结果是一样的。

<!--使用log4j1.2.x-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>2.0.16</version>
</dependency>会重定位到reload4j的依赖。<distributionManagement><relocation><groupId>org.slf4j</groupId><artifactId>slf4j-reload4j</artifactId><version>2.0.16</version>      </relocation>
</distributionManagement>

8.4 SLF4J+JUL

添加依赖:

<!--引入slf4j日志接口-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.16</version>
</dependency>
<!--使用(JUL)java.util.logging实现slf4j日志-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-jdk14</artifactId><version>2.0.16</version>
</dependency>

输出日志:

11月 26, 2024 10:37:45 下午 org.log.demo.App main
信息: Hello World

调用JDK包的java.util.logging.Logger#log(java.util.logging.LogRecord)输出日志。

【图】JDK包

8.5 SLF4J+simple

添加依赖:

<!--引入slf4j日志接口-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.16</version>
</dependency>
<!--使用simple实现slf4j日志-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>2.0.16</version>
</dependency>

输出日志:

[main] INFO org.log.demo.App - Hello World

调用slf4j-simple包的org.slf4j.simple.SimpleLogger#innerHandleNormalizedLoggingCall输出日志。

【图】slf4j-simple包

九、日志规约

在日志开发中,需要遵守一些规约来确保日志的规范性和可读性。
阿里巴巴Java开发手册中提供了以下日志规约:

应用中不可直接使用日志系统(如Log4j、Logback)中的API,而应依赖使用日志框架(如SLF4J、JCL)中的API。
尽量用英文来描述日志错误信息,如果日志中的错误信息用英文描述不清楚的话使用中文描述即可,否则容易产生歧义。

十、总结

本文详细介绍了Java日志框架的基本概念、主流框架以及使用方法。通过了解日志框架的组成和特性,开发者可以更好地选择和使用适合自己的日志框架,从而更有效地进行日志记录和管理。在实际开发中,建议遵守日志规约,确保日志的规范性和可读性。

参考资料

[1]Log4j: https://logging.apache.org/log4j/1.x/
[2]reload4j: https://reload4j.qos.ch/
[3]SLF4J: https://www.slf4j.org/manual.html
[4]Logback: https://logback.qos.ch/
[5]Log4j 2: https://logging.apache.org/log4j/2.12.x/
[6]桥接器: https://www.slf4j.org/legacy.html

原创 程序员Ink

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

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

相关文章

dotnet 简单写一个 pdb 符号文件下载器

本文将告诉大家如何读取 PE 文件头信息,拼接 PDB 符号文件下载地址,从微软公共符号服务器拉取符号文件本文将以拉取 ntdll.dll 为例子告诉大家如何从 msdl.microsoft.com 下载符号 我先将自己电脑上的 ntdll.dll 拷贝到输出路径,方便我进行访问。读取 C 盘的 Windows 文件夹…

读数据质量管理:数据可靠性与数据质量问题解决之道16数据认证

数据认证1. 对数据进行认证 1.1. 数据认证是指在数据资产满足关于数据质量、可观测性、权责分配、问题解决和沟通等公司内共同遵守的SLA后,批准它们被用于整个组织的过程 1.2. 数据认证为人员、框架和技术构建了关键流程,使其与核心业务政策保持一致 1.3. 数据认证的要求会因…

FinalShell-Decoder:一款FinallShell密码解密的GUI工具

项目介绍 FinalShell-Decoder是一款FinallShell密码解密的GUI工具,目前使用 Maven 进行了重构,jdk11以上版本可自行打包,使用时记得添加环境变量 : java--module-path "%JDK_INSTALL_PATH%\javafx-sdk-11.0.2\lib" --add-modules javafx.controls,javafx.fxml -ja…

鸿蒙NEXT元服务:利用App Linking实现无缝跳转与二维码拉起

【效果】 元服务链接格式(API>=12适用):https://hoas.drcn.agconnect.link/ggMRM 生成二维码后效果:​ 【参考网址】 使用App Linking实现元服务跳转:https://developer.huawei.com/consumer/cn/doc/AppGallery-connect-Guides/agc-applinking-atomic-link-0000002046…

模糊耗散合成神经编解码器中的拉普拉斯熵模型

模糊耗散合成神经编解码器中的拉普拉斯熵模型 虽然用条件扩散模型代替高斯解码器,可以提高神经图像压缩中重建的感知质量,但它们缺乏对图像数据的感应偏差,限制了它们实现最先进感知水平的能力。为了解决这一局限性,在解码器侧采用了非各向同性扩散模型。该模型施加了一种感…

记一次固态硬盘玩游戏卡顿的解决过程

现在是2024年11月27日,星期三 我的系统是windows 10,这一段时间我给我的笔记本电脑换了一个1t的固态硬盘,原先是500G的固态硬盘, 把系统复制过来之后,正常使用,电脑使用一些比如浏览器呀,vscode之类的软件都没啥问题, 我喜欢玩永劫无间这个游戏,这个游戏也算是大型游戏了, 然后…

朋友

朋友 题目背景 小明在 A 公司工作,小红在 B 公司工作。 题目描述 这两个公司的员工有一个特点:一个公司的员工都是同性。 A 公司有 \(N\) 名员工,其中有 \(P\) 对朋友关系。B 公司有 \(M\) 名员工,其中有 \(Q\) 对朋友关系。朋友的朋友一定还是朋友。 每对朋友关系用两个整…

Golang网络模型netpoll源码解析

0、引言 在学习完了Socket编程的基础知识、Linux系统提供的I/O多路复用的实现以及Golang的GMP调度模型之后,我们进而学习Golang的网络模型——netpoll。本文将从为什么需要使用netpoll模型,以及netpoll的具体流程实现两个主要角度来展开学习。当前使用的Go的版本为1.22.4,Li…

骁龙 8 Elite 至尊版 对比 电脑端cpu

台式/笔记本 参考https://socpk.com/cpu/ 骁龙 8 Elite是260 M4 (4+6) 是360而M4对比笔记本cpu参考

线性版本HierHolzer正确性说明

晚上在研究怎么求欧拉图回路,看到 \(O(n+m)\) 版本的 HierHolzer 算法实现,让我很迷惑。 void dfs(int x){for(int i = 1;i <= 500; ++i){if(g[x][i]){--g[x][i]; --g[i][x];dfs(i);}}ans[++cnt] = x; }OI-Wiki 上对于这段代码的描述是这样的:将找回路的 DFS 和 Hierholz…

plus_one

2024/11/26 --2024/11/28 验证哥德巴赫猜想 打印漏斗 1.统计字符 1. 不需要数组,在循环中统计各个种类的字符 2. 一定把各个种类初始化为0 3. 一个一个字符输入,不是字符串一次输入哦 4. 空格是 回车是 \n 5. 大小写字母的ASCII码不连续,所以是(s >= a&& s<…