log4net not logging when using external config file - c#

I have found other posts with similar problems, but thus far I haven't been able to solve my particular case.
If I put the log4net configuration in the app.config then all works fine, but I want it in a separate file.
I have looked at the following links, and have tried to implement some of the proposed solutions (but perhaps I didn't understand it correctly):
Similar SO question, but with web scenario
Apache documentation, 'Reading Files Directly' section
Tim Corey's log4net tutorial
So here is my Log4NetSettings.config file's content (wrapped in a configuration element):
<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" />
<connectionString value="" />
<commandText value="INSERT INTO [Calculation Engine 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 change the connectionstring value during runtime.
Here is what I have in the app.config:
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<appSettings>
<add key="log4net.Config" value="Log4NetSettings.config" />
</appSettings>
<log4net configSource="Log4NetSettings.config" />
In the executable class I have this:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Log4NetSettings.config", Watch = true)]
and then as soon as I can get hold of the connectionstring and configure it to the AdoNetAppender, I call:
log.Info("Testing");
I also have:
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
in the class with the assembly attribute and first logging call.
The Log4NetSettings.config file has its Build Action set to Content and its 'Copy to Output Directory' set to 'Copy Always'.
I have been pulling my hair out, and I don't know what I am doing wrong.
Should I have Log4NetSettings.config as just a normal xml file?
Should I put the assembly attribute in the AssemblyInfo.cs file?
Should I rather call:
XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(AppDomain.CurrentDomain.BaseDirectory + "Log4NetSettings.config"));
than have the assembly attribute?
It feels like I've tried so many different things, that perhaps I have mixed them up and gotten the wrong combination.

Adding debug = "true" to the <log4net> tag (i.e. <log4net debug="true"> ) gives a lot of helpful information which may help to resolve.

