Log4net writing custom object to sql database using custom appender? - sql-server-2012

Using SQL Server 2012 here is my table:
CREATE TABLE [dbo].[Test]
(
[One] [VARCHAR](50) NOT NULL,
[Two] [VARCHAR](50) NOT NULL
) ON [PRIMARY]
Here is my appender:
<appender name="TestAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="data source=localhost;initial catalog=ApplicationLog;integrated security=false;persist security info=True;User ID=someUser;Password=somePassword" />
<commandText value="INSERT INTO [dbo].[Test] ([One],[Two]) VALUES (#one, #two)" />
<parameter>
<parameterName value="#one"/>
<dbType value="String"/>
<size value="50"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%one"/>
</layout>
</parameter>
<parameter>
<parameterName value="#two"/>
<dbType value="String"/>
<size value="50"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%two"/>
</layout>
</parameter>
</appender>
Here is how I get the instance of the logger in code and try to write to it:
private static readonly ILog TestLogger = LogManager.GetLogger("TestAppender");
TestLogger.Info(new Test {One = "someOne", Two = "someTwo"});
Here is my test class:
public class Test
{
public string One { get; set; }
public string Two { get; set; }
}
After stepping through this, I have a record in my table and the contents of the columns are this:
One: "one" Two: "12wo"
What the heck is "12wo"? I know I'm missing something here. I think my conversion patterns are wrong. I've tried this instead:
<conversionPattern value="%property{one}"/>
..but that doesn't work either. Do I have to write a custom pattern layout or something? Thanks.

This site pointed me in the right direction.
I had to create a custom LayoutPattern and PatternConverter in order to write my object to the log successfully. Turns out the weird "12wo" text I was getting in the database was because the conversion pattern uses printf c style syntax. Anyhow, here's some code.
public class TestLayoutPattern : PatternLayout
{
public TestLayoutPattern()
{
AddConverter(new ConverterInfo
{
Name = "test",
Type = typeof (TestConverter)
});
}
}
public class TestConverter : PatternConverter
{
protected override void Convert(System.IO.TextWriter writer, object state)
{
if (state == null)
{
writer.Write(SystemInfo.NullText);
return;
}
var loggingEvent = state as LoggingEvent;
if (loggingEvent == null)
throw new NullReferenceException("loggingEvent");
var test = loggingEvent.MessageObject as Test;
if (test == null)
{
writer.Write(SystemInfo.NullText);
}
else
{
switch (Option.ToLower())
{
case "one":
writer.Write(test.One);
break;
case "two":
writer.Write(test.Two);
break;
default:
writer.Write(SystemInfo.NullText);
break;
}
}
}
}
Here is how to get an instance of the logger by name:
private static readonly ILog TestLogger = LogManager.GetLogger("TestLogger");
Here is how to write a Test object to the log.
TestLogger.Info(new Test {One = "field one", Two = "field two"});
Here is how a parameter should be defined in the web.config.
<parameter>
<parameterName value="#one" />
<dbType value="String" />
<size value="50" />
<layout type="MyApp.TestLayoutPattern">
<conversionPattern value="%test{one}" />
</layout>
</parameter>
Another thing to note is the root and logger sections of the web.config. In the root section is where the default logger is defined with its level set. I can define my custom TestLogger in a logger section which will reference the appender as shown below. This allows me to access the TestLogger by name as shown above.
<root>
<level value="ALL"/>
<appender-ref ref="ADONetAppender"/>
</root>
<logger additivity="false" name="TestLogger">
<level value="ALL"/>
<appender-ref ref="TestAppender" />
</logger>
I also found if you wanted to just add a few properties to the default ADONetAppender (and add a few fields to the table), you could instead use the log4net.ThreadContext to set those properties like so:
log4net.ThreadContext.Properties["MyCustomPrperty"] = value;
Then in the web.config under the parameter section you can access that property like this:
<parameter>
<parameterName value="#myCustomProperty"/>
<dbType value="String"/>
<layout type="log4net.Layout.RawPropertyLayout">
<key value="MyCustomProperty" />
</layout>
</parameter>

Related

Log4Net SmtpAppender not working in .netCore

