Maintaining the last N log entries in the log file (circular logging) - log4net

Does log4net support circular logging? (I think that's the right term.)
I want to maintain the last N entries in a log file. Say N=100. If I have 100 entries and then add a 101st, the desired behavior would be to delete the 1st entry from the top of the file and add the new log entry at the bottom, so that there is always 100 (or fewer) entries, with the oldest entry at the top and the newest at the bottom.
Basically, I want to record a couple of pieces of information to a file every minute. I only am interesting in seeing/keeping the last 100 entries. I could roll my own solution, but I'm already using log4net for logging other issues and was hoping I could use it for this purpose, as well.
Thanks

Out of the box solution would be RollingFileAppender with Size limit. You cannot specify how many entries you want to keep in the file, but if you play with the size (lets say 100KB), you can get what you want.
Here is an example:
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="0" />
<maximumFileSize value="100KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
And some documentation about why setting maxSizeRollBackups is important in this case:
If set to zero, then there will be no backup files and the log file
will be truncated when it reaches MaxFileSize.
MaxSizeRollBackups RollingMode

Related

C# - log4net rollover on date, size, and application relaunch

I'm working on a wpf application that uses log4net. It currently logs to a single file and only rolls over when it grows too large. I am trying to modify this so that it grows over when the file grows too large, when the date changes, or when the application is relaunched.
I am trying to get output as close to the following as possible
App_2017-07-06.0.txt //First launch on 2017-07-06
App_2017-07-06.1.txt //Rollover due to size limit
App_2017-07-06.2.txt //Application relaunch
App_2017-07-06.3.txt //Rollover due to size limit
App_2017-07-07.0.txt //Rollover due to date change
App_2017-07-07.1.txt //Rollover due to size limit
App_2017-07-07.2.txt //Application relaunch - Currently Logging File
From what I understand from the documentation, The rolling style can be set to "compostite" to capture date and size or it can be set to "once" to capture application relaunches. It doesn't seem to have a way to do all 3: http://logging.apache.org/log4net/release/sdk/html/T_log4net_Appender_RollingFileAppender_RollingMode.htm
I got it to the point where everything looks correct and works except the application relaunch overwrites a pre existing file (I imagine because of the appendToFile property). I just cant seem to get it working the way I need to and I can't find any answers in the documentation.
This question seems to be trying to achieve a similar goal, but did not solve my problem:
How do I force a rollover at application startup with Log4net RolloverFileAppender?
Am I missing something? Is it just not possible to do this with log4net?
My current configuration
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="Log\App.txt" />
<appendToFile value="false" />
<rollingStyle value="Composite" />
<maximumFileSize value="10KB" />
<maxSizeRollBackups value="-1" />
<staticLogFileName value="false" />
<preserveLogFileNameExtension value="true" />
<countDirection value="1" />
<lockingModel type="log4net.Appender.RollingFileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level User = %username Class = %property{ClassName} Method = %property{MethodName}%newlineMessage - %message%newline%exception%newline***************************************" />
</layout>
</appender>
My current output
App_2017-07-06.0.txt //First launch on 2017-07-06
App_2017-07-06.1.txt //Rollover due to size limit
App_2017-07-06.1.txt //Application relaunch - overwrites pre-existing file
App_2017-07-06.2.txt //Rollover due to size limit
App_2017-07-07.0.txt //Rollover due to date change
App_2017-07-07.1.txt //Rollover due to size limit
App_2017-07-07.1.txt //Application relaunch - overwrites pre-existing file - Currently logging file
As far as I know log4net copies the log file to xxxx.{number}.txt when it rolls over to the next file. So your first App_2017-07-06.1.txt was never created and is still called App_2017-07-06.txt when the application restarts. You have configured so App_2017-07-06.txt will be overridden and your first App_2017-07-06.1.txt was never created.
You can configure <appendToFile value="true" /> to not override the existing file.

log4net RollingFileAppender, Filename of type Name.Date.ProcessId.log.N

We are using Log4Net in an asp.net service and would like to have the log files to be in the following format:
Name.Date.ProcessId.log.N
e.g. Service.20150123-09.776243.log.0
The RollingFileAppender needs to be of composite style to create new log file if either the date or size constraint is reached.
It's easy enough to have the processid first and then the date. We have that currently, but would prefer to have the date first so its easier to sort first by date.
I've tried various configuration but the nearest I could come up with is
Name.yyyyMMdd-HH.ProcessId.HH.log.N
<appender name="ServiceLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString">
<conversionPattern value="C:\Logs\Service.%date{yyyyMMdd-HH}.%processid" />
</file>
<param name="DatePattern" value=".HH.lo\g" />
<param name="AppendToFile" value="true" />
<param name="RollingStyle" value="Composite" />
<param name="StaticLogFileName" value="false" />
<param name="MaximumFileSize" value="10MB" />
<param name="CountDirection" value="1" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d{MM/dd/yyyy HH:mm:ss fff},%X{Method},%X{TransID},%X{UserID},%X{DeviceID},%X{PID},%X{TH1},%m%n"/>
</layout>
</appender>
I need to have some valid date pattern in the DatePattern for it to work. Can't have just ".lo\g" there.
Adding the %processid in the DatePattern doesn't work.
But it would be ideal that way as we currently have a new log file every hour (because of our current DatePattern yyyyMMdd-HH). Probably variables in DatePattern won't work.
Setting the RollingStyle to "Size" makes above pattern work, but then new log files would be created based on Size and not Size and Date and we need it to be "composite".

log4net creates 2 log files instead of expected one, when log4net is configure both in mine and 3rd party dll

I have 2 projects in my solution, both of class library type.
Actions: Project which contains actions, written using White (UI automation framework over MS UI Automation)
Tests: Project with test fixtures and test methods, using MbUnit
I decided to add logging using log4net for both projects. The log4net configuration I'm using is below:
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString"
value="c:/AutomationLog/Automation_%date{dd.MM.yy_HH.mm.ss}.log" />
<appendToFile value="false" />
<rollingStyle value="Once" />
<maxSizeRollBackups value="-1" />
<maximumFileSize value="10MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5level %date{HH:mm:ss,fff} %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="RollingLogFileAppender" />
</root>
<logger name="root">
<level value="OFF" />
</logger>
</log4net>
I would like that one file is created each run (one per fixture suite run). But two files are created:
Automation_27.01.13_07.33.53.log
Automation_27.01.13_07.33.53.log.1
After investigation I found that log is spitted into 2nd file each time in the same place -- when actions contain types from White is called. Looks like it happens due to White also uses log4net internally.
So, I guess, situation is like this:
I have a class which initializes log4net
I start the fixtures suite
In the tests project there is a class which runs 1st, which contains Log.Info("...")
log file is created
text is appended until..
1st action which references White's types is run from tests project
At this stage a new file is created
I guess it happens because of inside White Dlls there is another call to initialize log4net, it is hard coded inside
Any idea how to prevent log splitting without modifying the code of White (3rd party dlls)?
I have solved the problem by just renaming the config file, white looks for log4net.config name. But I still haven't got the answer - is it possible how to force log4net to be initialized just once, and skip future attempts.

log4net + multiple threads + rolling file appender

I've got this settings for log4net in the log4net.config to allow multiple threads to write to the same file:
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<!-- Minimal locking to allow multiple threads to write to the same file -->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<file value="log\UI.log"/>
<appendToFile value="true"/>
<rollingStyle value="Date"/>
<maxSizeRollBackups value="30"/>
<datePattern value="-yyyyMMdd"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline%date [%thread] %-5level [%property{identity}] %logger{3} - %message%newline"/>
</layout>
</appender>
But after midnight the new created log file is being overwritten all the time and thus there is only the last one event in the file. After server restart it all goes right again till the next midnight.
So can anyone say whether this is a config issue or this is just a log4net issue?
The problem was solved by removing the locking model key since I have only one process (IIS, w3wp.exe) which uses the same logger.
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
Since it was said here:
If you use RollingFileAppender things become even worse as several process may try to start rolling the log file concurrently. RollingFileAppender completely ignores the locking model when rolling files, rolling files is simply not compatible with this scenario.
I think you will get unpredictable results.
My guess is that your use of a - sign on the datePattern is confusing the framework so that after the first roll any log triggers a roll event.
What happens when you try this with
<datePattern value="yyyyMMdd" />
per the example here.
To change the rolling period adjust the DatePattern value. For
example, a date pattern of "yyyyMMdd" will roll every day. See
System.Globalization.DateTimeFormatInfo for a list of available
patterns.

Log4Net + Send Email when done?

I have just started playing around with Log4Net... I now want to send an email with the full log either attached or directly in the mail. The problem with using SmtpAppender is that it requires a bufferSize which will be unknown because it should send the mail whether it's full of errors or just info.
Update: My config file
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="ebb#mail.com" />
<from value="ebb#mail.com" />
<subject value="Backup Application - Log" />
<smtpHost value="mailserver" />
<authentication value="1" />
<username value="userName" />
<password value="mypw" />
<port value ="25"/>
<lossy value="true" />
<bufferSize value="500" />
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="ALL"/>
</evaluator>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%timestamp [%thread] %-5level %logger – %message%newline" />
</layout>
</appender>
BufferSize equals to the number of log messages that have to be buffered (ie if you set to 512 the mail will be sent once 512 messages have been collected).
I believe that setting it to int.MaxValue (which is 2.147.483.647) is a reasonable choice. 2 billions of messages are too much for a system, even long-running.
If you give me 10 minutes I'll confirm you (from source code) that if you clean stop your application, all logs collected so far will be sent
[Update]: confirmed!! Destructor flushes the queue as expected
[Add] I would remove both lossy and evaluator. Your problem is clear: Evaluator has precedence over buffer :) :)
The evaluator is used to flush the queue when a certain condition is met. Your condition equals to true. When this condition is triggered, the email is sent, so this is why the mail is sent at every single log call.
This is slightly different from sending ONLY info and error messages, which is achieved by log filtering.
Remove the two attributes and your code will work. Setting int.MaxValue will allow you to store the maximum possible number of messages. It's unlikely (you'd better win the Superenalotto's 178mln€ jackpot like some guys did tonight, or being hit by a comet in your head) that an application collects more than 2 billion errors/info in a run.

Resources