Yes, you should have the log4net config in an XML file. When you have log4net in a separate file, it's no longer related to the app.config so the configuration section is not required, and the config should not be wrapped in a configuration element. (It isn't necessary to have the assembly attribute in the AssemblyInfo.cs, and I note you have initialised the logging system in your startup code, which is required when using the assembly attribute technique)
Thus your log4net file will look like this:
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<!-- config .. -->
If you still have problems, log4net has diagnostic and debugging capability, see this question for how to enable it.
(Also, if you're using log4net 1.2.11 or later, you can set the connection string name in the log4net config, assuming it is in your app.config)

Related

log4net troubleshooting with Database in C# ConsoleApp

I am new to logging in .Net and was trying to learn log4net as a logging tool. I am running into a problem which is
I have created a log4net config file and trying to add values of logs into SSMS but values are not populating as expected. Here is my log4net config file
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %5level %logger.%method [%line] - %message%newline" />
</layout>
</appender>
<appender name="DatabaseAppender" type="ToolsCommon.logging.AdoNetAppender">
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="Startup" />
<acceptOnMatch value="false" />
</filter>
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />0
<connectionString value="dynamicly created" />
<commandText value="INSERT INTO Log ([EntryID],[Message],[TimeStamp],[SeverityId]) VALUES (#entryid, #message, #log_date, #severityId)" />
<parameter>
<parameterName value="#entryid" />
<dbType value="String" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%entryid" />
</layout>
</parameter>
<parameter>
<parameterName value="#log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="#message" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</parameter>
<parameter>
<parameterName value="#severityId" />
<dbType value="Int64" />
<layout type="log4net.Layout.PatternLayout">
<converter>
<name value="severityId" />
<type value="ToolsCommon.logging.QCSeverityConverter" />
</converter>
<conversionPattern value="%severityId" />
</layout>
</parameter>
</appender>
<appender name="FileAppender" type="ToolsCommon.logging.CustomFileAppender">
<file value="%property{logFilePath}/ToolLog.log" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %5level %logger.%method [%line] - %message%newline" />
</layout>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="DatabaseAppender" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
Issue with above code is EntryID is not getting populated as expected rather it gets populated with some other value which I have no idea where that value is coming from.
Is there any way I can debug this and find out how EntryID value is populated?
I tried debugging it but since this is a config file I cannot debug it using Visual Studios debugger. I think this is getting invoked as and when logger is called but I am not sure where EntryId value is coming from? I am expecting any clue so I can figure out how EntryId is getting populated.
You can turn on internal debugging with:
<configuration>
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
</configuration>
see here for more info.
Please edit your configuration file according to this format:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,
log4net-net-1.0" />
</configSections>
<log4net>
..
</log4net>
</configuration>

Log4net with two different instances on same database

I set up logging with log4net a while back and just didn't think of a particular scenario.
At present I have a global config file. There is a RollingFileAppender and an AdonetAppender.
When only one instance of the program is running on the database then all is fine.
Sometimes, two slightly different instances may be run on the same database. The appenders log the messages from both instances, but often (in the communal code) it is not possible to tell from which instance the message originated. Of course, I could add the instance name to the message or whatever, but it seems like a last resort.
My brain is like a fog. I think the solution should be rather simple, but I just can't think what would be best.
Could I create another appender, and "switch" during runtime which appender is being used based on the instance name?
Here is the config file:
<?xml version="1.0" encoding="utf-8" ?>
<log4net debug="true">
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<file value="D:\Medupi logs\CalcEngineLog.txt"/>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="2"/>
<maximumFileSize value="4MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger %location - %message%newline%exception"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG"/>
<levelMax value="FATAL"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<!-- <threshold value="Warn" /> -->
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="" />
<commandText value="INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception]) VALUES (#log_date, #thread, #log_level, #logger, #location, #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="#location" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%location" />
</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"/>
<appender-ref ref="RollingFileAppender"/>
<appender-ref ref="DebugAppender"/>
</root>
</log4net>
This is how it is configured in the start-up project:
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static void Main()
{
if (!log4net.LogManager.GetRepository().Configured)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\\Program Files\\Symplexity\\bin\\Log4NetSettingsGlobal.xml"));
}
...
}
Additional information
In production, a Windows Service is started and this checks a config file for which instances must be run on a cluster.
The individual instances each have a separate process:
foreach (var cluster in clusters)
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
CreateNoWindow = true,
FileName = processFileName,
WindowStyle = ProcessWindowStyle.Hidden,
Arguments = string.Format("\"{0}\" \"{1}\"", cluster.Name, _ConfigPath)
};
_ClusterProcesses.Add(Process.Start(startInfo));
}
I assume I could add the appIdentifier (as per comments) to the Arguments of the startInfo?
Start by identifying the 2 application instances.
The easiest way to do so is to start each application instance with a unique commandline argument.
Start the first instance via eg. a .cmd file as MyApplication.exe instance1 and the second one as MyApplication.exe instance2.
Read this argument value from the args parameter of method Main.
static void Main(String[] args)
{
String appIdentifier = args[0];
// ...
}
Now you have multiple options.
Option 1: a shared Log4net configuration for both instances
Assign the appIdentifier to a Log4net context property.
The scope to use depends on your scenario, see the Log4net documentation.
log4net.GlobalContext.Properties["appIdentifier"] = appIdentifier;
RollingFileAppender
Include this context property in the layout of the RollingFileAppender via %property{appIdentifier}.
Each logged line will now contain the appropriate appIdentifier value 'instance1' or 'instance2'.
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="... %property{appIdentifier} ..."/>
</layout>
AdoNetAppender
First make sure that the table you are logging to contains a column to store the appIdentifier value,
eg. column appIdentifier VARCHAR(25).
Extend the configuration of the AdoNetAppender with an extra parameter to match the appIdentifier property.
<parameter>
<parameterName value="#appIdentifier"/>
<dbType value="String" />
<size value="25" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{appIdentifier}" />
</layout>
</parameter>
Extend the SQL INSERT statement with the corresponding column name and parameter.
INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception],[appIdentifier])
VALUES (#log_date, #thread, #log_level, #logger, #location, #message, #exception, #appIdentifier)
Option 2: a separate Log4net configuration file for each instance
This is not a pretty as option 1 ... but still an option.
Make a separate xml configuration file for each instance by matching the file name with the appIdentifier commandline argument.
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\\Program Files\\Symplexity\\bin\\" + appIdentifier + '.xml'));
RollingFileAppender
Configure a separate output file path for each instance.
For instance 1
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
...
<file value="D:\Medupi logs\Instance1.txt"/>
...
</appender>
For instance 2
<file value="D:\Medupi logs\Instance2.txt"/>
AdoNetAppender
See option 1 about how to set up a column to store the appIdentifier.
Include a constant value as identifier in the SQL INSERT statement.
INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception],[appIdentifier])
VALUES (#log_date, #thread, #log_level, #logger, #location, #message, #exception, 'Instance1')
Do the same in the xml configuration file for instance2 with value 'Instance2'.
Option 3: a single named logger
If you are only using a single ILog instance throughout your application, retrieve it by name using the value of the appIdentifier.
The %logger marker of the RollingFileAppender and #logger marker of the AdoNetAppender will contain the value 'Instance1' or 'Instance2' respectively.
I am not a fan of using a single logger, but I often see it being practiced this way.
private static log4net.ILog log;
static void Main(String args)
{
String appIdentifier = args[0];
if (!log4net.LogManager.GetRepository().Configured)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\\Program Files\\Symplexity\\bin\\Log4NetSettingsGlobal.xml"));
log = log4net.LogManager.GetLogger(appIdentifier);
}
// ...
}
Option 4: a parametrized file name for the RollingFileAppender
This is an alternative option for the RollingFileAppender only, combine it with one of the options above for the AdoNetAppender.
Set the Log4net context property as explained in option 1.
It is possible to include such Log4net context properties in the path to the output file of the RollingFileAppender.
Doing so will generate a file 'Instance1.log' or 'Instance2.log' respectively.
(Instead of varying the file name, you can also apply this to the (sub)folder name.)
Not sharing the same output file between multipe application instances running concurrently might be a good idea if you are having or are concerned about locking and/or performance issues.
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
...
<file type="log4net.Util.PatternString" value="D:\Medupi logs\%property{appIdentifier}.log" />
...
</appender>