I'm trying to send log messages to my gmail account but it keeps not working. I've tried changing ports and changing threshold value but it's not working. When I try writing logs in file everything is ok, so I think the problem is in log4net.config file.
My log4net configuration file contains:
<log4net>
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="mymail#gmail.com" />
<from value="mysendermail#gmail.com" />
<subject value="Crash log" />
<smtpHost value="smtp.gmail.com" />
<authentication value="Basic" />
<port value="465" />
<username value="myusername" />
<password value="mypass" />
<bufferSize value="1" />
<EnableSsl value="true"/>
<lossy value="true" />
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="DEBUG"/>
</evaluator>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5p %d{hh:mm:ss} %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="SmtpAppender"/>
</root>
</log4net>
In my Startup.cs file I have:
var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
And my example code is:
public class HomeController : Controller
{
private static readonly ILog log = LogManager.GetLogger(typeof(HomeController));
public HomeController()
{
}
public IActionResult Index()
{
return View();
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
ViewData["Title"] = "title";
log.Warn("test test", new NullReferenceException("missing"));
log.Debug("test test", new NullReferenceException("missing"));
log.Error("test test", new NullReferenceException("missing"));
log.Fatal("test test", new NullReferenceException("missing"));
return View();
}
}
Am I making something wrong. I tried everything
log4net's SmtpAppender is not supported in .net core yet.
Supported frameworks: https://logging.apache.org/log4net/release/framework-support.html
Have you tested the following port:
Gmail SMTP port (TLS): 587
Gmail SMTP port (SSL): 465 <- your current setting
what-are-the-gmail-smtp-settings

Quartz.Net, logging with Log4Net and Common.Logging throws "unknown log level"

