Как настроить log4net программно с нуля (без настроек)
Это плохая идея, я знаю, но... Я хочу настроить 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 с нуля без файла конфигурации?
12 ответов
Один из способов, которыми я занимался в прошлом, - это включить файл конфигурации в качестве встроенного ресурса и просто использовать log4net.Config.Configure (Stream).
Таким образом, я мог использовать синтаксис конфигурации, с которым я был знаком, и мне не пришлось беспокоиться о развертывании файла.
Вот пример класса, который создает конфигурацию 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");
}
}
}
Более краткое решение:
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 этот объект находится в неопределенном состоянии и не должен использоваться.
Немного опоздал на вечеринку. Но вот минимальный конфиг, который работал для меня.
Образец класса
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
Как говорит Джонатан, использование ресурса является хорошим решением.
Это немного ограничивает то, что содержимое встроенного ресурса будет исправлено во время компиляции. У меня есть компонент ведения журнала, который генерирует XmlDocument с базовой конфигурацией Log4Net, используя переменные, определенные как appSettings (например, имя файла для RollingFileAppender, уровень ведения журнала по умолчанию, может быть имя строки подключения, если вы хотите использовать AdoNetAppender). А потом я звоню log4net.Config.XmlConfigurator.Configure
настроить Log4Net, используя корневой элемент сгенерированного XmlDocument.
Затем администраторы могут настроить "стандартную" конфигурацию, изменив несколько параметров приложения (обычно уровень, имя файла,...) или указав внешний файл конфигурации, чтобы получить больше контроля.
Я не могу сказать в фрагменте кода вопроса, включает ли "и т. Д." Очень важный apndr.ActivateOptions(), который указан в ответе Тодда Стаута. Без ActivateOptions () Appender неактивен и не будет делать ничего, что могло бы объяснить причину сбоя.
Доктор 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
}
}
// Я встроил три файла конфигурации в качестве встроенного ресурса и получаю к ним доступ следующим образом:
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);
}
}
}
}
Я закончил тем, что использовал это:
http://www.mikebevers.be/blog/2010/09/log4net-custom-adonetappender/
После 4 часов возиться с конфигом и становиться все более расстроенным.
Надеюсь, это поможет кому-то.
Решение для 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
Вот пример того, как вы можете создать и использовать AdoNetAdapter
полностью в коде, полностью в отсутствие каких-либо App.config
файл (даже для Common.Logging
). Давай, удали это!
Это дает дополнительное преимущество, заключающееся в устойчивости к обновлениям в соответствии с новыми соглашениями об именах, где имя сборки теперь отражает версию. (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("%date{yyyy-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
[DbParameter]
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
Странно что BasicConfigurator.Configure(apndr)
не работал. В моем случае это сделало свое дело... Но, в любом случае, здесь идет ответ - вы должны были написать hier.Configured = true;
(код C#) после завершения всех настроек.