(一)Log4Net - 介绍

news2024/12/23 18:53:46

0、相关概念

Log4j

几乎每个大型应用程序都包含自己的日志记录或跟踪 API。根据这一规则,E.U. SEMPER 🌹项目决定编写自己的跟踪 API。那是在 1996 年初。经过无数次的增强、几个化身和大量的工作,API 已经发展成为 log4j —— 一个流行的 Java 日志包。该软件包是在 Apache 软件许可证下发布的,这是一个由开源 🌹倡议组织认证的成熟的开源许可证。最新的 log4j 版本,包括完整的源代码、类文件和文档,可以在 https://logging.apache.org/log4j/2.x/index.html 🌹上找到。

在代码中插入日志语句是一种低技术含量的调试方法。这也可能是唯一的方法,因为调试器并不总是可用或适用的。这通常是多线程应用程序和分布式应用程序所要面临的情况。

经验表明,日志记录是开发周期的一个重要组成部分。它有几个优点。它提供了关于应用程序运行的精确上下文。一旦插入到代码中,日志输出的生成就不需要人工干预。此外,日志输出可以保存在持久介质中,以供以后研究。除了在开发周期中使用之外,一个功能足够丰富的日志包还可以被视为一个审计工具。

正如 Brian W. Kernighan 和 Rob Pike 在他们真正优秀的书《The Practice of Programming》中所说的那样:

作为个人选择,除了获取堆栈跟踪或一两个变量的值之外,我们倾向于不使用调试器。一个原因是它很容易迷失在复杂的数据结构和控制流的细节中;我们发现,与努力思考并在关键位置添加输出语句和自检代码相比,逐步执行程序的效率更低。点击语句比扫描精心放置的显示的输出要花更长的时间。决定在哪里放置 print 语句比单步到达代码的关键部分花费更少的时间,即便假设我们知道关键部分在哪里。更重要的是,调试语句留在程序中;调试会话是短暂的。

日志记录确实有其缺点。它会降低应用程序的运行速度。如果过于冗长,可能会导致滚动盲区。为了减轻这些担忧,log4j 被设计成可靠、快速和可扩展的。由于日志记录很少是应用程序的主要关注点,因此 log4j API 力求易于理解和使用。

Log4j 2
LOG4J 2 Logo

Log4j 1.x 已被广泛采用并在许多应用程序中使用。然而,经过多年发展它的速度已经慢下来了。由于需要兼容非常旧的 Java 版本,它变得更加难以维护,并最终在 2015 年 8 月到达了其生命周期尽头 🌹。它的替代方案,SLF4J/Logback 对框架进行了许多必要的改进。

Apache Log4j 2 是 Log4j 的升级版,相对于其前身 Log4j 1.x 提供了重大改进。它提供了 Logback 中可用的许多改进,同时修复了 Logback 架构中的一些固有问题。

Log4Net
Apache log4net 库是一个帮助程序员将日志语句输出到各种输出目标的工具。log4net 是优秀的 Apache log4j™ 框架到 Microsoft® .NET 运行时的一个移植。log4net 保持了框架在精神上与最初的 log4j 相似,同时利用了 .NET 运行时的新特性。有关 log4net 的更多信息,请参阅 特性文档。
Apache log4net 项目
Logging Services Logo
log4net 是 Apache 软件基金会的 Apache 日志服务项目的一部分。Logging Services 项目旨在为应用程序调试和审计提供跨语言的日志服务。
Why Log 4?

有些同学可能会对名称中出现的数字 “4” 感到疑惑,但如果你尝试使用英文念出 Log4j 或者 Log4Net 的名称后,相信这个问题就会迎刃而解了。

没错,名称中的 “4” 代表英文单词 "for",因为 “4” 的英文单词 "four" 与 "for" 发音类似。所以,"Log4j" 的含义就是 "Log for Java", 意思是这是一个为 Java 提供日志服务的工具。

这是一种常见的命名方式,通过数字 “4” 来代替 "for", 使得名称更加简洁,也增加了一些趣味性。此外,还有其他的类似命名方式,比如使用 “2” 来代替 "to"。它们都在许多场合内大量使用着。

