Apache log4net™ 常见问题解答
信息
什么是 log4net?
log4net 是一个工具,可以帮助程序员将日志语句输出到各种输出目标。
如果应用程序出现问题,启用日志记录将有助于定位问题。使用 log4net,可以在运行时启用日志记录,而无需修改应用程序二进制文件。log4net 包的设计使得日志语句可以保留在生产代码中,而不会产生高性能成本。因此,日志记录的速度(或者更确切地说是不记录日志)至关重要。
同时,日志输出可能非常庞大,很快就会变得难以管理。log4net 的一个独特功能(也是所有 log4x 库的共同点)是层次结构日志记录器的概念。使用这些日志记录器,可以以任意粒度选择性地控制哪些日志语句输出。
log4net 的设计目标是速度和灵活性。这两个要求之间存在着微妙的平衡。
log4net 是一个可靠的日志记录系统吗?
不是。log4net 不可靠。它是一个尽力而为的故障停止日志记录系统。
故障停止意味着 log4net 不会在运行时抛出意外异常,从而可能导致应用程序崩溃。 如果由于任何原因,log4net 抛出未捕获的异常(除了 ArgumentException 和 ArgumentNullException,它们可能会被抛出), 请发送电子邮件到 [email protected] 邮件列表。未捕获的异常被视为需要立即关注的严重错误。
此外,当 log4net 指定的输出流未打开、不可写或已满时,它不会恢复到 System.Console.Out 或 System.Console.Error。这避免了由于日志记录失败而导致用户终端被大量数据淹没,从而破坏了原本正常工作的程序。但是,log4net 会向 System.Console.Error 和 System.Diagnostics.Trace 输出一条消息,表明无法执行日志记录。
log4net 的功能是什么?
- log4net 针对速度进行了优化。
- log4net 基于命名日志记录器层次结构。
- log4net 是故障停止的,但不可靠。
- log4net 是线程安全的。
- log4net 不限于预定义的设施集。
- 可以使用配置文件在运行时设置日志记录行为。配置文件采用 XML 格式。
- log4net 从一开始就被设计为处理异常。
- log4net 可以将其输出定向到许多接收器,包括:文件、控制台、NT 事件日志,甚至电子邮件。
- log4net 将日志记录分为几个级别:DEBUG、INFO、WARN、ERROR 和 FATAL。
- 可以通过实现新的布局类轻松更改日志输出的格式。
- 可以通过编写新的追加器类来更改日志输出的目标以及写入策略。
- log4net 支持每个日志记录器多个输出追加器。
有关 log4net 特性的更多信息,请参阅 特性 概述文档。
日志输出是什么样的?
日志输出可以通过多种方式自定义。此外,可以通过实现自己的 ILayout 来完全覆盖输出格式。
以下是一个使用 PatternLayout 和转换模式 %timestamp [%thread] %-5level %logger{2} %ndc - %message%newline 的示例输出。
176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse order. 225 [main] INFO examples.SortAlgo - Entered the sort method. 262 [main] DEBUG SortAlgo.OUTER i=1 - Outer loop. 276 [main] DEBUG SortAlgo.SWAP i=1 j=0 - Swapping intArray[0] = 1 and intArray[1] = 0 290 [main] DEBUG SortAlgo.OUTER i=0 - Outer loop. 304 [main] INFO SortAlgo.DUMP - Dump of integer array: 317 [main] INFO SortAlgo.DUMP - Element [0] = 0 331 [main] INFO SortAlgo.DUMP - Element [1] = 1 343 [main] INFO examples.Sort - The next log statement should be an error message. 346 [main] ERROR SortAlgo.DUMP - Tried to dump an uninitialized array. 467 [main] INFO examples.Sort - Exiting main method.
第一个字段是程序启动后经过的毫秒数。第二个字段是输出日志语句的线程。第三个字段是日志语句的级别。第四个字段是发出日志请求的日志记录器的名称的最右边两个组件。第五个字段(在 '-' 之前)是嵌套诊断上下文 (NDC)。请注意,嵌套诊断上下文可能是空的,如前两个语句所示。'-' 后的文本是语句的消息。
什么是日志记录器?
日志记录器概念是 log4net 配置的核心。日志记录器被组织成一个层次结构,并为程序员提供了运行时控制,可以控制哪些日志语句被打印或不被打印。
日志记录器通过 log4net 的配置分配级别。日志语句根据其级别和其日志记录器路由到追加器。
为什么我应该将我的 log4net 扩展捐赠回项目?
与 GNU 通用公共许可证 (GPL) 相反,Apache 软件许可证对您的扩展不作任何声明。扩展是指完全新的代码,它调用现有的 log4net 代码。您可以随意处置您的专有 log4net 扩展。特别是,您可以选择永远不将您的扩展发布给更广泛的公众。有关详细信息,请参阅 Apache 许可证 2.0 版。
我们非常小心地避免不必要地更改 log4net 客户端 API,以便较新的 log4net 版本与以前的版本向后兼容。对于 log4net 内部 API,我们没有那么谨慎。因此,如果您的扩展旨在与特定 log4net 版本的内部机制一起使用,那么当 log4net 的下一个版本发布时,您可能需要将您的专有扩展适应新版本。因此,您将被迫花费宝贵的资源来跟上 log4net 的变化。这通常被称为“愚蠢税”。通过捐赠代码并将其作为标准分发的一部分,您可以节省不必要的维护工作。
如果您的扩展有用,那么最终会有人编写一个提供相同或非常相似功能的扩展。您的开发工作将被浪费。
除非专有 log4net 扩展对业务至关重要,否则没有理由不将您的扩展捐赠回项目。
在贡献代码时,我应该注意什么?
- 即使您不喜欢,也要坚持现有的缩进风格。
在缩进风格之间交替会使源代码难以理解。让自己难做,但让其他人更容易。
-
彻底测试您的代码。
在调试(即日志记录)代码中发现错误是最令人恼火的事情。
- 保持简单、小巧和快速。
一切都是关于应用程序,而不是关于日志记录。
- 我是否提到了坚持缩进风格?
log4net 的历史是什么?
log4net 是流行的 Apache log4j™ 日志记录库的移植版本。最初的移植工作于 2001 年 6 月完成,从那时起,我们一直试图保持最初 log4j 的精神。有关更多详细信息,请参阅 log4net 历史 页面。
在哪里可以找到 log4net 的最新分发版?
log4net 主页 是一个很好的起点。
配置
如何在运行时更改日志行为?
可以使用在运行时解析的配置文件来设置日志记录行为。使用配置文件,程序员可以定义日志记录器并设置其级别。
配置文件以 XML 格式指定。有关更多详细信息,请参阅 log4net.Config.XmlConfigurator。
有关特定配置选项,请参阅各种 log4net.Layout 和 log4net.Appender 组件。
如何在运行时完全禁用所有日志记录?
将层次结构上的 Threshold 设置为 Level OFF 将禁用该层次结构的所有日志记录。这可以通过在 log4net 配置文件中的 log4net 配置元素上设置“threshold”属性为“OFF”来完成。例如
<log4net threshold="OFF" />
追加器的可配置选项有哪些?
log4net 使用公共属性来配置组件,例如追加器、布局、日志记录器等。
因此,追加器上的任何可写公共属性都对应于一个可配置选项。例如,在 RollingFileAppender 中,public int MaxSizeRollBackups { set; } 属性对应于 MaxSizeRollBackups 选项。
布局选项也由其可写属性定义。大多数其他 log4net 组件也是如此。
是否可以按级别将日志输出定向到不同的追加器?
可以。设置扩展 AppenderSkeleton 的任何追加器的 Threshold 选项(大多数 log4net 追加器扩展 AppenderSkeleton)将过滤掉所有级别低于阈值选项值的日志事件。
例如,将追加器的阈值设置为 DEBUG 也会允许 INFO、WARN、ERROR 和 FATAL 消息与 DEBUG 消息一起记录。(DEBUG 是最低级别)。这通常是可以接受的,因为没有周围的 INFO、WARN、ERROR 和 FATAL 消息,DEBUG 消息几乎没有用。类似地,将追加器的阈值设置为 ERROR 将过滤掉 DEBUG、INFO 和 WARN 消息,但不会过滤掉 ERROR 或 FATAL 消息。
此策略通常最能体现用户真正想要做的事情,而不是他们脑海中设想的解决方案。
如果您必须按精确级别匹配过滤事件,那么可以将 LevelMatchFilter 附加到任何追加器,以按精确级别匹配过滤掉日志记录事件。
是否有办法让 log4net 在配置文件更改时自动重新加载配置文件?
是的。 XmlConfigurator 通过 ConfigureAndWatch API 支持自动重新加载。有关详细信息,请参阅 API 文档。
我可以从另一个程序集加载附加程序吗?
是的。在配置文件中指定类型时,您可以提供类型的程序集限定名称。例如
<appender name="..." type="MyNamespace.MyAppender, MyAssembly">
.NET 运行时将尝试定位名为 MyAssembly 的程序集。.NET 如何定位程序集超出了本常见问题解答的范围。
从 GAC 加载程序集时,必须指定完全限定的程序集名称,包括版本、区域性和公钥。这符合 System.Type.GetType 支持的标准语法。有关如何获取程序集的版本和公钥的信息,请参阅下一个常见问题解答。
如何获取程序集的公钥?
程序集的完全限定名称包括版本、区域性和公钥。公钥源自用于标识发布者的强名称。从 GAC 引用程序集时,必须使用完全限定名称。要获取版本、区域性和公钥,您可以使用像 Lutz Roeder 的出色 .NET Reflector 这样的工具,该工具可从 http://www.aisto.com/roeder/dotnet 获取。
如何在布局标题中插入换行符?
配置文件中的换行符需要使用 XML 数字字符引用进行转义。表示 CR LF 的序列是 。以下示例在输出的开头和结尾添加一个标题和一个页脚,每个标题和页脚后跟一个换行符。
<layout type="log4net.Layout.PatternLayout"> <header value="[Header] " /> <footer value="[Footer] " /> <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" /> </layout>
如何使用模式设置字符串属性的值?
log4net 支持用于设置字符串属性的模式语法,类似于用于格式化输出消息的 PatternLayout。此模式语法可以通过在配置文件中指定字符串属性上的 type="log4net.Util.PatternString" 来使用。这告诉配置解析器在将结果转换为字符串之前将值传递给 PatternString 类型。
以下示例通过在 File 属性中指定 %processid 模式,将 FileAppender 的文件名设置为包含当前进程 ID。
<appender name="LogFileAppender" type="log4net.Appender.FileAppender"> <file type="log4net.Util.PatternString" value="log-file-[%processid].txt" /> <layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" /> </appender>
实现日志记录
有什么建议的命名记录器的方法吗?
是的,有一些。
您可以通过位置来命名日志记录器。事实证明,在每个类中实例化一个记录器,并将记录器名称设置为类的完全限定名称,是一种定义记录器的有用且直接的方法。这种方法有很多好处
- 它非常容易实现。
- 它非常容易向新开发人员解释。
- 它会自动反映您应用程序自身的模块化设计。
- 它可以根据需要进一步细化。
- 打印记录器会自动提供有关日志语句位置的信息。
但是,这不是命名记录器的唯一方法。一种常见的替代方法是按功能区域命名记录器。例如,“数据库”记录器、“远程处理”记录器、“安全”记录器或“XML”记录器。
您可以选择按功能命名记录器,并按位置进行细分,例如“DATABASE.MyApp.MyClass”或“DATABASE.MyApp.MyModule.MyOtherClass”。
您可以完全自由地选择记录器的名称。log4net 包只是允许您在层次结构中管理名称。但是,您有责任定义此层次结构。
注意:通过按位置命名记录器,人们往往会按功能命名事物,因为在大多数情况下,位置与功能密切相关。
如何在静态块中获取类的完全限定名称?
您可以使用语句 typeof(X).Name 轻松地在类 X 的静态块中检索类的完全限定名称。请注意,X 是类名,跨度是一个实例。但是,由于 LogManager.GetLogger 方法被重载以接受 Type 的实例以及 string,因此通常只需要类的类型。
以下是建议的用法模板
public class Foo { private static readonly ILog log = LogManager.GetLogger(typeof(Foo)); ... other code }
一种等效且更便携的解决方案(尽管稍微长一些)是使用静态构造函数的声明类型。
public class Foo { private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); ... other code }
注意:.NET Compact Framework 1.0 不支持 System.Reflection.MethodBase.GetCurrentMethod()。
注意:这两种形式仅在 Foo 不是泛型类时才等效。对于泛型类 Foo<T>,使用 typeof 的变体为每个不同的类型参数 T 生成不同的记录器,而使用反射的变体为所有 T 生成相同的记录器。
不进行日志记录的最快方法是什么?
对于某些记录器 log,编写,
log.Debug("Entry number: " + i + " is " + entry[i]);
会产生构造消息参数的成本,即将整数 i 和 entry[i] 都转换为字符串,并将中间字符串连接起来。无论消息是否会被记录,都会产生这种成本。
如果您担心速度,请编写
if(log.IsDebugEnabled) { log.Debug("Entry number: " + i + " is " + entry[i]); }
这样,如果为记录器 log 禁用了调试,您就不会产生参数构造的成本。另一方面,如果记录器启用了调试,您将产生两次评估记录器是否已启用:一次在 IsDebugEnabled 中,一次在 Debug 中。这是一个微不足道的开销,因为评估记录器所花费的时间不到实际记录语句时间的 1%。
真正不进行日志记录的最快方法是什么?
所以您认为前面的常见问题解答不是真正不进行日志记录的最快方法?好吧,有一种更快的方法,但它确实有一些缺点。从
if(log.IsDebugEnabled) { log.Debug("Entry number: " + i + " is " + entry[i]); }
可以进一步消除对 IsDebugEnabled 的调用,以便每个记录器只调用一次。如果您对每个类使用一个记录器,那么您可以在类的静态变量中存储记录器的启用状态,然后针对此变量进行测试
public class FastLogger { private static readonly ILog log = LogManager.GetLogger(typeof(FastLogger)); private static readonly bool isDebugEnabled = log.IsDebugEnabled; public void MyMethod() { if(isDebugEnabled) { log.Debug("Entry number: " + i + " is " + entry[i]); } } }
那么为什么这更快呢?好吧,首先,IsDebugEnabled 不会为每个日志语句调用,它每个记录器只调用一次。此外,由于 isDebugEnabled 变量是 private static readonly,因此 JIT 编译器可以在运行时完全优化掉 if 测试。这意味着在运行时,JIT 编译器甚至不会将日志记录语句编译成本机代码,即所有日志记录都会消失。
那么使用它的缺点是什么呢?好吧,log4net 的一个巧妙功能是,您可以在程序运行时更改日志记录配置。如果您需要调查应用程序中的问题,您不必停止应用程序、设置日志记录并重新启动应用程序,您可以更改日志记录配置,log4net 会重新加载它(有关详细信息,请参阅 XmlConfigurator.ConfigureAndWatch API)。但是,如果 JIT 已编译出所有日志记录语句,那么它们就消失了,您无法通过重新加载配置文件来恢复它们。实际上,这意味着日志记录配置只能在应用程序加载时设置,并且不能在运行时更改。由您决定是否需要最高速度或需要能够在应用程序运行时重新加载日志记录配置。
多个客户端请求的输出可以输出到不同的日志文件吗?
许多开发人员面临着区分来自同一类但不同客户端请求的日志输出的问题。他们想出了巧妙的机制来将日志输出扇出到不同的文件。在大多数情况下,这不是正确的方法。
使用上下文属性或堆栈(ThreadContext)更简单。通常,当开始处理客户端的请求时,您会将 ThreadContext.Properties["ID"] = "XXX" 客户端特定信息,例如客户端的主机名、ID 或任何其他区分信息。此后,日志输出将自动包含上下文数据,以便即使将日志输出到同一个文件,您也可以区分来自不同客户端请求的日志。
有关详细信息,请参阅 ThreadContext 和 PatternLayout 类。
如何让多个进程记录到同一个文件?
在您开始尝试任何提供的替代方案之前,问问自己是否真的需要让多个进程记录到同一个文件,然后不要这样做 ;-)。
FileAppender 为此用例提供了可插拔的锁定模型,但所有现有实现都存在问题和缺点。
默认情况下,FileAppender 在记录时会对日志文件保持独占写锁定。这会阻止其他进程写入文件。已知此模型在(至少在某些版本的)Mono 上的 Linux 上会失效,并且日志文件可能会在另一个进程尝试访问日志文件时立即损坏。
MinimalLock 仅在写入日志时才获取写锁定。这允许多个进程交错写入同一个文件,尽管性能会大幅下降。
InterProcessLock 根本不锁定文件,而是使用系统范围的互斥锁进行同步。这只有在所有进程都协作(并使用相同的锁定模型)时才有效。为每个要写入的日志条目获取和释放互斥锁会导致性能下降,但互斥锁比使用 MinimalLock 更可取。
如果您使用 RollingFileAppender,情况会更糟,因为多个进程可能会尝试同时开始滚动日志文件。 RollingFileAppender 在滚动文件时完全忽略锁定模型,滚动文件与这种情况根本不兼容。
更好的替代方案是让您的进程记录到 RemotingAppenders。使用 RemoteLoggingServerPlugin(或 IRemoteLoggingSink),进程可以接收所有事件并将它们记录到单个日志文件。其中一个示例展示了如何使用 RemoteLoggingServerPlugin。
如果我有多个进程跨多个主机(可能跨多个时区)使用 RemotingAppender 记录到同一个文件,时间戳会发生什么?
时间戳是在创建日志事件时创建的。也就是说,当调用 Debug、Info、Warn、Error 或 Fatal 方法时。这不受它们到达远程服务器的时间的影响。由于时间戳由 RemotingAppender 以 UTC 格式传输,因此它们都显示在创建日志文件的宿主机相同的时区。由于不同机器的时钟可能不同步,这可能会导致在不同主机上生成的事件之间的时间间隔不一致。
自定义
可以自定义日志输出格式吗?
可以。您可以实现 log4net.Layout.ILayout 接口来创建您自己的自定义日志格式,或者您可以扩展 LayoutSkeleton 类,该类提供了 ILayout 接口的默认实现。附加程序可以参数化以使用您选择的布局。
我可以编写自定义附加程序吗?
可以。您可以实现 log4net.Appender.IAppender 接口来创建您自己的自定义附加程序。我们建议您扩展 log4net.Appender.AppenderSkeleton 类,而不是从头开始。您应该在与 log4net 程序集分开的程序集中实现您的自定义代码。为了开始,值得查看 log4net.Appender.TraceAppender 的源代码,它是一个关于使附加程序正常工作的最小代码量的示例。
要配置 log4net 以使用您的自定义附加程序,您需要在配置文件中指定附加程序类型的程序集限定名。例如
<appender name="..." type="MyNamespace.MyAppender, MyAssembly">
.NET 运行时将尝试定位名为 MyAssembly 的程序集。.NET 如何定位程序集超出了本常见问题解答的范围。
故障排除
如何启用 log4net 内部调试?
有两种不同的方法可以在 log4net 中启用内部调试。这些方法列在下面。首选方法是在应用程序的配置文件中指定 log4net.Internal.Debug 选项。
-
还可以通过在应用程序的配置文件(不是 log4net 配置文件,除非 log4net 配置数据嵌入在应用程序的配置文件中)中设置一个值来启用内部调试。 log4net.Internal.Debug 应用程序设置必须设置为 true 值。例如
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="log4net.Internal.Debug" value="true"/> </appSettings> </configuration>
此设置在启动时立即读取,并将导致所有内部调试消息被发出。
-
要通过编程方式启用 log4net 的内部调试,您需要将 log4net.Util.LogLog.InternalDebugging 属性设置为 true。显然,设置得越早,产生的调试信息就越多。
内部调试消息被写入控制台和 System.Diagnostics.Trace 系统。如果应用程序没有控制台,则记录在其中的消息将丢失。请注意,应用程序可以通过设置 System.Console.Out 来重定向控制台流。默认情况下,跟踪系统会将消息发送到附加的调试器(消息将出现在输出窗口中)。如果进程没有附加调试器,则消息将发送到系统调试器。可以使用来自 http://www.sysinternals.com 的 DebugView 等实用程序来捕获这些消息。
由于 log4net 内部调试消息被写入 System.Diagnostics.Trace 系统,因此可以将这些消息重定向到本地文件。您可以通过将以下内容添加到应用程序的 .config 文件中来定义跟踪侦听器
<configuration> ... <system.diagnostics> <trace autoflush="true"> <listeners> <add name="textWriterTraceListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="C:\tmp\log4net.txt" /> </listeners> </trace> </system.diagnostics> ... </configuration>
确保运行应用程序的进程有权写入此文件。
如何在运行时评估配置错误?
为了防止 log4net 出现静默失败(如 LOG4NET-342 中所述),log4net 支持一种方法来评估它是否已配置,以及从 1.2.11 开始评估启动时生成的错误。要检查 log4net 是否已启动并正确配置,可以检查 log4net.Repository.ILoggerRepository.Configured 属性,并枚举配置消息,如下所示
if(!log4net.LogManager.GetRepository().Configured) { // log4net not configured foreach(log4net.Util.LogLog message in log4net.LogManager.GetRepository().ConfigurationMessages.Cast<log4net.Util.LogLog>()) { // evaluate configuration message } }
为什么 EventLogAppender 不起作用?
如果您没有收到传递到事件日志的事件,这通常表示存在权限问题。基本上,如果事件日志不存在,EventLogAppender 会尝试创建它,但您需要本地管理员权限才能创建事件日志(只需写入注册表中的正确位置)。您不需要管理员权限才能记录到现有的事件日志,但它必须存在。如果您从 Web 应用程序或使用事件日志的服务中使用事件日志,这可能有点棘手。
Web 应用程序将以 ASPNET 用户帐户运行。此帐户故意具有很少的权限,以减少有人入侵 Web 服务器的可能性。虽然该帐户有权写入事件日志,但它没有权限创建事件源(注册表创建和写入访问权限),而这些权限是写入事件日志所必需的。
有两种解决方案
-
将 ASPNET 用户添加到管理员组。这将起作用,因为用户将拥有所需的权限。这不建议用于生产环境。
-
由于事件源只需要为机器创建一次,因此创建一个安装程序并将其配置为创建事件源。安装程序需要以管理员身份运行(难道不是吗?)。有关如何创建简单事件日志安装程序的示例,请参阅 Microsoft .NET Framework SDK 中的 System.Diagnostics.EventLogInstaller。
有一篇 Microsoft 知识库文章介绍了此问题以及如何解决它。 PRB: "Requested Registry Access Is Not Allowed" Error Message When ASP.NET Application Tries to Write New EventSource in the EventLog .
为什么我不能从 Web 应用程序记录到 FileAppender?
Web 应用程序以 Web 服务器上的一个名为 ASPNET 的特殊用户帐户运行。此帐户具有受限权限,以保护 Web 服务器免受攻击。默认情况下,此帐户可能没有权限写入文件系统。确保 ASPNET 帐户有权创建和写入日志记录所选目录中的文件。
为什么我的服务中的日志记录不起作用?
Windows 服务以服务控制面板中指定的用户帐户运行。此帐户可能具有受限权限,确保该帐户有权创建和写入日志记录所选目录中的文件。
Windows 服务由 Windows 启动。服务中的当前目录设置为 Windows 系统目录(例如 C:\Windows\System32)。如果您从当前目录加载配置文件,请注意,此路径将不是您的程序集的位置。获取程序集路径的最佳方法是使用 AppDomain.BaseDirectory。请注意,log4net 内部从未使用当前目录。
为什么我的 ASP.NET Web 应用程序在部署到 IIS 上时停止记录?
此问题已由多人报告为问题 LOG4NET-178。该问题似乎是由 LOG4NET 配置错误或由应用程序关闭事件引起的计时问题造成的,该事件在应用程序启动事件之后很晚才出现,因此 LOG4NET 在启动后立即停止记录。
解决问题的第一个步骤是启用 log4net 内部调试功能,如 此处 所述,并修复所有出现的错误。如果问题仍然存在,此评论 建议将 LOG4NET 配置从 web.config 移动到一个单独的文件,例如 log4net.config。最后,如果这两个步骤都没有帮助,问题仍然存在,您可以尝试通过从类构造函数中调用配置调用来解决事件计时问题,如 此评论 中所述。
我在使用 AdoNetAppender 连接到我的数据库时遇到问题?
有关 ADO.NET 连接到数据库的不同方法的详细信息,请参阅: Connecting to a Data Source Using ADO.NET.
如果您需要使用 ODBC 连接到您的数据库,请注意,ADO.NET ODBC 驱动程序未包含在标准 .NET 框架可再发行组件中。您可以从 Microsoft 下载中心下载驱动程序: ODBC .NET Data Provider.
如何报告错误?
首先确保它确实是错误,而不是使用错误。如有疑问,请先在 log4net-user 邮件列表 上提问。
如果您已确定错误,请通过我们的 问题跟踪器 报告它。您可能想通过搜索现有问题来检查它是否已报告过。
log4net 在 RELEASE 模式下构建时不记录日志
如果您使用属性来配置 log4net,那么程序集加载的顺序可能会决定是否使用您的属性。程序集加载顺序在 DEBUG 和 RELEASE 模式下可能不同。
如 手册 中所述,该属性只会在第一个尝试使用 log4net 的程序集上读取。因此,尽早获取您的ILog实例非常重要。
对于命令行应用程序,“尽早”可能是包含Main方法的类,对于 Web 应用程序,它将是您的Global.asax类,对于 Windows 服务,它将是从服务基础.
log4net 根本没有记录日志
您可能忽略了应用程序中 log4net 的初始化代码。可以通过调用其中一个配置器(例如BasicConfigurator或XmlConfigurator在log4net.Config命名空间中显式初始化 log4net,或者通过包含[XmlConfiguratorAttribute]在首次使用 log4net 的程序集中隐式初始化 log4net。
有关更多信息,请参阅 手册。如果您使用属性配置 log4net,则程序集加载的顺序可能会决定是否使用您的属性。程序集加载顺序在 DEBUG 和 RELEASE 模式下可能不同。另请参阅 log4net 在 RELEASE 模式下构建时不记录日志。
ADO.NET 追加器在 .NET 4.5.1 上数据库故障后不会重新连接
从 .NET 4.5.1 开始,ADO.NET 添加了连接弹性,该弹性应该作为框架的一部分重新建立连接。因此,log4net 无法知道连接已断开,并且永远不会尝试重新建立连接。
不幸的是,重新连接似乎无法可靠地工作。一种解决方法可能是添加ConnectRetryCount=0到您的连接字符串中。
有关详细信息,请参阅 LOG4NET-442
其他
如何使 log4net 出现在 Visual Studio 添加引用对话框中?
Robert McLaws 博客上对此主题有一个很好的讨论:构建更好的服务器控件体验,第 2 部分。
您提供 Nuget 包吗?
从 2.0.6 版本开始,我们提供 Nuget 包。对于早期版本,Jiří Činčura 已经创建了 Nuget 包。