If you’re interested in getting started with C# logging as quickly as humanly possible, you’ve come to the right place.  Today, we’ll look at just how to do that. 

Don’t worry, we’ll get into comprehensive detail too, covering a number of things.  Some of those include

  • What is logging?
  • Why would you want to log?

But for now, let’s get to the action.  How can you start logging in the absolute quickest and simplest way possible?  Let’s take a look.

The Simplest C# Logging That Could Possibly Work

Let’s start with a clean slate for the sake of easy demonstration.  First, open Visual Studio and create a new console project by selecting File->New->Project, as shown here.

I’ve called my new project ConsoleProject and opted to put it into a new solution called LoggerPlaypen.  No need to congratulate me on my creativity, though.

This will open the Program.cs file containing your Program class.  Let’s now add a few lines of code to that class, as follows:

static void Main(string[] args)
    var displayMessage = "Hello, user!";


We now have an application that successfully defines a greeting, displays it, and waits for an enter keystroke to dismiss it. 

When you hit F5 to run, you’ll see the following message before pressing enter:

But let’s say that we wanted not only to display this message but also to log it to a file.  How might we do that? 

With something fancy, like a logging framework, inversion of control, or aspect-oriented programming?

Nah, let’s not get ahead of ourselves.  Try this instead, making sure to add “using System.IO;” at the top with the other using statements:

static void Main(string[] args)
    var displayMessage = "Hello, user!";

    File.WriteAllText("log.txt", displayMessage);


Now, when you hit F5, you’ll see your greeting message once again.  But that extra line you’ve added will create a log file for you. 

Go to your project directory, and then navigate to the Bin->Debug folder. You’ll see a file hanging out there in addition to your console executable.

Is That Really It?

And…that’s it.  You just successfully implemented your first C# logger!

Is that all you need to know about logging?  Of course not. 

Will this logging scheme be sufficient for any but the most trivial applications?  Nope. 

But have you learned the barest essential backbone of C# logging?  Absolutely.

I told you it’d be a quick start.  But I strongly recommend that you read on to really get a handle on this logging thing.

What Is Application Logging?

Let’s briefly talk about what application logging is, from a definition point of view.  Here’s how I’ll define it.

Application logging involves recording information about your application’s runtime behavior to a more persistent medium.

So take the little logger we created, for example.  The goal of the application was to display a message to the user, which it did. 

But what if we wanted to store information about that message more permanently? 

That’s where logging comes in.  We write the message to a more permanent medium so that we can investigate later, after the program has finished.

Sure, it might not be overly interesting when we’re defining the greeting inline.  But what if we took the greeting as a command line parameter or read it from a file somewhere? 

Then having this permanent record seems more useful.

Application logging is the practice of determining what information is useful to capture and then recording it somewhere for future access.

Why Bother Logging? What’s the Motivation?

So far, I’ve talked in general terms about motivation.  Maybe we want to see what happened later.  But let’s get specific.

Software is really complicated.  And to make matters worse, for most of its life, you don’t run it in debug mode on your desktop. 

Instead, you build this enormously complicated thing, and then you turn it loose into the wild, hoping for the best. 

Logging is your main means of managing this complexity and uncertainty. Logging helps in the following ways:

  • You can read through the entries in the log to gain a sequential understanding of what your application did.
  • When users report errors or issues, you can use the log to attempt to reconstruct what went wrong and then, ideally, to recreate and fix the issue.
  • If you actively monitor logs (generally in cloud/web environments more than desktop apps like our little toy), you can detect problems as they’re happening or even before they happen.
  • If your application handles important or sensitive information, you can use (hopefully secured) logs to track how that information flowed through your system.

In short, you log so that you can retroactively debug and analyze your application as if you were running it in the moment.

What’s Involved With Logging?

All of that sounds pretty impressive, and you might be wondering how we get all of that from a file containing the text “Hello, user!” 

It’s admittedly a bit of a long road. (And I’ll talk more about the vast difference between what we did above and proper application logging shortly.)

The answer is that a lot more goes into a proper logging strategy than just randomly dumping text.  I’ve talked at length on this subject in the past, but let’s quickly review some of the components that go into creating entries in your log.

Conceptually, you can think of each entry in a log as an event.  This makes sense because you’re communicating information at a snapshot moment in time. 

So the value of the log stems directly from what you capture about the events you put into the log.