1、框架

Log4Net 可用于多个框架。对于每个支持的框架,都构建了一个针对该框架的程序集:

  • .NET Standard 1.3 via .NET Core 1.0
  • Microsoft® .NET Framework 1.0
  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 2.0
  • Microsoft .NET Framework 3.5
  • Microsoft .NET Framework 4.0
  • Microsoft .NET Framework 4.5
  • Microsoft .NET Framework 3.5 Client Profile
  • Microsoft .NET Framework 4.0 Client Profile
  • Microsoft .NET Compact Framework 1.0
  • Microsoft .NET Compact Framework 2.0
  • Mono 1.0
  • Mono 2.0
  • Mono 3.5
  • Mono 4.0
  • Microsoft Shared Source CLI 1.0
  • CLI 1.0 Compatible

并不是所有的框架都是平等的,一些特性被排除在一些构建(的程序集)之外。有关更多信息,请参阅框架支持文档。

2、Loggers 与 Appenders

Log4Net 有三个主要的组件:loggers, appenderslayouts。这三种类型的组件协同工作使得开发者能够根据消息类型和级别记录日志消息,并在运行时控制这些消息的格式和报告位置。这些组件由 filters 辅助,filters 控制着(将对象转换为字符串的)object renderers 和 appender 的动作。

3、Logger 层次结构

与普通的 System.Console.WriteLine 相比,任何日志 API 的首要优势在于它能够禁用某些日志语句,同时允许其他日志语句不受阻碍地打印。此功能假设日志空间,即所有可能的日志语句的空间,是根据开发人员选择的一些标准进行分类的。

Loggers 是命名实体。Loggers 名称区分大小写,并遵循以下分层命名规则:

层次结构命名

如果一个 logger 名称后跟一个点是后代 logger 名称的前缀,则该 logger 被称为另一个 logger 的祖先。如果在其自身和后代 logger 之间没有祖先,则称其为子 logger 的父 logger。

层次结构的工作方式与 .NET 中的命名空间和类层次结构非常相似。这是非常方便的,我们很快就会看到。

例如:名为 “Foo.Bar” 的 logger 是名为 “Foo.Bar.Baz” 的 logger 的父级。
类似的,“System” 是 “System.Text” 的父级以及 “System.Text.StringBuilder” 的祖先。大多数开发人员应该熟悉这套命名方案。

root logger 位于 logger 层次结构的顶层。它主要有三点不同:

  1. 它总是存在的
  2. 它不能按名称检索
  3. 它总是有一个指定的级别

使用 log4net.LogManager 类的静态方法以检索 loggers。GetLogger 方法将所需 logger 的名称作为参数。如下列所示:

namespace log4net
{
    public class LogManager
    {
        public static ILog GetLogger(string name);
        public static ILog GetLogger(Type type);
    }
}

接收 Type 参数的 GetLogger 方法使用完全限定类型名作为要检索的 logger 名称。

这些 GetLogger 方法返回一个 ILog 接口。这是传递给开发人员的 Logger 的表现形式。ILog 接口定义如下:

namespace log4net
{
    public interface ILog
    {
        /* Test if a level is enabled for logging */
        bool IsDebugEnabled { get; }
        bool IsInfoEnabled { get; }
        bool IsWarnEnabled { get; }
        bool IsErrorEnabled { get; }
        bool IsFatalEnabled { get; }
 
        /* Log a message object */
        void Debug(object message);
        void Info(object message);
        void Warn(object message);
        void Error(object message);
        void Fatal(object message);
 
        /* Log a message object and exception */
        void Debug(object message, Exception t);
        void Info(object message, Exception t);
        void Warn(object message, Exception t);
        void Error(object message, Exception t);
        void Fatal(object message, Exception t);
 
        /* Log a message string using the System.String.Format syntax */
        void DebugFormat(string format, params object[] args);
        void InfoFormat(string format, params object[] args);
        void WarnFormat(string format, params object[] args);
        void ErrorFormat(string format, params object[] args);
        void FatalFormat(string format, params object[] args);
 
