I have two loggers for same database with different level. I would like to have different bufferSize for each logger.
One way is to have two appenders to same database with only difference in bufferSize element, but it's copy-paste.
Is it possible to extend already defined appender and change it's bufferSize property?
For example:
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="20" />
...other elements
</appender>
<appender name="AdoNetAppenderChild" extends="AdoNetAppender">
<bufferSize value="1" />
</appender>
<logger name="Fatal" additivity="false">
<level value="FATAL"/>
<appender-ref ref="AdoNetAppenderChild" />
</logger>
<logger name="Common" additivity="false">
<level value="INFO"/>
<appender-ref ref="AdoNetAppender" />
</logger>
What I want to avoid is having two appenders with same elements and properties and only different value is bufferSize
You can make one appender and use a evaluator to log when you have an error message:
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="ERROR"/>
</evaluator>
The Evaluator is a pluggable object that is used by the
BufferingAppenderSkeleton to determine if a logging event should not
be buffered, but instead written/sent immediately. If the Evaluator
decides that the event is important then the whole contents of the
current buffer will be sent along with the event. Typically an
SmtpAppender will be setup to buffer events before sending as the cost
of sending an email may be relatively high. If an important event
arrives, say an ERROR, we would like this to be delivered immediately
rather than waiting for the buffer to become full. This is where the
Evaluator comes in as it allows us to say: "when an important event
arrives don't worry about buffering, just send over everything you
have right now".
Related
I'm using Log4net ElasticSearchAppender in my C# webAPI with a BufferSize of 10 and Lossy set to true to preserve performance, as seen here :
https://github.com/bruno-garcia/log4net.ElasticSearch/wiki/02-Appender-Settings
<lossy value="false"/>Log4net.ElasticSearch uses a buffer to collect
events and then flush them to the Elasticsearch server on a background
thread. Setting this value to true will cause log4net.Elasticsearch to
begin discarding events if the buffer is full and has not been
flushed. This could happen if the Elasticsearch server becomes
unresponsive or goes offline.
I also set the evaluator to ERROR, that will force the flushing of the buffer anyway if an ERROR occurs.
Here's the associated config file :
<?xml version="1.0"?>
<log4net>
<appender name="ElasticSearchAppender" type="log4net.ElasticSearch.ElasticSearchAppender, log4net.ElasticSearch">
<threshold value="ALL" />
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p %c{1}:%L - %m%n" />
</layout>
<connectionString value="Server=my-elasticsearch-server;Index=foobar;Port=80;rolling=true;mode=tcp"/>
<lossy value="true" />
<bufferSize value="10" />
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="ERROR" />
</evaluator>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="ElasticSearchAppender" />
</root>
</log4net>
Here's the behaviour I get :
the flushing triggered by an ERROR (evaluator) is working fine, but INFO or DEBUG messages alone are never flushed to Elastic, even if there are 10, 20, or 100 of them.
The buffer does never flush when full in this configuration, it just keeps discarding DEBUG or INFO logs until an ERROR comes out, even though Elastic is online and perfectly responsive.
Note: I tried setting lossy to false, and the buffer flushes when full. But I'm affraid this would damage my application responsiveness too much.
Am I gettings something wrong?
Is there a better way to log while minimizing performance impact?
After testing the behaviour, here's what I found :
The buffer becoming full does never trigger a flushing when lossy is true.
Bruno garcia's article was quite misleading about the Lossy property, especially this sentence :
Setting this value to true will cause (...) to begin discarding events if the buffer is full (...). This could happen if the Elasticsearch server becomes unresponsive or goes offline.
In fact it has nothing to do with the appender/Elastic being unresponsive : in a lossy configuration, only evaluators will trigger the flushing of the buffer :
Level evaluator, will flush if an event of a certain lever occurs (ex: FATAL or ERROR), giving the context of the crash (=last logs occuring before the crash).
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="ERROR"/>
</evaluator>
Time evaluator will flush if a certain time interval has elapsed
<evaluator type="log4net.Core.TimeEvaluator">
<interval value="300"/>
</evaluator>
For my purpose I finally decided to configure a TimeEvaluator with a 5 minutes interval.
This way, as long as there is no more than 200 logs (my buffer size) per 5 minutes, no log will be discarded and the impact on performance is kept low.
I have an issue to diagnose, and to get an understanding of it, I wrote two unit tests - one that gets a Logger that exists in my config, and another that gets logger that doesn't exist.
The first surprise was that even if the logger name doesn't exist, I'm still getting back something (instead of NULL, as I was expecting). And it even has a ConsoleAppender attached to it.... okay.... but where does that come from?
Log4net config:
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="C:\tmp\log" />
... (some other settings) ....
</appender>
<root>
<level value="FATAL"/>
<appender-ref ref="RollingFileAppender" />
</root>
<logger name="DefinedLogger" additivity="false">
<level value="DEBUG" />
<appender-ref ref="MyCustomAppender" />
</logger>
<appender name="MyCustomAppender" type="MyAssembly.CustomAppender, MyAssembly">
.....
</appender>
</log4net>
Next, I was trying to understand how I can check what appenders are configured in each logger. My first attempt using the .Repository kept giving back all three appenders that are configured in my config file - OK, makes sense, since it's all the appenders that are in the repository.... but how can I check for an individual entry what appenders are attached?
[Test]
public void TestGetLoggerWithValidNameGetsLogger() {
// Arrange
string loggerName = "DefinedLogger";
// Act
ILog myCustomLogger = LogManager.GetLogger(loggerName);
// this returns *ALL* appenders in the config - not those attached to this logger....
var appenders = myLogger.Logger.Repository.GetAppenders();
// Assert
Assert.IsNotNull(myCustomLogger, "MyCustomLogger is NULL");
}
Any ideas? How can I check to make sure the proper appender(s) have been attached to my logger here?
I am using Log4Net as the logging mechanism for an application of ours. Our configuration is contained in a config file and in our code we programatically invoke which of our several loggers (mostly using FileAppenders) we want to invoke. Recently I realized that one of our log files was not being populated, and I tracked it down to a string mismatch, between the name in our configuration file and the name we were programatically invoking in our code. Because the LogManager could not find the specified logger the root was returned, which for our config is not setup to meaningfully log anywhere.
My question is, is there a way to setup log4net to allow for using specific loggers, but that will fall back to a general logger if the specified logger is not found?
For example using a configuration file like this
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="ConsoleAppender" />
</root>
<logger name="TestFileLogger">
<level value="ALL" />
<appender-ref ref="TestFileAppender" />
</logger>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p [%x] - %m%n" />
</layout>
</appender>
<appender name="TestFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="TestLog" />
<param name="DatePattern" value=".yyyyMMdd".log"" />
<param name="AppendToFile" value="true" />
</appender>
</log4net>
</configuration>
And invoking the loggers in C# like this
var fileLogger = LogManager.GetLogger("TestFileLogger");
fileLogger.Info("This logs appropriately.");
var bogusLogger = LogManager.GetLogger("Bogus");
bogusLogger.Info("This should go to a general log. Bogus is not a recognized appender");
Is there a good way to log the messages from bogusLogger to some kind of general log file? My initial thought was to create a general file appender that is part of the root node, but this is very noisy as ALL log messages will be routed to this file. Is there a way to ONLY route messages that are not captured in another log file?
You could use LogManager.Exists("Bogus") to determine if the bogus-logger or your default implementation should be used.
You could create an extensionmethod on LogManager that could handle it for you.
Is there a way to use more than one ADONetAppender in the same application. Currently I have one ado appender logging to the "Log" table. I would like to add another ADONetAppender to log to another table in the same application. Searching the google did not return much help.
Please let me know.
thanks
Yes, in my Blog post here: http://weblogs.asp.net/stevewellens/archive/2012/01/22/log4net-log-to-a-javascript-console.aspx I use three appenders.
Here is where they get listed:
<logger name="MyLogger">
<level value="ALL" />
<appender-ref ref="LogFileAppender" />
<appender-ref ref="TraceAppender" />
<appender-ref ref="JSConsoleAppender" />
</logger>
There's more but I'm not going to duplicate the whole post here.
Below is the code to use multiple ADO Appender
Just copy your ado appender and paste it again in your config file with below changes:
<appender name="CustomAppender" type="log4net.Appender.ADONetAppender">
Name of the appender should be different for Both the appender.
Then add into root tag
<root>
<appender-ref ref="ADONetAppender"/>
<appender-ref ref="CustomAppender"/>
I am using log4net and in one class require logging to a RollingFile appender, but then in another class, I wish to log to the event log + rolling file + console appender.
What is the best practice? and could I see some sample code?
By the way to make things more difficult, I am using Castle Windsor Logging Facility with Log4net to resolve my Logger instance.
If it helps, I was thinking this below, but have no idea if this is best practice, or how to activate a particular logger based on 'name' still utilising my current logger instance from windsor:
log4net.config:
...
<logger name="EventLogOnly">
<level value="ALL" />
<appender-ref ref="EventLogAppender" />
</logger>
<logger name="ConsoleEventLog">
<level value="ALL" />
<appender-ref ref="ColoredConsoleAppender" />
<appender-ref ref="EventLogAppender" />
</logger>
...
castle windsor container builder class:
container.AddFacility("logging.facility",
new LoggingFacility(LoggerImplementation.Log4net, "log4net.config"));
class in which to log:
private ILogger Logger;
public Test(ILogger logger) {
Logger.Info("Can I log under event log only?");
Logger.Info("Now can I log under both?");
}
Thanks guys.
You can do this by applying a filter to an appender. Only if the log event passes the filter does the event get logged by that appender.
This filter configuration will log only those events coming from the logger named "MyLogger":
<appender name="EventLogAppender" ...
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="MyLogger" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
...and this one will match log messages with certain contained text:
<filter type="log4net.Filter.StringMatchFilter">
<stringToMatch value="database" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
There's a good bit of configuration possible with filters. See the log4net SDK, or the Filters section of the manual, for more details.