I have an Azure cloud service which uses Quartz.Net (2.3.2) to run scheduled tasks. My main logging engine is Log4Net (2.0.3) , and I am using Common.Logging.Log4Net1213 (3.0.0) to bridge Common.Logging and Log4Net. I am using my own "NinjectJobFactory" to create all the jobs and their dependencies (It implements IJobFactory). My schedule startup code looks like this:
_scheduler = factory.GetScheduler();
_scheduler.JobFactory = new NinjectJobFactory(_kernel);
_scheduler.Start();
Everything works perfectly for normal, everyday logging (Quartz startup and shutdown, NServiceBus startup, tasks starting, exception handling inside jobs, etc). The problem I have is when there is a fatal exception in any of the lines above which prevents Quartz from starting. (Usually, this is because I have failed to properly include or configure a dependency that one of the jobs needs). Under these circumstances, instead of logging the real problem, I get an exception inside Log4NetLogger.cs complaining about an unknown logging level, and the underlying exception is never surfaced or logged. I have to break on caught exceptions in order to see the underlying exception. Can anyone suggest a fix? Thanks in advance!
The stack trace looks like this:
Microsoft.WindowsAzure.ServiceRuntime Critical: 1 : Unhandled
Exception: System.ArgumentOutOfRangeException: unknown log level
Parameter name: logLevel Actual value was Error. at
Common.Logging.Log4Net.Log4NetLogger.GetLevel(LogLevel logLevel) in
c:_oss\common-logging\src\Common.Logging.Log4Net129\Logging\Log4Net\Log4NetLogger.cs:line
180 at Common.Logging.Log4Net.Log4NetLogger.WriteInternal(LogLevel
logLevel, Object message, Exception exception) in
c:_oss\common-logging\src\Common.Logging.Log4Net129\Logging\Log4Net\Log4NetLogger.cs:line
140 at Common.Logging.Factory.AbstractLogger.Error(Object message,
Exception exception) in
c:_oss\common-logging\src\Common.Logging.Portable\Logging\Factory\AbstractLogger.cs:line
806 at Quartz.Simpl.SimpleThreadPool.WorkerThread.Run() in
c:\Program Files
(x86)\Jenkins\workspace\Quartz.NET\src\Quartz\Simpl\SimpleThreadPool.cs:line
492 at
System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state, Boolean
preserveSyncCtx) at
System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state, Boolean
preserveSyncCtx) at
System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state) at
System.Threading.ThreadHelper.ThreadStart()
My common.logging config in the app.config is:
<common>
<logging>
<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net1213">
<arg key="configType" value="FILE" />
<arg key="configFile" value="log4net.config" />
</factoryAdapter>
</logging>
</common>
Finally, my log4net.config is:
<log4net>
<appender name="ErrorAppender" type="log4net.Appender.BufferingForwardingAppender">
<bufferSize value="1" />
<lossy value="true" />
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="INFO" />
</evaluator>
<appender-ref ref="TraceAppender" />
</appender>
<appender name="TraceAppender" type="log4net.Appender.TraceAppender">
<threshold value="INFO" />
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="NServiceBus.Azure.Transports.WindowsAzureServiceBus.AzureServiceBusQueueCreator" />
<acceptOnMatch value="false" />
</filter>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="ErrorAppender" />
</root>
</log4net>
This may sound silly, but I was facing the exact same problem and I overcame it by making sure the versions of common.logging, common.logging.core, and common.logging.log4net1213 were all of the same version (3.3.1). Apparently, there is version dependency between the three libraries, and if one is used with another not the same version, this can result.
Here is the decompiled code for the function from which your exception is originating. It comes from Common.Logging.Log4Net1213 v3.3.1. Previous versions of this code didn't exactly account for Common.Logging.LogLevel.Warn, and so the exception fell out at the bottom of the switch statement:
public static Level GetLevel(Common.Logging.LogLevel logLevel)
{
switch (logLevel)
{
case Common.Logging.LogLevel.All:
return Level.All;
case Common.Logging.LogLevel.Trace:
return Level.Trace;
case Common.Logging.LogLevel.Debug:
return Level.Debug;
case Common.Logging.LogLevel.Info:
return Level.Info;
case Common.Logging.LogLevel.Warn:
return Level.Warn;
case Common.Logging.LogLevel.Error:
return Level.Error;
case Common.Logging.LogLevel.Fatal:
return Level.Fatal;
default:
throw new ArgumentOutOfRangeException("logLevel", (object) logLevel, "unknown log level");
}
}
In my case, common.logging.log4net1213 was one version behind the others (v3.3.0), so I updated the library to the same version as the other two libraries, and the problem evaporated.
Good luck!
Can you show us your log config?
My Servicebus/log4net/quartz implementation logs quartz startup issues successfully. This is the startup code i use; not sure if it will help as not seen your code.
NSB Version 4.6.2
public class MyServer : ServiceControl
{
private readonly ILog logger;
private ISchedulerFactory schedulerFactory;
private IScheduler scheduler;
public static IBus Bus = null;
public MyServer()
{
logger = LogManager.GetLogger(GetType());
}
public virtual void Initialize()
{
try
{
log4net.GlobalContext.Properties["Job"] = "Quartz";
Configure.Serialization.Xml();
Configure.Transactions.Enable();
SetLoggingLibrary.Log4Net();
Bus = Configure.With().DefaultBuilder().UnicastBus().SendOnly();
schedulerFactory = CreateSchedulerFactory();
scheduler = GetScheduler();
}
catch (Exception e)
{
logger.Error("Server initialization failed:" + e.Message, e);
throw;
}
}
}
public class Configuration
{
private static readonly NameValueCollection configuration;
static Configuration()
{
configuration = (NameValueCollection)ConfigurationManager.GetSection("quartz");
}
static public string GetQuartzConfigFileName()
{
return configuration["quartz.plugin.xml.fileNames"] as String;
}
}
And my app config looks like this
<configuration>
<configSections>
<section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
</configSections>
<common>
<logging>
<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4net1211">
<arg key="configType" value="INLINE" />
</factoryAdapter>
</logging>
</common>
<log4net debug="false">
<appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="FATAL" />
<foreColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="ERROR" />
<foreColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="WARN" />
<foreColor value="Yellow" />
</mapping>
<mapping>
<level value="INFO" />
<foreColor value="Green" />
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="GrafOI.Scheduler.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10000KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<!-- quartz clutters the logs so turn it off unless an error occurs -->
<logger name="Quartz">
<level value="ERROR" />
</logger>
<root>
<level value="DEBUG" />
<appender-ref ref="AdoNetAppender" />
<appender-ref ref="EventLogAppender" />
<appender-ref ref="ColoredConsoleAppender" />
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
<quartz>
<add key="quartz.scheduler.instanceName" value="GrafOI Scheduler" />
<add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" />
<add key="quartz.threadPool.threadCount" value="2" />
<add key="quartz.threadPool.threadPriority" value="Normal" />
<add key="quartz.jobStore.misfireThreshold" value="60000" />
<add key="quartz.jobStore.type" value="Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" />
<add key="quartz.jobStore.driverDelegateType" value="Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz" />
<add key="quartz.jobStore.tablePrefix" value="QRTZ_" />
<add key="quartz.jobStore.dataSource" value="myDS" />
<add key="quartz.dataSource.myDS.connectionString" value="server=.;database=GrafOI;Integrated Security=true;" />
<add key="quartz.dataSource.myDS.provider" value="SqlServer-20" />
<add key="quartz.plugin.xml.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz" />
<add key="quartz.plugin.xml.fileNames" value="~/GrafOI.Scheduler.Jobs.xml" />
<add key="quartz.jobStore.useProperties" value="true" />
</quartz>
<appSettings>
</appSettings>
</configuration>
Maybe there is something in there that helps