        /* Log a message string using the System.String.Format syntax */
        void DebugFormat(IFormatProvider provider, string format, params object[] args);
        void InfoFormat(IFormatProvider provider, string format, params object[] args);
        void WarnFormat(IFormatProvider provider, string format, params object[] args);
        void ErrorFormat(IFormatProvider provider, string format, params object[] args);
        void FatalFormat(IFormatProvider provider, string format, params object[] args);
    }
}

loggers 也许 被分配了级别。级别是 log4net.Core.Level 类的实例。以下级别按优先级的先后顺序定义:

  • ALL
  • DEBUG
  • INFO
  • WARN
  • ERROR
  • FATAL
  • OFF

如果给定的 logger 没有被分配级别,那么它将从具有指定级别的最接近的祖先处继承一个级别。更正式地:

级别继承
给定 logger X 的继承级别等于 logger 层次结构中的第一个非空级别,从 X 开始,在层次结构中向上进行,直至 root logger。

为了确保所有的 logger 最终都可以继承一个级别,root logger 总是有一个分配的级别。其默认值是 DEBUG。

下面是四个表,其中包含各种分配的级别值和根据上述规则继承级别结果。

Logger 名称分配的级别继承的级别
rootProotProot
XnoneProot
X.YnoneProot
X.Y.ZnoneProot

上例一中,只有 root logger 被分配了级别。该级别值,即 Proot,由其他 logger X、X.Y、X.Y.Z 继承。

Logger 名称分配的级别继承的级别
rootProotProot
XPxPx
X.YPxyPxy
X.Y.ZPxyzPxyz

上例二中,所有的 logger 都有分配的级别值。所以不需要级别继承

Logger 名称分配的级别继承的级别
rootProotProot
XPxPx
X.YnonePx
X.Y.ZPxyzPxyz

上例三中,root、X 以及 X.Y.Z logger 分别被分配为级别 Proot、Px 以及 Pxyz。logger X.Y 从其父级 X 处继承其级别值。

Logger 名称分配的级别继承的级别
rootProotProot
XPxPx
X.YnonePx
X.Y.ZnonePx

上例四中,root 以及 X logger 分别被分配为级别 Proot 以及 Px。logger X.Y 以及 X.Y.Z 从最近的具有指定级别的父级 X 处继承其级别值。

日志记录请求是(通过 log4net.ILog)调用 logger 实例的一个打印方法发出的。这些打印方法是 DebugInfoWarnErrorFatal

根据定义,打印方法确定日志记录请求的级别。例如:如果 log 是一个 logger 实例,则语句 log.Info("..") 是一个级别为 INFO 的日志请求。

如果日志记录请求的级别高于或等于其 logger 的级别,则说该日志记录请求已启用。否则,请求将被禁用。没有指定级别的 logger 将从层次结构中继承一个级别。这条规则总结如下:

基本选择规则
如果 L ≥ K,则启用级别为 K(指定的或继承的,视情况而定)的 logger 中的级别为 L 的日志请求。

这条规则是 log4net 的核心。它假设级别是有序的。对于标准级别,我们有 DEBUG < INFO < WARN < ERROR < FATAL。

使用相同的名称调用 log4net.LogManager.GetLogger 方法将始终返回对完全相同的 logger 对象的引用。

例如,在

ILog x = LogManager.GetLogger("wombat");
ILog y = LogManager.GetLogger("wombat");

中,xy 引用完全相同的 logger 对象。

因此,这使得配置一个 logger 并随后在不传递引用的前提下在代码中的另外一个地方检索相同的实例成为可能。log4net logger 可以按任何顺序进行创建和配置,这与生物学上的亲子关系(父母总是先于孩子)存在根本矛盾。特别是,“parent” logger 将找到并链接到它的后代,即使它是在它们之后实例化的。

log4net 环境的配置通常在应用程序初始化时完成。首选的方法是读取配置文件。稍后将讨论这种方法。

