Log4Net Rolling File Appender Rolling issue - log4net

I have the following log4net config:
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="Logs/%date{yyyy-MM-dd} Service.log" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<rollingStyle value="Date"/>
<datePattern value="yyyy-MM-dd"/>
<maxSizeRollBackups value="100"/>
<maximumFileSize value="15MB"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level %logger: %message%newline" />
</layout>
</appender>
Data will be logged constantly to the log file and rolls OK but I end up having rolled files like this:
2009-12-21 Service.log2009-12-22 (this is what it will write tonight)
2009-12-21 Service.log <-- this being the latest file
2009-12-21 Service.log2009-12-21 <-- last updated 23:59
I want the files to be like:
2009-12-21 Service.log
2009-12-22 Service.log
2009-12-23 Service.log

Get rid of the file name and the type in the file element:
<file value="Logs\" />
Then change your datePattern to (note: make sure you escape the letters in 'Service' appropriately, like the g in 'log' is a special format, so you need to escape it with the '\'):
<datePattern value="yyyy-MM-dd Service.lo\g"/>

I think the following should be what you need.
Add in the following element within your <appender />.
<staticLogFileName value="true" />

You can use the function below.
In this function first get file location that you set in webconfig and after that you can add any path that you want! (like Date, our Customer, or...)
WebConfig:
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="C:\\t4\\"/>
<appendToFile value="true"/>
<rollingStyle value="Composite"/>
<datePattern value="_yyyy-MM-dd.lo'g'"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="1MB"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date User:%identity IP:%X{addr} Browser: %X{browser} Url: %X{url} [%thread] %-5level %c:%m%n"/>
</layout>
</appender>
Function:
public static void ChangeFileLocation(string _CustomerName,string _Project)
{
XmlConfigurator.Configure();
log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository();
foreach (IAppender a in h.Root.Appenders)
{
if (a is FileAppender)
{
FileAppender fa = (FileAppender)a;
string sNowDate= DateTime.Now.ToLongDateString();
// Programmatically set this to the desired location here
string FileLocationinWebConfig = fa.File;
string logFileLocation = FileLocationinWebConfig + _Project + "\\" + _CustomerName + "\\" + sNowDate + ".log";
fa.File = logFileLocation;
fa.ActivateOptions();
break;
}
}
}
and result like this: C:\Logs\TestProject\Customer1\Saturday, August 31, 2013.log

Related

Log4net custom json message in Console/FileAppender

I need to log with log4net into multiple targets (loggly, console, file). For loggly I need to log in JSON for some metadata to find logs. I am altering the messageobject for this by adding the metadata to the dynamic messageobject like this (example subject).
public void Info(string message)
{
log.Info(GetLogObject(message));
}
private object GetLogObject(string message, Exception ex = null)
{
dynamic obj = new ExpandoObject();
obj.message = message;
obj.exception = ex;
obj.subject = _configuration.SubjectId;
return obj;
}
app.config
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="LogglyAppender" />
</root>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date - %message%newline" />
</layout>
</appender>
<appender name="LogglyAppender" type="log4net.loggly.LogglyAppender, log4net-loggly">
<rootUrl value="https://logs-01.loggly.com/" />
<inputKey value="*" />
<tag value="AppTag" />
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="ERROR" />
<levelMax value="FATAL" />
</filter>
</appender>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Logs/rolling.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level - %message%newline" />
</layout>
</appender>
Now the Console and FileAppenders are logging JSON as this:
2017-06-12 15:28:10,236 INFO - {[message, Will collect every 1 Hours, 0 Minutes and 0 Seconds], [exception, ], [subject, 11111111-1111-1111-1111-111111111111]}
2017-06-12 15:28:10,271 INFO - {[message, Collection Round started at 12.06.2017 13:28:10], [exception, ], [subject, 11111111-1111-1111-1111-111111111111]}
I want these appenders just to log the message itself (obj.message) to look like this:
2017-06-12 15:28:10,236 INFO - Will collect every 1 Hours, 0 Minutes and 0 Seconds
2017-06-12 15:28:10,271 INFO - Collection Round started at 12.06.2017 11:17:29
How can I do this?
Thank you very much :)
What would happen if you created your own type for the message object (instead of using a dynamic object), and override the ToString() method? I suspect, LogglyAppender would render the graph while the regular PatternLayout would resort to ToString(). I haven't tried this, just a hint. You could perhaps subclass the ExpandoObject not to loose that dynamicity.

