Logging in an application - c#

I built an application and want to use logging in my application before giving it out to a user to know the actions the user did in case before any error. I haven't used logging before, so I did a bit of study to find out good methods for the same and perhaps Trace class in .NET looks like what might help in this scenario.
Using a text file seems to be a better idea here for the logging setting a TextWriterTraceListener in the App.Config file of my project, and using in the code Trace.Writeline("Error info", "Field");
1) Even after reading good amount of content, I am not sure about good approaches for logging? Should there be different error logger and information logging files? How does logging work for specifying different types of error (such as warning, critical, error etc.?)
2) I want to use the same logfile in one of my other projects in the solution. So, do I add the same Trace Listener section in the App.Config of my project?

With a logging library (such as log4net), you can do all the things you are asking about easily. You can set up your logging in a config file to go to event log, file, console, etc (or any combination) and set the logging levels independently.
That is, you can say the file will have informationals, warnings, errors, and fatals, but the event log will log only the errors and fatals.
In general, nearly all of the logging libraries have these features. The examples below are just in terms of log4net but the same concepts apply to most logging libraries...
To get the logger in any class in your solution, you just need to ask for a logger in your class:
public MyClass
{
private static readonly ILog _log = log4net.LogManager.GetLogger(typeof(MyClass));
public void SomeMethod()
{
_log.Debug("This is a debug message.");
_log.Info("This is an informational message.");
_log.Warn("This is a warning message.");
_log.Error("This is an error message.");
_log.Fatal("This is a fatal message.");
}
}
In addition, you can filter based on the loggers, that is, you could suppress log messages from paraticular classes or only display logging messages for a subset of classes, etc.
A sample config settings block would look like this. This creates a rolling file appender (keeps several days worth of logs) and Console appender
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="c:\logs\AgentLog"/>
<appendToFile value="true"/>
<datePattern value=".yyyy-MM-dd"/>
<rollingStyle value="Date"/>
<MaxSizeRollBackups value="14"/>
<param name="StaticLogFileName" value="false"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%thread] %-5level %logger{1} - %m%n"/>
</layout>
</appender>
<appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="FATAL"/>
<foreColor value="Yellow"/>
<backColor value="Red, HighIntensity"/>
</mapping>
<mapping>
<level value="ERROR"/>
<foreColor value="Red, HighIntensity"/>
</mapping>
<mapping>
<level value="WARN"/>
<foreColor value="Yellow, HighIntensity"/>
</mapping>
<mapping>
<level value="INFO"/>
<foreColor value="Green, HighIntensity"/>
</mapping>
<mapping>
<level value="DEBUG"/>
<foreColor value="White"/>
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%thread] %-5level %logger{1} - %m%n"/>
</layout>
</appender>
<root>
<level value="INFO"/>
<appender-ref ref="RollingFileAppender"/>
<appender-ref ref="ColoredConsoleAppender"/>
</root>
</log4net>

Related

Class will not log to a file with log4net

I've got a MVC based C# Windows Forms application. I've got the following configuration in the app.config file:
<log4net>
<root>
<level value="ALL"/>
<appender-ref ref="LogConsoleAppender"/>
<appender-ref ref="RollingLogFileAppender"/>
</root>
<appender name="LogConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="QAC.Source.Services.Log.LogPatternLayout">
<conversionPattern value="%date %level [Thread: #%thread] %message [%logger -> %M]%newline"/>
</layout>
</appender>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="logs/QAC_"/>
<datePattern value="yyyy-MM-dd_HH-mm-ss'.log'"/>
<staticLogFileName value="false"/>
<appendToFile value="false"/>
<rollingStyle value="Date"/>
<maxSizeRollBackups value="50"/>
<maximumFileSize value="10MB"/>
<layout type="QAC.Source.Services.Log.LogPatternLayout">
<conversionPattern value="%date %level [Thread: #%thread] %message [%logger -> %M]%newline"/>
</layout>
</appender>
</log4net>
In the main method, in the first lin, I call private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); and after that I initialize my main service:
mainService = MainFactory.getMainServiceInstance();
And there is the problem. All log outputs in the main method are logged successfully to the file and console, also of other services (underlying services from MainService). The only class which is not logged is the MainService. The curiosity at this is, that the logging for this class starts after some time. I don't use any filter. I've turned on the log4net debugging:
<add key="log4net.Internal.Debug" value="true"/>
, but there is no error at least. So I think, that the initialization goes through. Also if I log before I initialize the MainService, it also logs to console and file. Just this class will not log.
Any help would be very nice. Thank you in advance.
I've found the solution. I am using a custom LogPatternLayout which tries also to initialize the MainService and so the MainService is initialized before the LogPatternLayout could be read and initialized into the Logger. Therefore logging takes place after the initialization of the MainService.

log multiple assemblies using log4net