通过软件组件,log4net 使得 loggers 命名变得非常容易。这可以通过在每个类中静态实例化一个 logger 来实现,logger 的名称等于类的完全限定名称。这是定义 logger 的一种有用且直接的方法。由于日志输出带有生成 logger 的名称,因此这种命名策略可以很容易地识别日志消息的来源。然而,这只是命名 loggers 的一种可能的策略,尽管很常见。log4net 不限制可能的 loggers 集合。开发人员可根据需要自由地命名 loggers。

尽管如此,以 logger 所在的类命名 logger 似乎是迄今为止已知的最佳策略。对于开发人员来说,要明确每个日志消息的来源很简单。最重要的是,它利用应用程序的设计来生成 logger 层次结构的设计。希望应用程序的设计中已经考虑了一些问题。

4、Appenders

基于 logger 选择性地启用或禁用日志记录请求的能力只是其中的一部分。Log4Net 允许日志请求打印到多个目的地。在 Log4Net 中,输出目的地被称为 appender。Appenders 必须实现 log4net.Appenders.IAppender 接口。

在 Log4Net 包中定义了以下 appenders:

类型描述
log4net.Appender.AdoNetAppender使用预处理语句或存储过程将日志事件写入数据库。
log4net.Appender.AnsiColorTerminalAppender将颜色突出显示的日志事件写入一个 ANSI 终端窗口。
log4net.Appender.AspNetTraceAppender将日志事件写入 ASP 跟踪上下文。然后可以在 ASP 页面的末尾或 ASP 跟踪页面上呈现这些内容。
log4net.Appender.BufferingForwardingAppender在将日志事件转发给子 appender 之前缓冲日志事件。
log4net.Appender.ColoredConsoleAppender将日志事件写入应用程序的控制台。事件可以进入标准输出流,也可以进入标准错误流。事件可以为每个级别定义可配置的文本和背景颜色。
log4net.Appender.ConsoleAppender将日志事件写入应用程序的控制台。事件可以进入标准输出流,也可以进入标准错误流。
log4net.Appender.DebugAppender将日志事件写入 .NET 系统。
log4net.Appender.EventLogAppender将日志事件写入 Windows 事件日志。
log4net.Appender.FileAppender将日志事件写入文件系统中的文件。
log4net.Appender.ForwardingAppender将日志事件转发给子 appender。
log4net.Appender.LocalSyslogAppender将日志事件写入本地 syslog 服务(仅限 UNIX)。
log4net.Appender.MemoryAppender将日志事件存储在内存缓冲区中。
log4net.Appender.NetSendAppender将日志事件写入 Windows Messenger 服务。这些消息显示在用户终端的对话框中。
log4net.Appender.OutputDebugStringAppender将日志事件写入调试器。如果应用程序没有调试器,系统调试器将显示该字符串。如果应用程序没有调试器并且系统调试器未激活,则忽略该消息。
log4net.Appender.RemoteSyslogAppender使用 UDP 网络将日志事件写入远程 syslog 服务。
log4net.Appender.RemotingAppender使用 .NET 远程处理将日志事件写入远程处理接收器。
log4net.Appender.RollingFileAppender将日志事件写入文件系统中的文件。RollingFileAppender 可以配置为根据日期或文件大小限制记录多个文件。
log4net.Appender.SmtpAppender将日志事件发送到电子邮件地址。
log4net.Appender.SmtpPickupDirAppender将 SMTP 邮件作为文件写入取件目录。然后,可以通过 SMTP 代理(如 IIS SMTP 代理)读取和发送这些文件。
log4net.Appender.TelnetAppender客户端通过 Telnet 连接以接收日志事件。
log4net.Appender.TraceAppender将日志事件写入 .NET 跟踪系统。
log4net.Appender.UdpAppender使用 UdpClient 将日志事件作为无连接 UDP 数据报发送到远程主机或多播组。

一个 logger 可以附加多个 appender。