Here are some things that you’ll usually want to capture for each event.

  • A unique identifier for the event.
  • A timestamp.  Exactly when did this event take place?
  • Context that makes it clear to a reader what’s going on.  For instance, just recording “Hello, user!” might prove confusing weeks or months later.  A better message, including context, might say, “Recorded user greeting of ‘Hello, user!'”
  • Tags and categories for each entry for later search and classification.
  • Log levels, such as “error,” “warning,” or “information,” that allow further filtering and provide additional context.

As you can see, our log file above leaves a bit to be desired.

You Can Log to Different Media

You may have noticed that, in spite of using a file in our initial example, I haven’t talked specifically about files very much. 

There’s a reason for that.  You might want to log somewhere other than a file.

In most contexts that you’ll hear about logging, you’ll hear about log files most commonly.  But that doesn’t mean you have to use a file. 

Here are some other common destinations for log information that you should be aware of.

There are more than that besides.  Honestly, the possibilities are limited only by your imagination and ability to implement it. 

And for each one of those destinations, there are a number of variants on the format and other particulars.

Our Little Log, Revisited

Now that you understand more about logging, let’s think back to our toy example, designed to get you going quickly. More specifically, let’s look back on how inadequate it turns out to be.

If you’ve given it some thought, here are questions you might be asking at this point:

  • That code overwrites the log each timedon’t we want it to append to the file or something?
  • What if we run two instances of that program at once?  Won’t that get weird with log file permissions?
  • If we want all of that contextID and timestamp stuffdo we seriously have to write all kinds of boilerplate code?
  • Must we write to files or whatever in every method in our codebase?
  • And speaking of files, what about the other mediums above?  How would we do databases or web services?

There are a lot of questions for which you’d need answers in order to have a good logging approach.  But don’t get discouraged.

Introducing Logging Frameworks

Luckily, you won’t need to spend the next four months answering the questions above.

It turns out that smart people have already answered them for you and made code available so that you don’t need to worry.  And best of all, this code is often free.

Enter the concept of the logging framework.

A logging framework is a package that you install in your codebase, usually via package management.  Out of the box, it gives you flexible, configurable logging and a ready-made API. 

You can make one-liner calls as simple as the one I made to the file API at the beginning of the post. 

But with this call and with some initial configuration, you have nicely formatted, consistent log entries and answers to all of the questions you were just asking.

Let’s Install a Logging Framework

Let’s return now to our little toy codebase.  And let’s also delete that call to File.WriteAllText() that we added since it’s time to log like pros.

Recall that I just mentioned using a package manager.  The first thing we’re going to do here is use the Visual Studio package manager, NuGet.

And we’re going to use it to install a logging framework called Log4Net.  This is not the only option you have for such a framework, but it’s a popular one, and this is a quick start guide. You can always play with others later.

So right-click on your project in Visual Studio, and click “Manage NuGet Packages.”

At the top right, click on the “Browse” link and then, in the search bar below, type “log4net” and press enter.  The top result will be what you want.

Click the little arrow next to the version to install it.

Configuring Your Logger

Alright.  Now, if you look around for tutorials about log4net, you might find yourself confused.

Log4net setup is handy, but it’s not completely turnkey.  And most tutorials confuse you with options and settings and such.

I’m going to break this down instead to dead simple, rote instruction. This isn’t the only way to do it, but it’s the way with the fewest confusing steps between you and the log file you’re chomping at the bit to have.

There are two components to what you need to do:

  1. You have to make changes to the App.config file that tell Log4Net how to behave in general.
  2. You have to add three lines of code:
    1. Declare an instance of the logger object.
    2. Execute a command that tells Log4Net to set itself up according to your settings in App.config
    3. Actually log something.

So let’s do that.  First, add this exact code to your App.config file, inserting it between the <configuration> tag and the <startup> closing tag.

  <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
    <param name="File" value="proper.log"/>
    <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="2" />
    <maximumFileSize value="1MB" />
    <staticLogFileName value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"/>
    <level value="ALL" />
    <appender-ref ref="LogFileAppender" />

To be clear, here is the “before” screenshot of my App.Config file:

And this is what it looks like afterward:

Once you’ve made the changes, save the App.config file.

Now For The Code

Okay. With the configuration squared away, you’re ready to add some code.  The easiest thing will be to add each line of code I mention and then to use the Ctrl+Period shortcut to take care of the usings.

Doing this will trigger Visual Studio to automatically add the libraries you need, including the Log4Net ones that came along for the ride when you installed the NuGet package.

First, as a class level field, add the following line.  You will need to use the Ctrl+Period trick to bring in the proper library.

private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

Next, at the very top of your Main() method, add the following line.  It will require another Ctrl+Period.


And finally, add the following line of code to where you earlier had the call to File.WriteAllLines().


