I am trying to configure Log4Net purely by code, but when I did with a minimal configuration, I was flooded by logging messages from NHibernate and the fluent interface.
So, what I am trying to do is simple. Tell Log4Net to show me only log messages of my single class. I toyed around a little bit, but can't figure it out...
Can anyone help, I think the following code illustrates my idea:
var filter = new log4net.Filter.LoggerMatchFilter();
filter.LoggerToMatch = typeof(DatabaseDirectory).ToString();
filter.AcceptOnMatch = false;
var x = new log4net.Appender.ConsoleAppender();
x.Layout = new log4net.Layout.SimpleLayout();
x.AddFilter(filter);
log4net.Config.BasicConfigurator.Configure(x);
Ok, thanks for your help, but there must be some issue here. But I get closer. I tried the XML configuration, which has much more documentation. And I managed to achieve the desired result using the following XML configuration. There must be some misconfiguration in the "pure code" version above.
The following XML configuration provides the "correct" output, which is not the same than the config in code above. Anybody sees the difference?
<log4net>
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="Examples.FirstProject.Entities.DatabaseDirectory"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%C.%M] %-5p %m%n" />
</layout>
</appender>
I figured it out.. Sometimes, just writing it down opens your eyes...
var filter = new log4net.Filter.LoggerMatchFilter();
filter.LoggerToMatch = typeof(DatabaseDirectory).ToString();
filter.AcceptOnMatch = true;
var filterDeny = new log4net.Filter.DenyAllFilter();
var x = new log4net.Appender.ConsoleAppender();
x.Layout = new log4net.Layout.SimpleLayout();
x.AddFilter(filter);
x.AddFilter(filterDeny);
log4net.Config.BasicConfigurator.Configure(x);
See what was missing :-) The denyALL filter!!
Some more code examples:
public static void AllToConsoleSetup()
{
var x = new log4net.Appender.ConsoleAppender { Layout = new log4net.Layout.SimpleLayout() };
log4net.Config.BasicConfigurator.Configure(x);
SetupDone = true;
}
public static void ShowOnlyLogOf(Type t)
{
var filter = new log4net.Filter.LoggerMatchFilter {LoggerToMatch = t.ToString(), AcceptOnMatch = true};
var filterDeny = new log4net.Filter.DenyAllFilter();
var x = new log4net.Appender.ConsoleAppender {Layout = new log4net.Layout.SimpleLayout()};
x.AddFilter(filter);
x.AddFilter(filterDeny);
log4net.Config.BasicConfigurator.Configure(x);
SetupDone = true;
}
Really UGLY but working (it screws up the highlighting, do not miss the last lines):
public static void DefaultSetup()
{
// AllToConsoleSetup();
XmlConfigurator.Configure(XmlSetup());
// DbConfig();
}
private static Stream XmlSetup()
{
const string x = @" <log4net>
<root>
<level value=""ALL"" />
<appender-ref ref=""AdoNetAppender"">
</appender-ref>
</root>
<appender name=""AdoNetAppender"" type=""log4net.Appender.AdoNetAppender"">
<bufferSize value=""1"" />
<connectionType value=""System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" />
<connectionString value=""data source=Christian-PC\SQLEXPRESS;initial catalog=log4net_2;integrated security=false;persist security info=True;User ID=log4net;Password=XXXX"" />
<commandText value=""INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)"" />
<parameter>
<parameterName value=""@log_date"" />
<dbType value=""DateTime"" />
<layout type=""log4net.Layout.RawTimeStampLayout"" />
</parameter>
<parameter>
<parameterName value=""@thread"" />
<dbType value=""String"" />
<size value=""655"" />
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""%thread"" />
</layout>
</parameter>
<parameter>
<parameterName value=""@log_level"" />
<dbType value=""String"" />
<size value=""50"" />
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""%level"" />
</layout>
</parameter>
<parameter>
<parameterName value=""@logger"" />
<dbType value=""String"" />
<size value=""655"" />
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""%logger"" />
</layout>
</parameter>
<parameter>
<parameterName value=""@message"" />
<dbType value=""String"" />
<size value=""4000"" />
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""%message"" />
</layout>
</parameter>
<parameter>
<parameterName value=""@exception"" />
<dbType value=""String"" />
<size value=""2000"" />
<layout type=""log4net.Layout.ExceptionLayout"" />
</parameter>
<filter type=""log4net.Filter.LoggerMatchFilter"">
<param name=""LoggerToMatch"" value=""Ruppert"" />
</filter>
<filter type=""log4net.Filter.DenyAllFilter"">
</filter>
</appender>
</log4net>";
return new MemoryStream(ASCIIEncoding.Default.GetBytes(x));
}
Here is anohter way to configure log4net with XML via code using XmlDocument to load the xml. The difference from Christian's example is that I am using the XmlConfigurator.Configure
overload that takes an XmlElement
as a parameter. I also used single tick marks rather than doubling up the double quotation marks. All in all, I think that it is the tiniest bit cleaner.
string xml =
@"<log4net>
<appender name='file1' type='log4net.Appender.RollingFileAppender'>
<!-- Log file locaation -->
<param name='File' value='log4net.log'/>
<param name='AppendToFile' value='true'/>
<!-- Maximum size of a log file -->
<maximumFileSize value='2KB'/>
<!--Maximum number of log file -->
<maxSizeRollBackups value='8'/>
<!--Set rolling style of log file -->
<param name='RollingStyle' value='Composite'/>
<param name='StaticLogFileName' value='false'/>
<param name='DatePattern' value='.yyyy-MM-dd.lo\g'/>
<layout type='log4net.Layout.PatternLayout'>
<param name='ConversionPattern' value='%d [%t] %-5p %m%n'/>
</layout>
</appender>
<!-- Appender layout fix to view in console-->
<appender name='console' type='log4net.Appender.ConsoleAppender'>
<layout type='log4net.Layout.PatternLayout'>
<param name='Header' value='[Header]\r\n'/>
<param name='Footer' value='[Footer]\r\n'/>
<param name='ConversionPattern' value='%d [%t] %-5p %m%n'/>
</layout>
</appender>
<appender name='debug' type='log4net.Appender.DebugAppender'>
<layout type='log4net.Layout.PatternLayout'>
<param name='ConversionPattern' value='%d [%t] %logger %-5p %m%n'/>
</layout>
</appender>
<root>
<level value='INFO'/>
<!--
Log level priority in descending order:
FATAL = 1 show log -> FATAL
ERROR = 2 show log -> FATAL ERROR
WARN = 3 show log -> FATAL ERROR WARN
INFO = 4 show log -> FATAL ERROR WARN INFO
DEBUG = 5 show log -> FATAL ERROR WARN INFO DEBUG
-->
<!-- To write log in file -->
<appender-ref ref='debug'/>
<appender-ref ref='file1'/>
</root>
</log4net>";
//
// Use XmlDocument to load the xml string then pass the DocumentElement to
// XmlConfigurator.Configure.
//
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
log4net.Config.XmlConfigurator.Configure(doc.DocumentElement);
The following codes works on how to config AdoNetAppender in codes. If you want to add filter, you need to add filter instance on the appender instance in GetAppender() method. For using logger in your application, Dependency Injection is recommended to be used to isolate logger implementation and interface. In addition, use DI scope concept to make it singleton to avoid object recreation.
Note: CrmConfigHelper class is used to get access to application configuration file appSettings section.
The codes samples are shown below
public sealed class SqlLogger:ILogger
{
private ILog _logger;
public SqlLogger()
{
log4net.Config.BasicConfigurator.Configure(GetAppender());
this._logger = log4net.LogManager.GetLogger(CrmConfigHelper.GetString(Constants.LOG4NET_LOGGER_NAME));
}
private log4net.Appender.AdoNetAppender GetAppender()
{
log4net.Appender.AdoNetAppender appender = new log4net.Appender.AdoNetAppender();
appender.ConnectionType = CrmConfigHelper.GetString(Constants.LOG4NET_CONNECTION_TYPE);
appender.ConnectionString = CrmConfigHelper.GetString(Constants.LOG4NET_DB_CONNECTION);
appender.BufferSize = CrmConfigHelper.getInteger(Constants.LOG4NET_BUFFER_SIZE);
appender.CommandText = "INSERT INTO [EventLog] ([Date],[HostName],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @hostname, @log_level, @logger, @message,@exception)";
appender.CommandType = System.Data.CommandType.Text;
appender.AddParameter(new log4net.Appender.AdoNetAppenderParameter() {
ParameterName = "@log_date",
DbType = System.Data.DbType.DateTime,
Size = 100,
Layout = new RawLayoutConverter().ConvertFrom(new PatternLayout("%date{yyyy'-'MM'-'dd HH':'mm':'ss'.'fff}")) as IRawLayout
});
appender.AddParameter(new log4net.Appender.AdoNetAppenderParameter()
{
ParameterName = "@hostname",
DbType = System.Data.DbType.String,
Size = 50,
Layout = new RawLayoutConverter().ConvertFrom(new PatternLayout("%property{log4net:HostName}")) as IRawLayout
});
appender.AddParameter(new log4net.Appender.AdoNetAppenderParameter()
{
ParameterName = "@log_level",
DbType = System.Data.DbType.String,
Size = 50,
Layout = new RawLayoutConverter().ConvertFrom(new PatternLayout("%level")) as IRawLayout
});
appender.AddParameter(new log4net.Appender.AdoNetAppenderParameter()
{
ParameterName = "@logger",
DbType = System.Data.DbType.String,
Size = 50,
Layout = new RawLayoutConverter().ConvertFrom(new PatternLayout("%logger")) as IRawLayout
});
appender.AddParameter(new log4net.Appender.AdoNetAppenderParameter()
{
ParameterName = "@message",
DbType = System.Data.DbType.String,
Size = 4000,
Layout = new RawLayoutConverter().ConvertFrom(new PatternLayout("%message")) as IRawLayout
});
appender.AddParameter(new log4net.Appender.AdoNetAppenderParameter()
{
ParameterName = "@exception",
DbType = System.Data.DbType.String,
Size = 2000,
Layout = new RawLayoutConverter().ConvertFrom(new ExceptionLayout()) as IRawLayout
});
appender.ActivateOptions();
return appender;
}
public void Error(Message context)
{
_logger.Error(context.ToJsonString());
}
public void Error(Message context, Exception exception)
{
_logger.Error(context.ToJsonString(), exception);
}
public void Warn(Message context)
{
_logger.Warn(context.ToJsonString());
}
public void Warn(Message context, Exception exception)
{
_logger.Warn(context.ToJsonString(), exception);
}
public void Info(Message context)
{
_logger.Info(context.ToJsonString());
}
public void Info(Message context, Exception exception)
{
_logger.Info(context.ToJsonString(), exception);
}
}
public interface ILogger
{
void Error(Message context);
void Error(Message context, Exception exception);
void Warn(Message context);
void Warn(Message context, Exception exception);
void Info(Message context);
void Info(Message context, Exception exception);
}
public sealed class Message
{
public string RunDate { get; set; }
public string RunBy { get; set; }
public string Message { get; set; }
public string ToJsonString()
{
return new JavaScriptSerializer().Serialize(this);
}
}