给定 logger 的每个启用的日志记录请求将被转发到该 logger 中的所有 appender 以及层次结构中更高的 appender。 换句话说,appenders 是从 logger 层次结构中累加继承的。例如,如果将控制台 appender 添加到 root logger,那么所有启用的日志请求将至少在控制台上打印。如果另外一个文件 appender 被添加到 logger X 中,那么对 XX 的子级启用的日志记录请求将在文件和控制台上打印。通过将 logger 上的 additivity 设置为 false,可以覆盖此默认行为,以便不再添加 appender 累积。

支配 appender 累加的规则总结如下:

appender 可加性

logger X 的日志语句的输出将转到 X 及其祖先中的所有 appenders。这就是术语 "appender additivity" 的含义。

但是,如果 logger X 的祖先,比如 Y,将 additivity 标志设置为 false,则 X 的输出将定向到 X 及其上至 Y(包括 Y)的祖先中的所有 appenders;但不定向到 Y 的任何祖先中的 appenders。

默认情况下,loggers 将其 additivity 标志设置为 true

下表展示了一个示例:

Logger 名称添加的 AppendersAdditivity 标志输出目标说明
rootA1不适用A1没有默认的 appender 附加到 root
xA-x1, A-x2trueA1, A-x1, A-x2“x” 和 root 的 appenders
x.ynonetrueA1, A-x1, A-x2“x” 和 root 的 appenders
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1“x.y.z”、“x” 和 root 中的 appenders
securityA-secfalseA-sec因为 additivity 标志设置为 false,因此没有 appender 累积
security.accessnonetrueA-sec只有 “security” 的 appenders,因为 “security” 中的 additivity 标志设置为了 false

5、Filters

appenders 可以过滤传递给它们的事件。可以在配置中指定 filters,以便对通过不同 appenders 记录的事件进行精细控制。

最简单的控制形式是在 appender 上指定一个 Threshold(阈值)。其工作原理是只记录级别大于或等于阈值的事件。

更复杂和自定义的事件过滤可以使用在每个 appender 上定义的过滤器链来完成。filters 必须实现 log4net.Filter.IFilter 接口。

在 Log4Net 包中定义了以下 filters:

类型描述
log4net.Filter.DenyAllFilter丢弃所有日志事件。
log4net.Filter.LevelMatchFilter和事件的级别完全匹配。
log4net.Filter.LevelRangeFilter对一系列级别进行匹配。
log4net.Filter.LoggerMatchFilter与记录器名称的开头匹配。
log4net.Filter.PropertyFilter匹配特定属性值的子字符串。
log4net.Filter.StringMatchFilter匹配事件消息中的子字符串。

filters 可以配置为根据匹配接受或拒绝事件。

6、Layouts

通常情况下,用户不仅希望自定义输出目的地,还希望自定义输出格式。这是通过将 layout 与 appender 关联来实现的。layout 负责根据用户的意愿格式化日志记录请求;而 appender 负责将格式化后的输出发送到目的地。PatternLayout 是标准 log4net 分发的一部分,它允许用户根据类似于 C 语言 printf 函数的转换模式指定输出格式。

例如,带有转换模式 “%timestamp [%thread] %-5level %logger - %message%newline” 的 PatternLayout 将输出类似于以下内容:

176 [main] INFO  Com.Foo.Bar - Located nearest gas station.

第一个字段是程序开始后经过的毫秒数。第二个字段是发出日志请求的线程。第三个字段是日志语句的级别。第四个字段是与日志请求关联的 logger 的名称。“-”后面的文本是语句的消息。

log4net 包中包含以下 layouts:

类型描述
log4net.Layout.ExceptionLayout呈现来自日志事件的异常文本。
log4net.Layout.PatternLayout根据一组灵活的格式化标志来格式化日志事件。
log4net.Layout.RawTimeStampLayout从日志事件中提取时间戳。
log4net.Layout.RawUtcTimeStampLayout以通用时间(UTC)从日志事件中提取时间戳。
log4net.Layout.SimpleLayout非常简单地格式化日志事件:[level] - [message]
log4net.Layout.XmlLayout将日志事件格式化为 XML 元素。
log4net.Layout.XmlLayoutSchemaLog4j将日志事件格式化为符合 log4j 事件文件类型定义的 XML 元素。