The final class should look like this.

That’s it—all done.  Now, you just have to run it.

Taking a Look at the Results

Run it as before, and you’ll observe the same behavior as before.  But if you navigate back to the Bin->Debug folder, check out a new file there, called “proper.log.”

Here’s what the folder and file look like now.

Remember how earlier I talked about the sorts of things that you want to capture in a log?  Well, look what we’ve got here with our single log event now:

  • A timestamp.
  • The thread this message came from.
  • The logging level.
  • The class of origin.
  • The message.

Is that everything you might want?  No.

But is it a darned good start, considering that you added a package, pasted some XML, and added three lines of code?  I’d certainly say so.

And that is the power of a logging framework.

And speaking of that blob of XML that you pasted just to get things going, there’s a lot of power there as well.

The “layout” entry lets you configure a TON of information about each log entry that you capture, for instance. 

In there, you can also control some other settings as well, such as the name of the log file, how big you let it get, and some other stuff about rolling backups.

That “other stuff” starts to become clearer when you understand what an appender is.  Note that you configured an appender in XML.

The appender is the pluggable thing that lets you choose whether you want to send it to a file, a database, the console, etc.  In the link I included, you can see that there are a lot of appenders at your fingertips. 

This means that you can leave your application logging code alone and just update this XML file with a different appender to direct your logs somewhere else entirely.

Pretty neat, huh?

Other C# Logging Options

Microsoft added a very great feature called TraceSource in the .NET Framework version 2.0 is an enhanced tracing system for your apps. This can help you in logging your C# applications.

In order to start using this tracing system in your application, you need to do a few configurations to get started with this tool. If the project is large with lots of components, it is recommended to create a trace source for each component. You should also name a trace source with the same name as that of the application. For this example, trace source is named as TraceSourceApp and the code bellow initializes trace source.

using System;   using System.Diagnostics;   using System.Threading;   namespace TraceSourceApp   {       class Program       {           private static TraceSource mySource =             new TraceSource("TraceSourceApp");           static void Main(string[] args)           {               Activity1();               mySource.Close();               return;           }           static void Activity1()           {               mySource.TraceEvent(TraceEventType.Error, 1,                 "Error message.");               mySource.TraceEvent(TraceEventType.Warning, 2,                 "Warning message.");           }       }   }

Now that trace source has been initialized, we can also initialize trace listeners and filters, this will help in identifying filters or listeners from the previous code. Here is how you can do that. According to the documentation, “the filters identified for the two listeners are initialized with different source levels. This results in some messages being written by only one of the two listeners”.

<configuration> <system.diagnostics> <sources> <source name="TraceSourceApp" switchName="sourceSwitch" switchType="System.Diagnostics.SourceSwitch"> <listeners> <add name="console" type="System.Diagnostics.ConsoleTraceListener"> <filter type="System.Diagnostics.EventTypeFilter" initializeData="Warning"/> </add> <add name="myListener"/> <remove name="Default"/> </listeners> </source> </sources> <switches> <add name="sourceSwitch" value="Warning"/> </switches> <sharedListeners> <add name="myListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="myListener.log"> <filter type="System.Diagnostics.EventTypeFilter" initializeData="Error"/> </add> </sharedListeners> </system.diagnostics> </configuration> 

C# Logging: What Now?

We’ve really just scratched the surface of C# logging here, but that was the goal of this guide. I wanted to get you up to speed as quickly as humanly possible.

The work you now have in front of you is a bunch of learning, but at least you can do that learning with a real live working log file.

You should probably start by playing with your current setup, which is by no means the best.

For instance, you might want to move the log4net configuration entry out of App.config and into its own separate file in a nod to the single responsibility principle.  That’s an option you have.

It’s also worth noting that you have options not just for appenders but also for logging frameworks, if you want.

Log4net is battle-tested and will get the job done for you, but you might want to try other options at some point as well. 

And if you stick with log4net, you might want to check out an in-depth tutorial, like this one at Pluralsight, for instance.

And you can always stay tuned to the blog here at Scalyr for additional posts about logging and logging practices, like this one.

Scalyr makes a log aggregation tool, meaning that once you get good enough to have lots and lots of data in lots and lots of log files, they’ll help you put them all in one place, search them, and visualize the data in them.
If this sounds like what you need to get the job done, you can sign up here and try out the Scalyr demo.

So this blog is always a good source for general logging information.

But your main task now is to tinker, experiment, configure, and grow your application logging until it suits your needs and you’re getting all of the data you want out of it.

Comments are closed.

Jump in with your own data. Free for 30 days.

Free Trial