log4net custom properties not working - log4net-configuration

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.

Related

Log4net won't Register in database using AdoNetAppender MVC

I'm new in log4net logger and I'm trying to create logger using AdoNetAppender to save the logs to a database.
When I use the Simple Layout
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
and for saving to the database:
<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="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread" />
</layout>
</parameter>
It successfully saves to the database; the issue is that i need to register more details (location, method, line) but it fail to save that in the database.
For that I used the following configuration:
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger %location
%method %line - %message %newline" />
</layout>
And for database inserting:
<commandText value="INSERT INTO Log( [Date],[Thread],[Level],[Logger],
[Locaotion],[Method],[Line],[Message],[Exception])
VALUES (#log_date, #thread, #log_level,
#logger,#location #method, #line, #message, #exception)" />
<parameter>
<parameterName value="#location" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%location" />
</layout>
</parameter>
<parameter>
<parameterName value="#method" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%method" />
</layout>
</parameter>
<parameter>
<parameterName value="#line" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%line" />
</layout>
</parameter>
My C# code :
private static ILog log = LogManager.GetLogger(typeof(HomeController));
//private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
How can I save the full error details in the database?
I get the error i was forget a (,) in database inserting code ans i add Application_Error in global
conversionPattern Code:
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%location] [%method] [%line] - %message %newline" />
</layout>
database inserting code :
<commandText value="INSERT INTO Log( [Date],[Thread],[Level],[Logger],[Location],[Method],[Line],[Message],[Exception])
VALUES (#log_date, #thread, #log_level, #logger, #location, #method, #line, #message, #exception)" />
and for global code :
private static readonly ILog log = LogManager.GetLogger(typeof(MvcApplication));
private void Application_Error(Object sender, EventArgs e)
{
var ex = Server.GetLastError().GetBaseException();
log.Error("App_Error", ex);
}

Log4Net Logging Variables

Is it possible for log4net to log custom variables, or am I limited to the "pre-configured" variables? e.g.
%appdomain: the friendly name of the appdomain from which the log entry was made
%date: the local datetime when the log entry was made
%exception: a formatted form of the exception object in the log entry, if the entry contains an exception; otherwise, this format
expression adds nothing to the log entry
etc
I need to log session information, from ActionExecutedContext for example. if that's relevant I call log4net from a Global Exception filter.
Is it possible, if so, how?
The currently configured appender, using only aforementioned "pre-configured" variables:
<appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
<bufferSize value="1" />
<connectionType value="LALALA DELETED" />
<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="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="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
In log4net a layout can contain many patterns, ie. classes that can output one specific information when encountering their string declaration in the layout.
For example %logger in the layout will resolve to the LoggerPatternConverter class, which outputs the logger name when encountered.
There is a global instantation of all the available patterns in the PatternLayout class:
PatternLayout.s_globalRulesRegistry = new Hashtable(45);
PatternLayout.s_globalRulesRegistry.Add("literal", typeof(LiteralPatternConverter));
PatternLayout.s_globalRulesRegistry.Add("newline", typeof(NewLinePatternConverter));
PatternLayout.s_globalRulesRegistry.Add("n", typeof(NewLinePatternConverter));
PatternLayout.s_globalRulesRegistry.Add("c", typeof(LoggerPatternConverter));
but you can add some custom patterns to your layout through the configuration. Just create your own pattern layout converter:
public class ZoinxPatternlayoutConverter: PatternLayoutConverter
{
protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
{
writer.Write("Zoinx at {0:d}", DateTime.Now);
}
}
And add it to your layout:
<appender name="console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message %zoinx %newline" />
<converter>
<name value="zoinx"/>
<type value="App.ZoinxPatternLayoutConverter, App" />
</converter>
</layout>
</appender>
Of course in your case this may be a bit much and you would perhaps benefit from deriving from a Layout directly if you only ever use one property in each layout, such as the RawTimeStampLayout you have in your config. Alternatively you could inherit the PatternLayout to register all the patterns you're interested in once and for all and use them across your configuration.

Log4net does not log to SQL Server 2008 R2 from VS studio 2010

I am trying to Log to SQL Server using Log4net in a WCF Service.I am using Windows authentication and connection to db is fine.
Below are the config File and code.
The File Log is happening correctly.
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
</configSections>
<log4net debug ="true">
<root>
<level value="ALL" />
<appender-ref ref="AdoNetAppender_SqlServer" />
<appender-ref ref="LogFileAppender" />
</root>
<appender name="AdoNetAppender" 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=V01KUMAVX\\SQLEXPRESS;initial catalog=dbLog;integrated security=SSPI;" />
<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="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="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
</log4net>
</configuration>
and the code :
public class Service1 : IService1
{
private static readonly log4net.ILog log;
static Service1()
{
log4net.Config.XmlConfigurator.Configure();
log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}
public string GetData(int value)
{
log4net.Config.XmlConfigurator.Configure();
log.Debug(string.Format("You entered: {0}", value));
if (!log4net.LogManager.GetRepository().Configured)
{
// log4net not configured
foreach (log4net.Util.LogLog message in log4net.LogManager.GetRepository().ConfigurationMessages)
{
// evaluate configuration message
}
}
return string.Format("You entered: {0}", value);
}
}
How to Log the Log4net Silent errors and how to view them ?
Your Appender names don't match:
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
vs.
<appender-ref ref="AdoNetAppender_SqlServer" />
Change them both to be "AdoNetAppender" or "AdoNetAppender_SqlServer" so that they match (doesn't matter which).

ASP.NET MVC with Castle Logging and log4net woes

In my installer, I have
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<LoggingFacility>(f => f.UseLog4Net("log4net.config"));
}
My log4net config file (which gets deployed to the bin directory of my ASP.NET MVC app) is:
<log4net>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="100" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="data source=GCO06773\SQLEXPRESS;initial catalog=GEMS;integrated security=false;persist security info=True;User ID=<<userid removed>>;Password=<<password removed>>#" />
<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="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="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="AdoNetAppender" />
</root>
</log4net>
I have a controller that's instantiated via Castle.Windsor The relevant code is:
public ILogger Logger { get; set; }
public ActionResult Index()
{
Logger.Info("Info");
Logger.Debug("Debug");
Logger.Error("Error");
Logger.Fatal("Fatal");
Logger.Warn("Warn");
return View();
}
The Logger is a valid castle logger and when I delve into it in the watch window, it appears that there are no appenders associated with the underlying log4net logger, except for the root appender.
According to procmon, the log4net.config file is actually being accessed at some point, so I'm assuming it's being read. I get no errors in the IIS log, no errors in the event log and no exceptions or errors that I can detect. I'm stumped.
UPDATE 1:
Forgot to mention, I added this:
<add key="log4net.Config" value="log4net.config"/>
to the web.config, but it didn't seem to change anything.
UPDATE 2:
I'm using Log4Net 1.2.10.0 with public key token: 1b44e1d426115821 (this matters with Castle Logging which is why I mention it).
The log4net.config file should be in root of your web, not in bin directory.

OAuthWebSecurity.VerifyAuthentication IsSuccessful returns false how do I to determine the reason?

I'm using DotNetOpenAuth with a MVC 4 application. All the sudden Google auth is failing (MS is working). The stock code does this:
[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)
{
var result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (!result.IsSuccessful)
{
return RedirectToAction("ExternalLoginFailure");
}
I know that result.IsSuccessful is false, but how do I get the reason? result.Error is null.
I also looked at this page to use log4net. I do get a log on the local dev box but not when I deploy it to a remote server.
log4net webconfig:
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="RelyingParty.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="100KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date (GMT%date{%z}) [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<!-- Setup the root category, add the appenders and set the default level -->
<root>
<level value="INFO" />
<appender-ref ref="RollingFileAppender" />
</root>
<!-- Specify the level for some specific categories -->
<logger name="DotNetOpenAuth">
<level value="ALL" />
</logger>
</log4net>
Edit I also tried log4net to a sql db but it still didn't log anything
I finally figured this out. The actual error message I was getting:
Protocol error: This message has a timestamp of 10/23/2012 12:19:33 PM,
which is beyond the allowable clock skew for in the future.
Turns out after a server crash the timezone didn't get set correctly. This caused the validation/token to fail from google's end.
The AdoNetAppender was "not working" because bufferSize was set to 100. After setting it to 1 it started getting logs. Here is the adonetappender I'm using
<log4net>
<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" />
<connectionStringName value="CONNECTIONSTRINGNAME" />
<commandText value="INSERT INTO Log4Net ([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="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="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
<root>
<level value="Debug" />
<appender-ref ref="AdoNetAppender" />
</root>
<logger name="DotNetOpenAuth">
<level value="ALL" />
</logger>
</log4net>
You should set the bufferSize back to 100 when you get your issue worked out.