log4net custom properties not working

I'm trying to implement a custom log4net logger/logmanager so that I can add a new level. I also want to have the schema of the target table (I'm using an AdoNetAppender and SQL Server 2008) to have some optional fields; for instance, the level I'm adding is used to track usage, and one of the fields needs to be a Duration (to tell us how long something took). However, other levels (INFO, DEBUG, FATAL, etc.) will not have any value for Duration. So I need to be able to insert into the table with not all fields having values--preferably without having to reimplement all of the standard levels logging to pass a null value for the irrelevant fields. Ideally, log4net would automatically pass NULL for property values that it cannot find for a particular logging event.
The code in my custom logger is as follows:
public void Usage(TimeSpan? duration, DateTime? startTime, DateTime? endTime, string message)
{
if (IsUsageEnabled)
{
LoggingEvent loggingEvent = new LoggingEvent(GetType(), Logger.Repository, Logger.Name, _currentUsageLevel,
message, null);
if (startTime.HasValue)
{
loggingEvent.Properties["StartTime"] = startTime.Value;
}
else
{
loggingEvent.Properties["StartTime"] = DBNull.Value;
}
if (endTime.HasValue)
{
loggingEvent.Properties["EndTime"] = endTime.Value;
}
else
{
loggingEvent.Properties["EndTime"] = DBNull.Value;
}
if (duration.HasValue)
{
loggingEvent.Properties["Duration"] = duration.Value.TotalMilliseconds;
}
else
{
loggingEvent.Properties["Duration"] = DBNull.Value;
}
Logger.Log(loggingEvent);
}
}
(the assignments to DBNull.Value were added in hopes that that would help log4net pass nulls when the value was not passed, but I hope they can be removed).
The appender config is as follows:
<appender name="SQLAppender" 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="my connection string (which actually gets replaced at runtime with a connection string retrieved from a config database)" />
<commandText value="INSERT INTO MyTable ([Date],[Thread],[Level],[Logger],[Message],[Exception], [Login], [StartTime], [EndTime], [Duration]) VALUES (#log_date, #thread, #log_level, #logger, #message, #exception, #login, #start_time, #end_time, #duration)" />
<parameter>
<parameterName value="#log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="#thread" />
<dbType value="String" />
<size value="255" />
<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="255" />
<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="4000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
<parameter>
<parameterName value="#login" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{CurrentLogin}" />
</layout>
</parameter>
<parameter>
<parameterName value="#start_time" />
<dbType value="DateTime" />
<layout type="log4net.Layout.PatternLayout"
value="%property{StartTime}" />
</parameter>
<parameter>
<parameterName value="#end_time" />
<dbType value="DateTime" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{EndTime}" />
</layout>
</parameter><parameter>
<parameterName value="#duration" />
<dbType value="Double" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Duration}" />
</layout>
</parameter>
</appender>
When I try to log something with my custom level, it works ONLY if I pass in duration, starttime and endtime. For anything else--including default levels--it seems to fail silently, with no clues in the output window (although logging with default levels throws exceptions saying it can't convert String to DateTime or Double).
Any reason that anyone can see why this would fail silently like this?
I finally figured it out, after tracing through some log4net source. The problem was that I was using a PatternLayout for rendering the custom parameters. This layout coerces null to the string "(null)", which causes problems if you're trying to insert it into, say, a nullable datetime field in the table. The solution is simple: instead of using the PatternLayout layout, use log4net.Layout.RawPropertyLayout. This will pass a null property as null, which is what you want.

Can't get NHibernate and Log4Net to generate logging.

I can't get any logging of NHibernate or my application. I tried EVERYTHING that I could think about, but nothing works!
Here is my code:
using System.Reflection;
using NHibernate.Cfg;
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Log4Net.config", Watch = true)]
namespace NHibernate_Log4Net
{
class Program
{
static void Main(string[] args)
{
var cfg = new Configuration()
.Configure()
.AddAssembly(Assembly.GetCallingAssembly());
}
}
}
namespace NHibernate_Log4Net.Model
{
public class Item
{
public int Id { get; set; }
public int Title { get; set; }
public int Alias { get; set; }
}
}
Item.hbm.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernate_Log4Net.Model.Item" assembly="NHibernate_Log4Net" auto-import="false">
<class name="User" table="Users">
<id name="Id">
<generator class="Native"/>
</id>
<property name="Title" length="255" not-null="true" />
<property name="Alias" length="255" not-null="true" />
</class>
</hibernate-mapping>
Log4Net.config file:
<?xml version="1.0" encoding="utf-8"?>
<log4net debug="false">
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log4netLogger.log" />
<appendToFile value="false" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1000KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>
<appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5p %m - %c -%n" />
</layout>
</appender>
<appender name="TraceAppender" type="log4net.Appender.TraceAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date - %message%newline"/>
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="OutputDebugStringAppender" />
<appender-ref ref="TraceAppender" />
</root>
<logger name="NHibernate" additivity="false">
<level value="FATAL"/>
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="OutputDebugStringAppender" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="TraceAppender" />
</logger>
<logger name="NHibernate.SQL" additivity="false">
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="RollingFileAppender" />
</logger>
<!--
NHibernate.Loader.Loader logs diagnostic stuff and SELECTs.
You can use either logger, or both, depending on you needs.
-->
<logger name="NHibernate.Loader.Loader" additivity="false">
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="RollingFileAppender" />
</logger>
</log4net>
NHibernate throws an error that the class Users doesn't exist. This is expected, but why do I not see any logging from this, from NHibernate?
(I can log it myself, but the point that I can't see any log from NHibernate).
The config file needs to match the executable, is your executable named Log4Net.exe?
I have had trouble using the XmlConfigurator attribute in the past. Perhaps you should just call XmlConfigurator explicitly in your main like so:
namespace NHibernate_Log4Net
{
class Program
{
static void Main(string[] args)
{
XmlConfigurator.ConfigureAndWatch(new FileInfo("log4net.config"));
var cfg = new Configuration()
.Configure()
.AddAssembly(Assembly.GetCallingAssembly());
}
}
}
If the logging levels set for the NHibernate loggers are too high (or off), you may not see any log messages from NHibernate. For example, what is the value of
((log4net.Repository.Hierarchy.Logger)LogManager.GetLogger("NHibernate").Logger).Level?
You should have a section in your config file like this:
<log4net>
<appender name="NHLog" type="log4net.Appender.FileAppender">
<file value="logs/nhibernate.log" />
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%t] %-5p %c - %m%n" />
</layout>
</appender>
<logger name="NHibernate" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="NHLog"/>
</logger>
</log4net>
Update: If the log file is always created (when non-existent before a run) but is always empty, this points to an exception during appending. To minimise the possibility of an exception, have a very simple conversionPattern (perhaps just "%m%n") and see if any output is generated. If there is, add back elements of the conversion pattern until you find where the problem is. If no output is generated, log something to the NHibernate logger from your own code (this is perfectly OK) and step through it in the debugger.
ILog log = LogManager.GetLogger("NHibernate");
log.Info("Application starting");
If still no joy, you may have to post some of your code/configuration.
https://web.archive.org/web/20110514164829/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/07/01/how-to-configure-log4net-for-use-with-nhibernate.aspx
I used the above link to set up logging. Try the following
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Log4Net.config", Watch = true)]
namespace NHibernate_Log4Net
{
class Program
{
static void Main(string[] args)
{
log4net.Config.XmlConfigurator.Configure();
var cfg = new Configuration()
.AddAssembly(Assembly.GetCallingAssembly());
.Configure()
}
}
}
The extra is this line
log4net.Config.XmlConfigurator.Configure();
It didnt work for me till I added that. I know this is somewhat similar to what Jeffery suggested, but... just my 2cents.
Also, shouldnt the Configure() call on the Nhibernate configuration object come last? After the Assembly is added?

NHibernate Enabling log4net

I have enabled log4net and run my app which is giving an exception.
But the log file is empty.
Doesn't NHibernate log info about the exception???
Malcolm
You need to configure log4net. Just by adding log4net dll to the project doesn't log anything. You need to create appenders to specify where all the loggin should be directed to. Create a xml file like this one:
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Logs\Trace.log" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<maxSizeRollBackups value="30" />
<maximumFileSize value="1000KB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level - %message%newline" />
</layout>
<threshold value="DEBUG"/>
</appender>
<root>
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
...and configure it when starting up the application:
public static void Main()
{
var logconfig = new System.IO.FileInfo(PATH_TO_LOG_CONFIG);
if(logconfig.Exists)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(logconfig);
}
}