After lots of issues, finally I managed to configure log4net for my window service.
I am pretty new to it and today I started configuring it. I have these below doubts.
1) I need to use that into multiple assemblies. Say I have an assembly 'A' which is added as reference in assembly 'B' which is mine main assembly where I have reference of log4net.I need to log both assembly 'A' and 'B'.
2) My application is multi-threaded and use lots of thread. So is log4net is thread safe?
3) I am using this below config in my app.config. I am not much aware what’s the use of it. But I don’t want to use unnecessary parameters.
<log4net>
<root>
<level value="DEBUG"/>
<appender-ref ref="LogFileAppender"/>
</root>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="C:\logs\log.txt"/>
<param name="AppendToFile" value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="100KB"/>
<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>
Use <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> under appender section. I twill increase the logging performance.
log4net is threadsafe.
<maximumFileSize value="10MB" /> //For 100 Kb configuration there will be lot of files.
<datePattern value="_yyyyMMdd" /> //It will hint the logger to create a new file per date.
Create a static class for Logger and call the static function from every assembly where you want to use.
Sample Class for logging:
public static class Logger
{
static Logger()
{
XmlConfigurator.Configure();
}
public static void Log()
{
string methodName = new System.Diagnostics.StackFrame(1, true).GetMethod().Name;
string moduleName = new System.Diagnostics.StackFrame(1, true).GetMethod().ReflectedType.FullName;
var appLog = LogManager.GetLogger(loggername);
appLog.Error(...);
}
}
that's not a problem; just use ILog as usual in assembly B.
just make sure you call Configure() once in your application's lifetime.
Yes.
I recommend familiarizing yourself with log4net. a simple google search or a look at the docs should do the trick.
<root>
<level value="DEBUG"/>
<appender-ref ref="LogFileAppender"/>
</root>
It will log only in debug mode, so make following entry in config:
<root>
<level value="ALL"/>
<appender-ref ref="LogFileAppender"/>
</root>

With log4net, do you specify which appender to use?

When you have 2 appenders, say one for debug errors and one for product errors, in your code, do you explicitly create 2 log classes or is an appender basically going to write any log messages that meet a certain criteria?
So in your code, you use a single log method, and depending on how your appenders are setup, it will log the message if it e.g. is logged from a specific namespace, or is a certain log level.
So its possible a single log entry is written to 2 log files?
Yes, you can have single log statements post to multiple appenders. As long as it meets the criteria for each, it will use each appender.
For example, take this config section, it logs all messages to file, and also logs warnings and errors to event viewer.:
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="c:\logs\MySite"/>
<appendToFile value="true"/>
<datePattern value=".yyyy-MM-dd.\l\o\g"/>
<rollingStyle value="Date"/>
<MaxSizeRollBackups value="14"/>
<param name="StaticLogFileName" value="false"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%thread] %-5level %logger{1} - %m%n"/>
</layout>
</appender>
<appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
<applicationName value="Trading.Web"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%thread] %-5level %logger{1} - %m%n"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="WARN"/>
<param name="LevelMax" value="ERROR"/>
</filter>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="RollingFileAppender"/>
<appender-ref ref="EventLogAppender" />
</root>
</log4net>
So this:
_log.Debug("This is a debug message");
Will only appear in the log file (because it doesn't meet the event log appender's filter criteria).
But this:
_log.Error("This is an error message.");
Will log to both the log file and the event viewer.
UPDATE: On your filtering question, if you had:
<root>
<level value="DEBUG"/>
<appender-ref ref="RollingFileAppender"/>
</root>
<!-- Print only messages of level WARN or above in the namespace Com.Foo -->
<logger name="Com.Foo">
<level value="WARN" />
</logger>
Then all things under Com.Foo will log if WARN or higher, but everything else would log at DEBUG or higher....

log4net not working in dll