log4net, separate processes, separate RollingFileAppenders

We have a Windows Service that kicks off another process called ClusterProcess.
I want to get logs from the Windows Service and from the ClusterProcess, and I want those logs to go to the database and to files. For instance, if the database is not accessible, I want to be able to see the logs in the files.
I have an external config file for the Log4Net settings. There are two RollingFileAppenders and two AdoNetAppenders.
There is the default root logger and an additional logger called, ClusterProcessLogger.
The idea is that the Windows Service uses the root logger and logs to one of the AdoNetAppenders and one of the RollingFileAppenders, and that the ClusterProcess uses the ClusterProcessLogger and logs to the other AdoNetAppender and the other RollingFileAppender.
The AdoNetAppenders for both cases work fine. The RollingFileAppenders do not.
So here is the external config file for the log4net configuration:
<?xml version="1.0" encoding="utf-8" ?>
<log4net debug="true">
<appender name="RollingFileAppender"
type="log4net.Appender.RollingFileAppender">
<file value="C:\Program Files\Sym\Logging\CalcEngineLog.txt"/>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="5"/>
<maximumFileSize value="10MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger %location -
%message%newline%exception"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG"/>
<levelMax value="FATAL"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="SpecialRollingFileAppender"
type="log4net.Appender.RollingFileAppender">
<file value="C:\Program Files\Sym\Logging\SpecialCalcEngineLog.txt"/>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="5"/>
<maximumFileSize value="10MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger %location -
%message%newline%exception"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG"/>
<levelMax value="FATAL"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<!--<threshold value="Error" />-->
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data,
Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="" />
<commandText value="INSERT INTO [Calculation Engine Log] ([Date],[Thread],
[Level],[Logger],[Location],[Message],[Exception]) VALUES (#log_date,
#thread, #log_level, #logger, #location, #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="#location" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%location" />
</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>
<appender name="ClusterProcessAdoNetAppender"
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="" />
<commandText value="INSERT INTO [ClusterProcess Calculation Engine 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>
<logger name="ClusterProcessLogger" additivity="false">
<level value="Debug" />
<appender-ref ref="ClusterProcessAdoNetAppender" />
<appender-ref ref="SpecialRollingFileAppender" />
</logger>
<root>
<level value="Debug"/>
<appender-ref ref="AdoNetAppender"/>
<appender-ref ref="RollingFileAppender"/>
</root>
</log4net>
Here is the Main method of the Windows Service:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.IO;
using System.Globalization;
using Sym.Core.Culture;
using System.Threading;
namespace Sym.WindowsService
{
static class Program
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static void Main()
{
if (!log4net.LogManager.GetRepository().Configured)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\\Program Files\\Sym\\bin\\Log4NetSettingsGlobal.xml"));
}
log.Debug("Culture info before change: " + Thread.CurrentThread.CurrentCulture.Name);
...
}
}
}
Here is the App.config of the Windows Service:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="ConfigPath" value="C:\Program Files\Sym\Configs\Example.config"/>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<system.diagnostics>
<trace autoflush="true">
<listeners>
<add
name="textWriterTraceListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="C:\Program Files\Sym\Logging\Log4Net_Trace_WindowsServer.txt" />
</listeners>
</trace>
</system.diagnostics>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
For the ClusterProcess project, here is the Main method:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sym.Core.Config;
using Sym.Clustering.Framework;
using System.Diagnostics;
using Sym.Core.Services;
using System.Reflection;
using System.IO;
using log4net.Repository.Hierarchy;
using log4net;
using log4net.Appender;
using System.Configuration;
using System.Collections.Specialized;
using Sym.Core.Log4Net;
using log4net.Config;
namespace Sym.Clustering.ClusterProcess
{
class Program
{
private static ClusterProcessor _ClusterProcessor ;
private static SelectedConfiguration _Config;
private static readonly log4net.ILog log = log4net.LogManager.GetLogger("ClusterProcessLogger");
static void Main(string[] args)
{
try
{
...
try
{
log4net.Config.XmlConfigurator.Configure();
if (!log4net.LogManager.GetRepository().Configured)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\\Program Files\\Sym\\bin\\Log4NetSettingsGlobal.xml"));
}
Log4NetConfiguration.ConfigureLog4Net(_Config); // Load connection strings
}
catch (Exception e)
{
Console.WriteLine(e);
}
log.Debug("ClusterProcess using DatabaseName: " + config.Database.DatabaseName);
...
}
catch(Exception exp)
{
log.Error(exp.Message, exp);
}
}
}
}
Here is the App.config of the ClusterProcess project:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<system.diagnostics>
<trace autoflush="true">
<listeners>
<add
name="textWriterTraceListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="C:\Program Files\Sym\Logging\Log4Net_Trace_ClusterProcess.txt" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
In the log4net trace of the ClusterProcess, these errors are given:
log4net:ERROR Failed to find configuration section 'log4net' in the application's .config file. Check your .config file for the <log4net> and <configSections> elements. The configuration section should look like:
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
log4net:ERROR [RollingFileAppender] ErrorCode: GenericFailure. Unable to acquire lock on file C:\Program Files\Sym\Logging\SpecialCalcEngineLog.txt. The process cannot access the file 'C:\Program Files\Sym\Logging\SpecialCalcEngineLog.txt' because it is being used by another process.
log4net:ERROR [RollingFileAppender] ErrorCode: GenericFailure. Unable to acquire lock on file C:\Program Files\Sym\Logging\CalcEngineLog.txt. The process cannot access the file 'C:\Program Files\Sym\Logging\CalcEngineLog.txt' because it is being used by another process.
I don't know what to do about the first error.
The second and third error shouldn't be there. The ClusterProcess should be able to access the SpecialCalcEngineLog.txt, and it shouldn't be bothered with the CalcEngineLog.txt, as that is the RollingFileAppender that the Windows Service is using.
How do I solve these errors? What am I overlooking?
First error can be mitigated by adding
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
somewhere.
Multiple RollingFileAppenders cannot write to the same file. However, you can distinguish by, for example, adding process id to the file name (and then collect logs from all processes):
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="C:\log\logfile_[%processid].log" />
...
</appender>