7、Object Renderers

同样重要的是,log4net 将根据用户指定的标准呈现日志消息的内容。例如,如果您经常需要记录橙子(当前项目中使用的对象类型),那么您可以注册一个 OrangeRenderer,每当需要记录橙子时就会调用它。

对象呈现遵循类层次结构。例如,假设橙子是水果,如果你注册了一个 FruitRenderer,那么包括橙子在内的所有水果都将由 FruitRenderer 渲染,除非你注册了一个特定于橙子的 OrangeRenderer。

object renderers 必须实现 log4net.ObjectRenderer.IObjectRenderer 接口。

请注意,ObjectRenderers 不会被 DebugFormat、InfoFormat、WarnFormat、ErrorFormat 和 FatalFormat 方法使用。

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

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

相关文章

LabVIEW利用以太网开发智能液位检测仪

LabVIEW利用以太网开发智能液位检测仪 目前&#xff0c;工业以太网接口在国内外的发展已经达到了相当深入的程度&#xff0c;特别是在自动化控制和工业控制领域有着非常广泛的应用。在工业生产过程中&#xff0c;钢厂的连铸机是前后的连接环节&#xff0c;其中钢水从大钢包进入…

ToBeWritten之改进威胁猎杀:自动化关键角色与成功沟通经验

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

uni-app--》基于小程序开发的电商平台项目实战(四)

&#x1f3cd;️作者简介&#xff1a;大家好&#xff0c;我是亦世凡华、渴望知识储备自己的一名在校大学生 &#x1f6f5;个人主页&#xff1a;亦世凡华、 &#x1f6fa;系列专栏&#xff1a;uni-app &#x1f6b2;座右铭&#xff1a;人生亦可燃烧&#xff0c;亦可腐败&#xf…

八大排序详解(默认升序)

一、直接插入排序 直接插入排序&#xff1a;直接插入排序就是像打扑克牌一样&#xff0c;每张牌依次与前面的牌比较&#xff0c;遇到比自己大的就将大的牌挪到后面&#xff0c;遇到比自己小的就把自己放在它后面(如果自己最小就放在第一位)&#xff0c;所有牌排一遍后就完成了排…

PX4仿真添加world模型文件,并使用yolov8进行跟踪

前言 目的:我们是为了在无人机仿真中使用一个汽车模型,然后让仿真的无人机能够识别到这个汽车模型。所以我们需要在无人机仿真的环境中添加汽车模型。 无人机仿真中我们默认使用的empty.world文件,所以只需要将我们需要的模型添加到一起写进这个empty.world文件中去就可以…

电脑多开微信教程,可以多开n个

下载地址 链接&#xff1a;https://pan.baidu.com/s/1uWXIhfTZ-aD0A4RBxrI8bg?pwdy2s5 提取码&#xff1a;y2s5 效果如图&#xff1a;

地下水数值模拟软件如何选择?GMS、Visual MODFLOW Flex、FEFLOW、MODFLOW

强调模块化教学&#xff0c;分为前期数据收集与处理&#xff1b;三维地质结构建模&#xff1b;地下水流动模型构建&#xff1b;地下水溶质运移模型构建和反应性溶质运移构建5个模块&#xff1b;采用全流程模式将地下水数值模拟软件GMS的操作进行详细剖析和案例联系。不仅使学员…

Android中的RxJava入门及常用操作符

文章目录 1.定义2.作用3.特点4.使用4.1创建被观察者&#xff08;Observable&#xff09;4.2创建观察者&#xff08;Observer&#xff09;4.3订阅&#xff08;Subscribe&#xff09;4.4Dispose 5.操作符5.1操作符类型5.2just操作符5.2链式调用5.3 fromArray操作符5.4 fromIterab…

服务器文件备份