I'm currently having issues with getting log4net to work within a particular dll. I'm currently using log4net in other dlls being called by my test app and logging is working fine within those dlls and also within my test app. It's this one particular dll that I'm having trouble with. Here is snippet of code from the dll I'm having trouble with.
//This is from ABC.dll
public class SessionFactory
{
protected static ISessionFactory sessionFactory;
private static readonly ILog log = LogManager.GetLogger(typeof(SessionFactory));
private static void Init()
{
try
{
//Read the configuration from hibernate.xml.cfg or app.config
Configuration normalConfig = new Configuration().Configure();
ConfigureNhibernateValidator(normalConfig);
log.Debug("Initializing session factory");
sessionFactory = Fluently.Configure(normalConfig)
.Mappings(m =>
m.FluentMappings
.AddFromAssemblyOf<OrderHeaderMap>()
.Conventions.AddFromAssemblyOf<PascalCaseColumnNameConvention>())
.ProxyFactoryFactory("NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu")
.BuildSessionFactory();
log.Debug("Finished initializing the session factory");
}
catch(Exception ex)
{
//Code not shown
}
}
}
In my test app I am calling:
log4net.Config.XmlConfigurator.Configure();
Here is my log4net configuration in my App.config file:
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<!-- ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF -->
<root>
<level value="DEBUG"/>
<appender-ref ref="SpeedTest"/>
</root>
<!-- This is a default logger that nhibernate uses to push out all the SQL statements to-->
<logger name="NHibernate.SQL" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="NHibernateConsoleLog"/>
<appender-ref ref="NHibernateFileLog"/>
</logger>
<!-- This is a default logger that nhibernate uses to push out all the debugging type information to-->
<logger name="NHibernate" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="NHibernateFileLog"/>
</logger>
<appender name="NHibernateConsoleLog" type="log4net.Appender.TraceAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
</layout>
</appender>
<appender name="NHibernateFileLog" type="log4net.Appender.RollingFileAppender">
<file value="Logs/nhibernate.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%t] %-5p %c - %m%n" />
</layout>
</appender>
<appender name="SpeedTest" type="log4net.Appender.RollingFileAppender">
<file value="Logs/SpeedTest.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{HH:mm:ss.fff} [%t] %-5p %c - %m%n" />
</layout>
</appender>
</log4net>
</configuration>
Again logging is working fine within my test application using the SpeedTest appender but the logging within the above snippet of code is not working. I set breakpoints above on when it initializes the logger and it seems to hit it. I can post the log4net debug output if necessary but I didn't really see much. Just let me know if you need it and I will post.
Any suggestions as to why logging is not being recorded in the above snippet of code?
It seems that this issue was stemming from me changing the directory to all my external dependencies (log4net being one of them) awhile back in TFS. All I did was drop all my references in my visual studio project and re-add them from my new dependencies folder and everything worked as expected after this. Thanks for all those that helped here.
My suspicion would be that it isn't reading the configuration from the configuration file when you call configure.
If you add the following lines to your application, what do you see (either on the console, or in IDE output window, or by stepping through in the debugger):
var log4netConfig = ConfigurationManager.GetSection("log4net");
var log4netConfigIsNull = log4netConfig == null;
Console.WriteLine(log4netConfigIsNull);
Does it look like the configuration is actually available from the configuration file?
[Edit: in response to your comment]
If you now add another line of debug code to your app:
Console.WriteLine(log.IsDebugEnabled);
what output do you get? Does the logger think it is configured for debug logging? (I'm particularly interested in the behaviour of this in, or after, SessionFactory.Init).
The immediate thought would be that your logging code isn't getting hit, possibly due to an exception being thrown prior to your first log4net call. Can you put a log entry into a finally block, just to test that the logger works?

Is it possible to wildcard logger names in log4net configuration?

In my application, I use log4net, with all types creating their own logger based on their type - e.g. :
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
As I am developing, I leave the root logger on DEBUG so as to catch all log output from my code.
However, a third party component also uses this same approach, but is generating 100s of log messages a second, none of which I am interested in.
Is it possible to use some sort of wildcarding in the logger configuration, to force all their loggers to only log at WARN, e.g. :
<logger name="com.thirdparty.*">
<level value="WARN"/>
</logger>
[The exact example above, using a * doesn't work]
You can just specify part of a namespace so it will apply to all messages within that namespace (including nested).
Here is the example I often use:
<root>
<level value="FATAL" />
<appender-ref ref="RollingFile" />
</root>
<logger name="MyCompany.Web" >
<level value="WARN" />
<appender-ref ref="WebErrors" />
</logger>
<!-- Will log all FATALs from NHibernate, including NHibernate.SQL and all the nested -->
<logger name="NHibernate" >
<level value="FATAL" />
</logger>
Additionally I would recommend to read the manual. It provides a lot of explanation. For example you can read about Logger Hierarchy. Here is the quote from there:
A logger is said to be an ancestor of
another logger if its name followed by
a dot is a prefix of the descendant
logger name. A logger is said to be a
parent of a child logger if there are
no ancestors between itself and the
descendant logger. The hierarchy works
very much in the same way as the
namespace and class hierarchy in .NET.
and also:
Level Inheritance:
The inherited level for a given logger X, is equal to the first
non-null level in the logger
hierarchy, starting at X and
proceeding upwards in the hierarchy
towards the root logger.
Can't you do the opposite of what you're asking. What I mean is just set the default log level to warn and then set the specific loggers you have defined to DEBUG.
Also, you could set the threshold of your appender to DEBUG and have the other appender set the WARN.
For example:
<appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
<applicationName value="Application" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
<threshold value="WARN" />
</appender>
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender,log4net">
<to value="asdf#example.com" />
<from value="group#example.com" />
<subject value="Notification" />
<smtpHost value="server01" />
<bufferSize value="1" />
<lossy value="false" />
<layout type="log4net.Layout.PatternLayout,log4net">
<conversionPattern value="%property{log4net:HostName} :: %level :: %message %newlineLogger: %logger%newlineThread: %thread%newlineDate: %date%newlineNDC: %property{NDC}%newline%newline" />
</layout>
<threshold value="DEBUG" />
</appender>

Categories

Resources