Log4Net not writing to SQL Table

Can someone off a 2nd set of eyes? I am clearly missing something.
I'm using Log4Net in a Web API app. Whenever I invoke a Log.Error() it executes, but nothing actually gets written to my dbo.Log table. I am running a local database. When I replaced the configuration with one that wrote to a file, the code worked perfectly.
Here is what my configuration and code looks like.
The Log4Net.config file contains the following:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="100" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="Data Source=MUD-DAD\SQLEXPRESS;Initial Catalog=FitAchiever;Persist Security Info=True;User ID=sa;Password=asdadf334"/>
<commandText value="INSERT INTO dbo.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="DEBUG"/>
<appender-ref ref="AdoNetAppender"/>
</root>
</log4net>
</configuration>
The Global.asax.cs file contains the following, note the call to the Log4Net.Config() method which specifies which config to use.
public class WebApiApplication : System.Web.HttpApplication
{
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(WebApiApplication));
protected void Application_Start()
{
log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(Server.MapPath("Log4Net.config")));
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
The initialization of the logger in my Controller is:
public class FitDashboardController : ApiController
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
IFitDashboardService fitDashboardService = new FitDashboardService();
...
And the call is:
catch (System.Exception ex)
{
log.Error("Error:" + MethodBase.GetCurrentMethod().Name + " :: " + ex.Message);
}
Again, the code works when writing to a file, but not the database. Any ideas?
This question has been answered here. The problem is your <bufferSize> tag, which according to the linked answer...
<bufferSize value="100" />
It is saying that it will keep 100 logs in
memory until written into DB. Maybe that's why you don't see anything
in DB?
Change your code to say <bufferSize value="1" /> and this should cause log4net to write to your database after keeping 1 log in memory. See Apache's documentation on buffersize for more details.
Have you tried running sql server profiler and doing a trace against the database in the connection string? The insert statement log4net submits should show up along with values for the various parameters.

File and Database multiple configuration for log4net

I have Web API that needs to write log data, both to a file and to the database.
Is it possible to configure such a setup? I am trying to understand ILoggerRepository but I am confused.
Can someone give me the some tips on how to configure it and how to use it in application?
It sounds to me like you want to write to two different style appenders at the same time.
The good news is that this is built right into the log4Net library.
What you want to do so configure you configuration file to two different appenders. A file Appender and a Database appender.
Examples of the different appenders can be found here.
http://logging.apache.org/log4net/release/config-examples.html
Here is an example of an web.config that i whipped together. You are going to have to tweak it to get it to work for you, but it will get you on the right start.
One last point, your log4net configuration does not have to be in your web.config but IMHO it just makes things easier to get started. Worry about breaking it out once your web.config starts to get a little cluttered.
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<log4net>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="log-file.txt" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<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=[database server];initial catalog=[database name];integrated security=false;persist security info=True;User ID=[user];Password=[password]" />
<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" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
</configuration>

Categories

Resources