Passing log4net ConfigurationFile to a new AppDomain

What I need to do is get hold of the the :
<param name="File" value=".\MyExe.exe.log"/>
value but when I try to access the appender using the following :-
var rootAppender = ((Hierarchy)LogManager.GetRepository())
.Root.Appenders.OfType<RollingFileAppender>()
.FirstOrDefault();
var myFilePath = rootAppender != null ? rootAppender.File : string.Empty;
I find all I get is null. So how do I access my path?
This is my log4net config section :-
<log4net threshold="All">
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value=".\MyExe.exe.log"/>
<param name="AppendToFile" value="true"/>
<param name="ImmediateFlush" value="true"/>
<param name="RollingStyle" value="size"/>
<param name="MaxSizeRollBackups" value="5"/>
<param name="MaximumFileSize" value="1MB"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"/>
</layout>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
</appender>
<logger name="MYLOGGER">
<level value="All"/>
<appender-ref ref="RollingLogFileAppender"/>
</logger>
</log4net>
Thanks
The reason you can't find the file name is that you are querying the root logger - but you don't have a root logger defined in your config, and the default root does not have any appenders linked to it.
You would need to run this code instead which uses your defined logger to get the filename:
var loggerAppender = LogManager.GetLogger("MYLOGGER").Logger.Repository
.GetAppenders()
.OfType<RollingFileAppender>()
.FirstOrDefault();
var myFilePath = loggerAppender != null ? loggerAppender.File : string.Empty;

Log4Net - how to properly use PropertyFilter

Ok, here's the appender:
<appender name="DebugFileAppender" type="log4net.Appender.FileAppender">
<file value="debug.log" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<filter type="log4net.Filter.PropertyFilter">
<Key value="ApplicationName" />
<StringToMatch value="Test Application" />
</filter>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG" />
<levelMax value="DEBUG" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="Date:%date Thread:[%thread] Level:%-5level Logger:%logger - ApplicationName:%property{ApplicationName}; Message:%message%newline" />
</layout>
</appender>
Now, it seems this would check the ThreadContext.Properties["ApplicationName"] string and if it finds 'Test Application' it would log.
Well, it's logging everything, even if ApplicationName="DoNotLog".
Now, I will freely admit this may be due to the way I'm (attempting) to use Log4Net - I need to expose it to COM, so I've wrapped up a singleton Log4Net instance, and call this from COM:
public void LogDebug(LogCodeSource codeSource, LogExecutionSource execSource, object message, Exception exception = null)
{
// Add the thread context properties for appender filtering purposes
log4net.ThreadContext.Properties["ApplicationName"] = codeSource.ApplicationName;
log4net.ThreadContext.Properties["ComponentName"] = codeSource.ComponentName;
log4net.ThreadContext.Properties["MethodName"] = codeSource.MethodName;
log4net.ThreadContext.Properties["ClientMachineName"] = execSource.ClientMachineName;
log4net.ThreadContext.Properties["ServerMachineName"] = execSource.ServerMachineName;
log4net.ThreadContext.Properties["UserName"] = execSource.UserName;
if (exception == null)
{
LoggerContext.GetInstance.MainLogger.Debug(message);
}
else
{
LoggerContext.GetInstance.MainLogger.Debug(message, exception);
}
}
In my tests, if I call:
var logger = new MyLogger();
logger.LogDebug(new LogCodeSource { ApplicationName = "Test Application", ComponentName = "Test component", MethodName = "Test method" }, new LogExecutionSource { ClientMachineName = "Test client", ServerMachineName = "Test server", UserName = "Test user" }, "Test message");
logger.LogDebug(new LogCodeSource { ApplicationName = "DoNotLog", ComponentName = "Test component", MethodName = "Test method" }, new LogExecutionSource { ClientMachineName = "Test client", ServerMachineName = "Test server", UserName = "Test user" }, "Test message");
In root, I've tried:
<root>
<appender-ref ref="DebugFileAppender" />
</root>
As well as:
<logger name="MyLogger">
<level value="DEBUG" />
<appender-ref ref="DebugFileAppender" />
</logger>
Both logs are showing up in my debug.log file.
Any idea why the property filter isn't preventing DoNotLog from going to debug.log?
Here's what I'm seeing in the log:
Date:2015-12-31 11:11:00,928 Thread:[14] Level:DEBUG Logger:MyLogger - ApplicationName:Test Application; Message:Test message
Date:2015-12-31 11:11:00,942 Thread:[14] Level:DEBUG Logger:MyLogger - ApplicationName:DoNotLog; Message:Test message
What you are after is an AND filter since you want to log all message where ApplicationName matches a particular name AND the level is DEBUG. Unfortunately log4net doesn't ship AND filters out of the box however this SO answer details how to implement one.
Property filter are also available when using Microsoft.Extensions.Logging.Abstraction instead if log4Net directly.
Example:
using (_logger.BeginScope(new[] { new KeyValuePair<string, object>("YourPropertyKey", "YourPropertyValue") }))
{
_logger.LogDebug("My special log with context property set");
}
Then in the config an appender has to be setup for the property and key.
Note there is rexexToMatch in oder to stringToMacth if you want to allow multiple values to a key.
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="property_logger" />
</root>
<appender name="property_logger" type="log4net.Appender.RollingFileAppender">
<file value="logs/my_service.log" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<datePattern value="-yyyyMMdd" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="5MB" />
<preserveLogFileNameExtension value="true" />
<staticLogFileName value="false" />
<filter type="log4net.Filter.PropertyFilter">
<Key value="YourPropertyKey" />
<StringToMatch value="YourPropertyValue" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%2thread] %-5level %.50logger - %message%newline" />
</layout>
</appender>
</log4net>