服务器上&#xff0c;做好跟应用程序有关的文件备份&#xff08;一般备份到远程的盘符&#xff09;&#xff0c;有助于当服务器发生硬件等故障时&#xff0c;可以对系统进行进行快速恢复。 下面以Windows服务器为例&#xff0c;记录如何做文件的备份操作。 具体操作如下&#…

贷款行业,教你如何直接沟通客户

信贷行业拓展业务的人力与时间成本非常高。如是做小微贷款业务的公司可能在寻找贷款客户、筛选客户资质这两项初始工作上花掉超过50%的精力。 并且由于行业特殊性&#xff0c;金融信贷受政策的影响比较大&#xff0c;没法形成固定的推广渠道&#xff0c;线上营销不好做&#x…

什么是站内搜索引擎?如何在网站中加入站内搜索功能?

在当今数字时代&#xff0c;用户体验对于网站的成功起着至关重要的作用。提升用户体验和改善整体网站性能的一种方法是引入站内搜索引擎。站内搜索引擎是一种强大的工具&#xff0c;它的功能类似于Google或Bing等流行搜索引擎&#xff0c;但它专注于实施自己网站上的内容。用户…

工业路由器项目应用(4g+5g两种工业路由器项目介绍)

引言&#xff1a; 随着工业智能化的不断发展&#xff0c;工业路由器在各个领域的应用越来越广泛。本文将介绍两个工业路由器项目的应用案例&#xff0c;一个是使用SR500 4g工业路由器&#xff0c;另一个是使用SR800 5g工业路由器。 详情&#xff1a;https://www.key-iot.com/i…

IPO观察丨重新启动上市,“小而美”能让科迪乳业再次出圈吗?

如今&#xff0c;乳制品市场俨然是一片红海&#xff0c;尽管市场竞争激烈&#xff0c;但对于一些企业而言&#xff0c;发展机会仍然相当可观。 近日举办的2023年中工作会议上&#xff0c;科迪乳业母公司科迪集团对外表示&#xff0c;要部署好下个阶段的重点工作&#xff0c;为…

Jenkins 添加节点Node报错JNI error has occurred UnsupportedClassVersionError

节点日志 报错信息如下 Error: A JNI error has occurred, please check your installation and try again Exception in thread “main” java.lang.UnsupportedClassVersionError: hudson/remoting/Launcher has been compiled by a more recent version of the Java Runtime…

阿里云服务器通用算力型、经济型、七代云服务器实例、倚天云服务器实例区别参考

目前阿里云服务器的实例规格中&#xff0c;既有五代六代实例规格&#xff0c;也有七代和八代倚天云服务器&#xff0c;同时还有通用算力型及经济型这些刚推出不久的新品云服务器实例&#xff0c;其中第五代实例规格已经不是主推的实例规格了&#xff0c;现在主售的实例规格是七…

windows server 2012 R2的C盘空间满了,但是找不到大文件的两种原因

目录 一、第一种原因&#xff1a;windows server backup备份导致C盘空间耗尽 二、第二种原因&#xff1a;超级桌管软件生成的文件放在C盘被隐藏 最近经历了两次C盘满了&#xff0c;但是又找不到大文件的问题&#xff0c;定位了许久&#xff0c;以下是两种原因。 一、第一种原…

python实现UI自动化配置谷歌浏览器驱动

web端UI自动化执行在哪个浏览器&#xff0c;需要对应哪个浏览器的驱动。以谷歌浏览器为例&#xff0c;进行配置。一、查看谷歌浏览器版本 如下截图&#xff1a;我的谷歌浏览器版本是&#xff1a; 117.0.5938.150 二、下载对应版本谷歌浏览器驱动 首先可以从其他版本驱动地址中…

超详细 | 鲸鱼优化算法原理及其实现(Matlab/Python)

鲸鱼优化算法(whale optimization algorithm,WOA)是由Mirjalili和Lewis[1]于2016年提出的一种新型群体智能优化搜索方法,它源于对自然界中座头鲸群体狩猎行为的模拟&#xff0c;该算法整个过程包含搜索觅食、收缩包围和螺旋更新位置三个阶段。 鲸鱼优化算法的三个种群更新机制…