如何从头开始以编程方式配置 log4net(无配置)
Posted
技术标签:
【中文标题】如何从头开始以编程方式配置 log4net(无配置)【英文标题】:How to configure log4net programmatically from scratch (no config) 【发布时间】:2010-10-20 16:15:21 【问题描述】:我知道这是个坏主意,但是... 我想在没有配置文件的情况下从头开始以编程方式配置 log4net。我正在为我和我的团队开发一个简单的日志记录应用程序,用于我们负责的一堆相对较小的部门应用程序。我希望他们都登录到同一个数据库。日志记录应用程序只是 log4net 的包装器,预配置了 AdoNetAppender。
所有应用程序都是 ClickOnce 部署的,这在部署配置文件时出现了一个小问题。如果配置文件是核心项目的一部分,我可以将其属性设置为与程序集一起部署。但它是链接应用程序的一部分,所以我没有选择将它与主应用程序一起部署。 (如果这不是真的,请告诉我)。
可能因为这是一个坏主意,似乎没有太多示例代码可用于从头开始以编程方式配置 log4net。这是我目前所拥有的。
Dim apndr As New AdoNetAppender()
apndr.CommandText = "INSERT INTO LOG_ENTRY (LOG_DTM, LOG_LEVEL, LOGGER, MESSAGE, PROGRAM, USER_ID, MACHINE, EXCEPTION) VALUES (@log_date, @log_level, @logger, @message, @program, @user, @machine, @exception)"
apndr.ConnectionString = connectionString
apndr.ConnectionType = "System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
apndr.CommandType = CommandType.Text
Dim logDate As New AdoNetAppenderParameter()
logDate.ParameterName = "@log_date"
logDate.DbType = DbType.DateTime
logDate.Layout = New RawTimeStampLayout()
apndr.AddParameter(logDate)
Dim logLevel As New AdoNetAppenderParameter()
logLevel.ParameterName = "@log_level"
'And so forth...
在为apndr
配置了所有参数后,我一开始尝试了这个...
Dim hier As Hierarchy = DirectCast(LogManager.GetRepository(), Hierarchy)
hier.Root.AddAppender(apndr)
没有用。然后,作为在黑暗中的镜头,我尝试了这个。
BasicConfigurator.Configure(apndr)
那也没用。有没有人有关于如何在没有配置文件的情况下从头开始以编程方式配置 log4net 的好的参考资料?
【问题讨论】:
另见***.com/questions/1436713/… 【参考方案1】:这是一个完全在代码中创建 log4net 配置的示例类。我应该提到,通过静态方法创建记录器通常被认为是不好的,但在我的上下文中,这就是我想要的。无论如何,您可以根据自己的需要划分代码。
using log4net;
using log4net.Repository.Hierarchy;
using log4net.Core;
using log4net.Appender;
using log4net.Layout;
namespace dnservices.logging
public class Logger
private PatternLayout _layout = new PatternLayout();
private const string LOG_PATTERN = "%d [%t] %-5p %m%n";
public string DefaultPattern
get return LOG_PATTERN;
public Logger()
_layout.ConversionPattern = DefaultPattern;
_layout.ActivateOptions();
public PatternLayout DefaultLayout
get return _layout;
public void AddAppender(IAppender appender)
Hierarchy hierarchy =
(Hierarchy)LogManager.GetRepository();
hierarchy.Root.AddAppender(appender);
static Logger()
Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
TraceAppender tracer = new TraceAppender();
PatternLayout patternLayout = new PatternLayout();
patternLayout.ConversionPattern = LOG_PATTERN;
patternLayout.ActivateOptions();
tracer.Layout = patternLayout;
tracer.ActivateOptions();
hierarchy.Root.AddAppender(tracer);
RollingFileAppender roller = new RollingFileAppender();
roller.Layout = patternLayout;
roller.AppendToFile = true;
roller.RollingStyle = RollingFileAppender.RollingMode.Size;
roller.MaxSizeRollBackups = 4;
roller.MaximumFileSize = "100KB";
roller.StaticLogFileName = true;
roller.File = "dnservices.txt";
roller.ActivateOptions();
hierarchy.Root.AddAppender(roller);
hierarchy.Root.Level = Level.All;
hierarchy.Configured = true;
public static ILog Create()
return LogManager.GetLogger("dnservices");
【讨论】:
+1 来自我,您似乎在这里得到了如何在没有配置文件的情况下纯粹以编程方式执行此操作的答案。 好吧,它仍然不起作用,创建了空文本文件,但是没有写入任何内容:( +1 forhierarchy.Configured = true;
这对我有用
对我来说诀窍是roller.ActivateOptions()...一些黑暗的巫毒教。
@Legends "dnsservices.txt" 只是您的日志文件的相对名称。似乎与当前工作目录有关。我将其更改为用户系统上的绝对路径,因此日志将始终转到已知目录。【参考方案2】:
我过去这样做的一种方法是将配置文件作为嵌入式资源包含在内,并且只使用了log4net.Config.Configure(Stream)。
这样,我可以使用我熟悉的配置语法,而不必担心部署文件。
【讨论】:
完整的方法名称是 log4net.Config.XmlConfigurator.Configure(如链接)【参考方案3】:更简洁的解决方案:
var layout = new PatternLayout("%-4timestamp [%thread] %-5level %logger %ndc - %message%newline");
var appender = new RollingFileAppender
File = "my.log",
Layout = layout
;
layout.ActivateOptions();
appender.ActivateOptions();
BasicConfigurator.Configure(appender);
别忘了调用ActivateOptions方法:
在设置配置属性后,必须在此对象上调用 ActivateOptions 方法。在调用 ActivateOptions 之前,此对象处于未定义状态,不得使用。
【讨论】:
使用 BasicConfigurator.Configure(IAppender) 重载确实可以省去很多麻烦,干杯。 +1 为那个。调用ActivateOptions()
肯定是缺失的,或者至少在文档中没有得到足够的指出。【参考方案4】:
正如Jonathan 所说,使用资源是一个很好的解决方案。
这有点限制,因为嵌入的资源内容将在编译时修复。我有一个日志记录组件,它使用定义为 appSettings 的变量生成具有基本 Log4Net 配置的 XmlDocument(例如,RollingFileAppender 的文件名、默认日志记录级别,如果您想使用 AdoNetAppender,可能是连接字符串名称)。然后我调用log4net.Config.XmlConfigurator.Configure
使用生成的 XmlDocument 的根元素配置 Log4Net。
然后管理员可以通过修改一些 appSettings(通常是级别、文件名等)来自定义“标准”配置,或者可以指定外部配置文件以获得更多控制权。
【讨论】:
【参考方案5】:我无法在问题的代码 sn-p 中判断“'等等...”是否包含非常重要的 apndr.ActivateOptions() ,这在 Todd Stout 的回答中有所指示。如果没有 ActivateOptions(),Appender 将处于非活动状态,并且不会做任何可以解释其失败原因的事情。
【讨论】:
我不认为我有那个。这可能是问题所在。谢谢。【参考方案6】:聚会有点晚了。但这里有一个对我有用的最小配置。
示例类
public class Bar
private readonly ILog log = LogManager.GetLogger(typeof(Bar));
public void DoBar() log.Info("Logged");
最小的 log4net 跟踪配置(在 NUnit 测试中)
[Test]
public void Foo()
var tracer = new TraceAppender();
var hierarchy = (Hierarchy)LogManager.GetRepository();
hierarchy.Root.AddAppender(tracer);
var patternLayout = new PatternLayout ConversionPattern = "%m%n";
tracer.Layout = patternLayout;
hierarchy.Configured = true;
var bar = new Bar();
bar.DoBar();
打印到跟踪侦听器
Namespace+Bar: Logged
【讨论】:
这几乎可以工作,但我需要在 PatternLayout 和 Appender 上调用 .ActiveOptions 才能完全工作。 不知道为什么。它对我有用,也许我们使用了不同的版本。【参考方案7】:Dr. Netjes 有这个以编程方式设置连接字符串:
// Get the Hierarchy object that organizes the loggers
log4net.Repository.Hierarchy.Hierarchy hier =
log4net.LogManager.GetLoggerRepository() as log4net.Repository.Hierarchy.Hierarchy;
if (hier != null)
//get ADONetAppender
log4net.Appender.ADONetAppender adoAppender =
(log4net.Appender.ADONetAppender)hier.GetLogger("MyProject",
hier.LoggerFactory).GetAppender("ADONetAppender");
if (adoAppender != null)
adoAppender.ConnectionString =
System.Configuration.ConfigurationSettings.AppSettings["MyConnectionString"];
adoAppender.ActivateOptions(); //refresh settings of appender
【讨论】:
【参考方案8】:// 我已将三个配置文件嵌入为嵌入式资源并像这样访问它们:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Resources;
using System.IO;
namespace Loader
class Program
private static log4net.ILog CustomerLog = log4net.LogManager.GetLogger("CustomerLogging");
private static log4net.ILog OrderLog = log4net.LogManager.GetLogger("OrderLogging");
private static log4net.ILog DetailsLog = log4net.LogManager.GetLogger("OrderDetailLogging");
static void Main(string[] args)
// array of embedded log4net config files
string[] configs = "Customer.config", "Order.config", "Detail.config";
foreach (var config in configs)
// build path to assembly config
StringBuilder sb = new StringBuilder();
sb.Append(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
sb.Append(".");
sb.Append(config);
// convert to a stream
Stream configStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(sb.ToString());
// configure logger with ocnfig stream
log4net.Config.XmlConfigurator.Configure(configStream);
// test logging
CustomerLog.Info("Begin logging with: " + config);
OrderLog.Info("Begin logging with: " + config);
DetailsLog.Info("Begin logging with: " + config);
for (int iX = 0; iX < 10; iX++)
CustomerLog.Info("iX=" + iX);
OrderLog.Info("iX=" + iX);
DetailsLog.Info("iX=" + iX);
CustomerLog.Info("Ending logging with: " + config);
OrderLog.Info("Ending logging with: " + config);
DetailsLog.Info("Ending logging with: " + config);
【讨论】:
【参考方案9】:奇怪的是BasicConfigurator.Configure(apndr)
不起作用。在我的情况下,它完成了它的工作......但是,无论如何,答案就在这里 - 你应该在完成所有设置后写hier.Configured = true;
(c#代码)。
【讨论】:
【参考方案10】:我最终使用了这个:
http://www.mikebevers.be/blog/2010/09/log4net-custom-adonetappender/
经过 4 个小时的修改配置并逐渐变得更加沮丧。
希望对某人有所帮助。
【讨论】:
【参考方案11】:这是一个简单明了的示例,说明如何完全在代码中创建和使用AdoNetAdapter
,完全没有任何App.config
文件(甚至Common.Logging
也不行)。来吧,删除它!
这还有一个额外的好处是可以抵御new naming conventions 下的更新,其中程序集名称现在反映了版本。 (Common.Logging.Log4Net1213
等)
[SQL]
CREATE TABLE [Log](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Date] [datetime] NOT NULL,
[Thread] [varchar](255) NOT NULL,
[Level] [varchar](20) NOT NULL,
[Source] [varchar](255) NOT NULL,
[Message] [varchar](max) NOT NULL,
[Exception] [varchar](max) NOT NULL
)
[主要]
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Config
Imports log4net.Appender
Module Main
Sub Main()
Dim oLogger As ILog
Dim sInput As String
Dim iOops As Integer
BasicConfigurator.Configure(New DbAppender)
oLogger = LogManager.GetLogger(GetType(Main))
Console.Write("Command: ")
Do
Try
sInput = Console.ReadLine.Trim
Select Case sInput.ToUpper
Case "QUIT" : Exit Do
Case "OOPS" : iOops = String.Empty
Case Else : oLogger.Info(sInput)
End Select
Catch ex As Exception
oLogger.Error(ex.Message, ex)
End Try
Console.Clear()
Console.Write("Command: ")
Loop
End Sub
End Module
[DbAppender]
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Appender
Imports log4net.Repository.Hierarchy
Public Class DbAppender
Inherits AdoNetAppender
Public Sub New()
MyBase.BufferSize = 1
MyBase.CommandText = Me.CommandText
Me.Parameters.ForEach(Sub(Parameter As DbParameter)
MyBase.AddParameter(Parameter)
End Sub)
Me.ActivateOptions()
End Sub
Protected Overrides Function CreateConnection(ConnectionType As Type, ConnectionString As String) As IDbConnection
Return MyBase.CreateConnection(GetType(System.Data.SqlClient.SqlConnection), "Data Source=(local);Initial Catalog=Logger;Persist Security Info=True;User ID=username;Password=password")
End Function
Private Overloads ReadOnly Property CommandText As String
Get
Dim _
sColumns,
sValues As String
sColumns = Join(Me.Parameters.Select(Function(P As DbParameter) P.DbColumn).ToArray, ",")
sValues = Join(Me.Parameters.Select(Function(P As DbParameter) P.ParameterName).ToArray, ",")
Return String.Format(COMMAND_TEXT, sColumns, sValues)
End Get
End Property
Private ReadOnly Property Parameters As List(Of DbParameter)
Get
Parameters = New List(Of DbParameter)
Parameters.Add(Me.LogDate)
Parameters.Add(Me.Thread)
Parameters.Add(Me.Level)
Parameters.Add(Me.Source)
Parameters.Add(Me.Message)
Parameters.Add(Me.Exception)
End Get
End Property
Private ReadOnly Property LogDate As DbParameter
Get
Return New DbParameter("Date", DbType.Date, 0, New DbPatternLayout("%dateyyyy-MM-dd HH:mm:ss.fff"))
End Get
End Property
Private ReadOnly Property Thread As DbParameter
Get
Return New DbParameter("Thread", DbType.String, 255, New DbPatternLayout("%thread"))
End Get
End Property
Private ReadOnly Property Level As DbParameter
Get
Return New DbParameter("Level", DbType.String, 50, New DbPatternLayout("%level"))
End Get
End Property
Private ReadOnly Property Source As DbParameter
Get
Return New DbParameter("Source", DbType.String, 255, New DbPatternLayout("%logger.%M()"))
End Get
End Property
Private ReadOnly Property Message As DbParameter
Get
Return New DbParameter("Message", DbType.String, 4000, New DbPatternLayout("%message"))
End Get
End Property
Private ReadOnly Property Exception As DbParameter
Get
Return New DbParameter("Exception", DbType.String, 2000, New DbExceptionLayout)
End Get
End Property
Private Const COMMAND_TEXT As String = "INSERT INTO Log (0) VALUES (1)"
End Class
[数据库参数]
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Appender
Imports log4net.Repository.Hierarchy
Public Class DbParameter
Inherits AdoNetAppenderParameter
Private ReadOnly Name As String
Public Sub New(Name As String, Type As DbType, Size As Integer, Layout As ILayout)
With New RawLayoutConverter
Me.Layout = .ConvertFrom(Layout)
End With
Me.Name = Name.Replace("@", String.Empty)
Me.ParameterName = String.Format("@0", Me.Name)
Me.DbType = Type
Me.Size = Size
End Sub
Public ReadOnly Property DbColumn As String
Get
Return String.Format("[0]", Me.Name)
End Get
End Property
End Class
[DbPatternLayout]
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Appender
Imports log4net.Repository.Hierarchy
Public Class DbPatternLayout
Inherits PatternLayout
Public Sub New(Pattern As String)
Me.ConversionPattern = Pattern
Me.ActivateOptions()
End Sub
End Class
[DbExceptionLayout]
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Appender
Imports log4net.Repository.Hierarchy
Public Class DbExceptionLayout
Inherits ExceptionLayout
Public Sub New()
Me.ActivateOptions()
End Sub
End Class
【讨论】:
【参考方案12】:'Vb.Net 的解决方案
Private Shared EscanerLog As log4net.ILog = log4net.LogManager.GetLogger("Log4Net.Config")
Public Sub New(ByVal sIDSesion As String)
Dim sStream As Stream
Dim JsText As String
Using reader As New StreamReader((GetType(ClsGestorLogsTraza).Assembly).GetManifestResourceStream("Comun.Log4Net.Config"))
JsText = reader.ReadToEnd()
sStream = GenerateStreamFromString(JsText)
log4net.Config.XmlConfigurator.Configure(sStream)
End Using
End Sub
Public Function GenerateStreamFromString(ByVal s As String) As Stream
Dim stream = New MemoryStream()
Dim writer = New StreamWriter(stream)
writer.Write(s)
writer.Flush()
stream.Position = 0
Return stream
End Function
Public Function StreamFromResource(ByVal sFilename As String) As Stream
Dim nAssembly As System.Reflection.Assembly = System.Reflection.Assembly.GetExecutingAssembly()
Dim s As Stream = nAssembly.GetManifestResourceStream(System.Reflection.MethodBase.GetCurrentMethod.DeclaringType, sFilename)
Return s
End Function
【讨论】:
以上是关于如何从头开始以编程方式配置 log4net(无配置)的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式强制 log4net 释放日志文件以便可以访问它?