Can the log file path be changed on the fly (reguarly)

I've been experimenting with log4net as it appears that it does not support a particular logging feature my project needs. In short, I want to be able to control the log file path in code. This path will change constantly. The specific use case is a set of file system watchers, and a separate log file is required per instance.
Can this be done?
I want to be able to specify a variable that controls the logging destination in code.
For example:
var log4NetLogger1 = new Log4NetLogger("LogFileAppender1");
log4NetLogger1.InformationEvent("Log message 1");
var log4NetLogger2 = new Log4NetLogger("LogFileAppender2");
log4NetLogger2.InformationEvent("Log message 2");
In the above example, I am passing a string to the log4net wrapper, that matches an appender name in configuration. The idea is that the log4net wrapper changes the logging target by modifying the appender in use via GetLogger.
_log = log4net.LogManager.GetLogger(appenderName);
The result is that two different log files are created, but the log message is written to both. It appears that some aspect of the log4net configuration is global, and I am not able to change log path on the fly in this way.
Configuration file:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
</configSections>
<log4net>
<root>
<appender-ref ref="LogFileAppender1"/>
<appender-ref ref="LogFileAppender2"/>
</root>
<appender name="LogFileAppender1" type="log4net.Appender.RollingFileAppender">
<threshold value="INFO"/>
<param name="File" value="C:\Tmp\EDP_TEST\LOG_DESTINATION\TestLog1.txt"/>
<param name="AppendToFile" value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="10MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n"/>
</layout>
</appender>
<appender name="LogFileAppender2" type="log4net.Appender.RollingFileAppender">
<threshold value="INFO"/>
<param name="File" value="C:\Tmp\EDP_TEST\LOG_DESTINATION\TestLog2.txt"/>
<param name="AppendToFile" value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="10MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n"/>
</layout>
</appender>
</log4net>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
Full wrapper class:
using System;
using log4net;
namespace Log4NetRunner
{
public enum LoggingLevel
{
Information,
Warning,
Error
}
public class Log4NetLogger
{
private readonly ILog _log;
public Log4NetLogger(Type type)
{
if (_log == null)
{
_log = log4net.LogManager.GetLogger(type);
}
}
public Log4NetLogger(string appenderName)
{
if (_log == null)
{
_log = log4net.LogManager.GetLogger(appenderName);
}
}
public void FatalErrorEvent(string messageText)
{
SendLog(messageText, LoggingLevel.Error);
}
public void WarningEvent(string messageText)
{
SendLog(messageText, LoggingLevel.Warning);
}
public void InformationEvent(string messageText)
{
SendLog(messageText, LoggingLevel.Information);
}
private void SendLog(string messageText, LoggingLevel logLevel)
{
ILog logger = _log;
switch (logLevel)
{
case LoggingLevel.Error:
logger.Error(messageText);
break;
case LoggingLevel.Warning:
logger.Warn(messageText);
break;
case LoggingLevel.Information:
logger.Info(messageText);
break;
default:
logger.Error("Unknown Logging level: " + messageText);
break;
}
}
}
}
Yes it can, you just need to use the loggers section of the config.
http://www.beefycode.com/post/Log4Net-Tutorial-pt-5-Using-Logger-Objects.aspx
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
</configSections>
<log4net>
<root>
<!--<appender-ref ref="LogFileAppender1"/>
<appender-ref ref="LogFileAppender2"/>-->
</root>
<logger name="Logger1">
<level value="ALL" />
<appender-ref ref="LogFileAppender1" />
</logger>
<logger name="Logger2">
<level value="ALL" />
<appender-ref ref="LogFileAppender2" />
</logger>
<appender name="LogFileAppender1" type="log4net.Appender.RollingFileAppender">
<threshold value="INFO"/>
<param name="File" value="C:\Tmp\EDP_TEST\LOG_DESTINATION\TestLog1.txt"/>
<param name="AppendToFile" value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="10MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n"/>
</layout>
</appender>
<appender name="LogFileAppender2" type="log4net.Appender.RollingFileAppender">
<threshold value="INFO"/>
<param name="File" value="C:\Tmp\EDP_TEST\LOG_DESTINATION\TestLog2.txt"/>
<param name="AppendToFile" value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="10MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n"/>
</layout>
</appender>
</log4net>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

