In an ideal world, software is completely “bug-free,” and unicorns come in a variety of colors.
A good software should be resilient to occasional meltdowns. Goals should be to keep the issues to a minimum and increase the turnaround time. In this blog, I am going to describe how ‘Logging’ can be extremely useful when troubleshooting Production issues.
Same old story
On a Friday evening, just before you are about to step out, you get a support call, and they say, “Something went wrong!” I am sure we all have been there before. On probing more, you get another piece of useless information, “I was doing my usual thing and the application crashed on me!” And you spend the rest of the evening just understanding what that “something” is. The fix may or may not take that long but just getting to the point where you can re-create the scenario may take some good time and effort.
Tell me “exactly” what happened
As somebody who would be troubleshooting the issue, I would be interested in getting as much information as possible. I would ask questions like –
- What steps led to the error?
- Was an error flashed on the screen?
- Is there anything logged in the Event Viewer?
- Is the error user/environment specific?
- Can it be recreated?
It would be great to have a report that describes in detail the events that have occurred, and the order in which they have occurred. Any information is good information when it comes to troubleshooting.
My Dear Diary
Logging is like writing a Diary. Imagine that an application is recording events, errors or just plain tips, it can be really anything. This Diary or Log can be used later for
- Troubleshooting errors by following the trail that is left behind
- Monitoring errors and exceptions
- Data Mining by tracking user clicks
- Building Knowledge Base
Usual Resorts for Logging and their Shortcomings
Developers resort to old techniques for logging, such as logging to Windows Event Log using (System.Diagnostics.EventLog) or using StreamWriter to write to an external file. Although these are acceptable, they are not the most modern and decent ways of logging. If you wish to write logs to a different channel, or log only certain events, then it may need code changes. Also, it is like re-inventing the wheel, since there are 3rd party libraries already doing all of this and more. In this blog, I am going to discuss an open source .Net Framework called ‘Log4Net’
I am going to walk you through the whole process of setting up Log4Net through a sample Console app.
Step 1: Create a new Console App inside Visual Studio or VS Code and name it as ‘Logging’.
Step 2: Installation
Right click the Project in solution explorer and go to ‘Manage Nuget Packages.’ Search for Log4Net and install it. Alternatively, use NuGet Package Manager and type command
PM> Install-Package log4net
In the AssemblyInfo.cs file, add the following line
Step 3: Configuration
Next step is to define a config section for Log4Net in web.config or app.config.
Next step is to add your Log4Net settings in the Log4Net Config Section. This section will have entries for various Appenders and Root Logger.
Step 4: Appenders
Let’s start with a simple Console Appender.
Step 5: Log using Log4Net
And that’s it, this is how it’s done. Let’s look at some other options out there in terms of Appenders and Filtering.
Log Levels and Log Filters
There are 5 different log message levels as listed below, each with a varied degree of seriousness and priority.
Developers should use all these levels to log information, events, errors, etc. depending on what happened and the seriousness of the situation.
In log4net lingo, an output destination is called an appender. Appenders can be thought upon as subscribers who are registered to log events to multiple destinations. Log4Net comes with a standard list of appenders. Most common appenders are FileAppender, RollingFileAppender, and EventLogAppender.
Although Log4Net comes with a long list of appenders, there may be a case where you want to write your own appender, let’s say an integration with your Bug Tracking system to create a new bug on each error, or maybe trigger a Logic App in Azure when an error happens. This can be done by implementing log4net.Appender.IAppender interface. An easier approach is to derive your custom class from an abstract class log4net.Appender.AppenderSKeleton which does most of the work of implementing the interface.
Users can customize the message that gets logged to the list of Appenders using Layouts. A layout goes with each Appender.
This will output something like this
2018-12-26 12:25:22,822 INFO StarTrackIntegration.Bll.LocationIntegrator – Location Integration started
The following layouts are included in the log4net package:
|log4net.Layout.ExceptionLayout||Renders the exception text from the logging event.|
|log4net.Layout.PatternLayout||Formats the logging event according to a flexible set of formatting flags.|
|log4net.Layout.RawTimeStampLayout||Extracts the timestamp from the logging event.|
|log4net.Layout.RawUtcTimeStampLayout||Extracts the timestamp from the logging event in Universal Time.|
|log4net.Layout.SimpleLayout||Formats the logging event very simply: [level] – [message]|
|log4net.Layout.XmlLayout||Formats the logging event as an XML element.|
|log4net.Layout.XmlLayoutSchemaLog4j||Formats the logging event as an XML element that complies with the log4j event dtd.|
This is a very useful feature of Log4Net as it allows a user to decide what gets logged. The filters can be configured to either accept or reject the event based upon the match.
You’ll want to maintain 2 files, one as Trace.txt and the other as Error.txt using File Appenders. Trace file will have all the events logged as the executing progresses, whereas Error file will only log errors. This can be achieved with the following configuration
The following filters are defined in the log4net package:
|log4net.Filter.DenyAllFilter||Drops all logging events.|
|log4net.Filter.LevelMatchFilter||An exact match to the event’s level.|
|log4net.Filter.LevelRangeFilter||Matches against a range of levels.|
|log4net.Filter.LoggerMatchFilter||Matches against a the start of the logger name.|
|log4net.Filter.PropertyFilter||Matches a substring from a specific property value.|
|log4net.Filter.StringMatchFilter||Matches a substring from the event’s message.|
You can also create your own event filtering for complex business needs using the log4net.Filter.IFilter interface.
What I like about Log4Net
Log4Net is easy enough to start using it out of the box but at the same time it allows customization if there is a need for it. Also, the configurations such as adding new appenders and such are maintained at the high level (config file) where the deployment team can set it up easily without involving Development team or other IT staff.
Do You Know What to Do When Things Go South?
As a gold-certified Microsoft partner, KTL Solutions is here to help your business with any issues on Microsoft programs. We provide training programs to help you avoid problems to start with and offer customized software solutions tailored to what your business needs. Let us help you when you have an issue. Give us a call today.