.net - studio - Cómo configurar log4net mediante programación desde cero(sin configuración)
log4net install (11)
Esta es una mala idea, lo sé, pero ... Quiero configurar log4net programáticamente desde cero sin ningún archivo de configuración. Estoy trabajando en una aplicación de registro simple para que yo y mi equipo la usemos para un grupo de aplicaciones departamentales relativamente pequeñas de las que somos responsables. Quiero que todos se conecten a la misma base de datos. La aplicación de registro es solo una envoltura alrededor de log4net con AdoNetAppender preconfigurado.
Todas las aplicaciones están implementadas con ClickOnce, lo que presenta un pequeño problema con la implementación del archivo de configuración. Si el archivo de configuración fuera parte del proyecto principal, podría establecer sus propiedades para implementar con el ensamblado. Pero es parte de una aplicación vinculada, por lo que no tengo la opción de implementarlo con la aplicación principal. (Si eso no es cierto, alguien por favor hágamelo saber).
Probablemente porque es una mala idea, no parece haber mucho código de muestra disponible para configurar programáticamente log4net desde cero. Esto es lo que tengo hasta ahora.
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...
Después de configurar todos los parámetros para apndr
, al principio intenté esto ...
Dim hier As Hierarchy = DirectCast(LogManager.GetRepository(), Hierarchy)
hier.Root.AddAppender(apndr)
No funcionó. Luego, como un tiro en la oscuridad, intenté con esto en su lugar.
BasicConfigurator.Configure(apndr)
Eso tampoco funcionó. ¿Alguien tiene alguna buena referencia sobre cómo configurar log4net programáticamente desde cero sin ningún archivo de configuración?
Una forma en que hice esto en el pasado es incluir el archivo de configuración como un recurso incrustado, y solo usé log4net.Config.Configure (Stream) .
De esa forma, podría usar la sintaxis de configuración con la que estaba familiarizado, y no tener que preocuparme por implementar un archivo.
Es extraño que BasicConfigurator.Configure(apndr)
no funcionó. En mi caso, hizo su trabajo ... Pero, de todos modos, aquí va la respuesta: deberías haber escrito hier.Configured = true;
(código c #) después de que haya terminado toda la configuración.
Aquí hay una clase de ejemplo que crea la configuración de log4net completamente en el código. Debo mencionar que crear un registrador a través de un método estático generalmente se considera malo, pero en mi contexto, esto es lo que quería. De todos modos, puede dividir el código para satisfacer sus necesidades.
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");
}
}
}
No puedo decir en el fragmento de código de la pregunta si el "''Y así sucesivamente ..." incluye la muy importante apndr.ActivateOptions () que se indica en la respuesta de Todd Stout. Sin ActivateOptions (), el Appender está inactivo y no hará nada que pueda explicar por qué está fallando.
// He incrustado tres archivos de configuración como un recurso incrustado y los tengo acceso de esta manera:
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);
}
}
}
}
Terminé usando esto:
http://www.mikebevers.be/blog/2010/09/log4net-custom-adonetappender/
Después de 4 horas jugando con la configuración y cada vez más frustrado.
Espero que ayude a alguien.
El Dr. Netjes tiene esto para establecer la conexión de cadena programáticamente:
// 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
}
}
Un poco tarde para la fiesta. Pero aquí hay una configuración mínima que funcionó para mí.
Clase de muestra
public class Bar
{
private readonly ILog log = LogManager.GetLogger(typeof(Bar));
public void DoBar() { log.Info("Logged"); }
}
Configuración mínima de trace log4net (dentro de la prueba 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();
}
Imprime en el oyente de seguimiento
Namespace+Bar: Logged
Aquí hay un ejemplo de sopa de AdoNetAdapter
sobre cómo puede crear y usar un AdoNetAdapter
completamente en código, completamente en ausencia de cualquier archivo App.config
(ni siquiera para Common.Logging
). Adelante, elimínalo!
Esto tiene el beneficio adicional de ser resistente contra las actualizaciones bajo las nuevas convenciones de nomenclatura , donde el nombre del ensamblado ahora refleja la versión. ( Common.Logging.Log4Net1213
, etc.)
[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
)
[Principal]
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
Como dice Jonathan , usar un recurso es una buena solución.
Es un poco restrictivo en el sentido de que los contenidos de los recursos incrustados serán corregidos en tiempo de compilación. Tengo un componente de registro que genera un XmlDocument con una configuración básica de Log4Net, usando variables definidas como appSettings (por ejemplo, nombre de archivo para RollingFileAppender, nivel de registro predeterminado, tal vez el nombre de la cadena de conexión si quiere usar AdoNetAppender). Y luego llamo a log4net.Config.XmlConfigurator.Configure
para configurar log4net.Config.XmlConfigurator.Configure
usando el elemento raíz del XmlDocument generado.
Entonces los administradores pueden personalizar la configuración "estándar" modificando algunas configuraciones de aplicaciones (generalmente nivel, nombre de archivo, ...) o pueden especificar un archivo de configuración externo para obtener más control.
Una solución más concisa:
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);
No olvides llamar al método ActivateOptions :
El método ActivateOptions debe invocarse en este objeto una vez que se hayan establecido las propiedades de configuración. Hasta que se llame a ActivateOptions, este objeto está en un estado indefinido y no debe utilizarse.