Is it possible to use Log4Net to write to log file within a CLR Stored Procedure?

Issue: Unable to debug (write) to a Log File using Log4net inside of a CLR Stored Procedure. Possibly a problem with the way I'm building the CLR project? I'm only importing the DLL's into sql server (create assembly....). Do I need to import the App.Config as well?
DLL Name:
CLRTest.dll
Source Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;
using log4net;
using log4net.Config;
public class MyClass
{
private readonly static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string sContextConn = "Context Connection=true";
[Microsoft.SqlServer.Server.SqlProcedure]
public static void Select1()
{
XmlConfigurator.Configure();
log.Debug("Begin Select1()...");
using (SqlConnection connection = new SqlConnection(sContextConn))
{
connection.Open();
SqlCommand command = new SqlCommand("select 1", connection);
SqlDataReader r = command.ExecuteReader();
SqlContext.Pipe.Send(r);
}
log.Debug("End Select1()...");
}
}//end MyClass
Log4Net XML Config (App.Config):
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="C:\log\clrsql.log" />
<param name="AppendToFile" value="true" />
<datePattern value="yyyyMMdd-HHmm" />
<param name="rollingStyle" value="Size" />
<param name="maxSizeRollBackups" value="50" />
<param name="maximumFileSize" value="25MB" />
<param name="staticLogFileName" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [t-%t] [%c.%M(%L)] %m%n" />
</layout>
</appender>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d %-5p [%c.%M(%L)] %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
Permissions on SQL Server:
C:\log\ --
NETWORK SERVICE and MyDomain\sqlserveraccount have Full Control on the "log" folder.
SQL Scripts:
drop procedure clr_Select1
go
drop assembly CLRTest
go
create ASSEMBLY CLRTest FROM 'C:\Share\ClrSql\TEST\CLRTest.dll' WITH PERMISSION_SET = unsafe
go
CREATE PROCEDURE clr_Select1
AS EXTERNAL NAME CLRTest.MyClass.Select1
go
exec clr_Select1
SQL Output:
(No column name)
1
The log4net configurator call that you're making actually tries to find the AppDomain.CurrentDomain.SetupInformation.ConfigurationFile. I don't know what the AppDomain is for a stored procedure.
You might want to try a standalone configuration file and use the overload of that configurator.
log4net.Config.XmlConfigurator.Configure(new FileInfo("config.log4net"));

Resources