I just rolled off a client recently that needed to build a Universal Windows Platform (UWP) application as part of their hardware and software solution. For those who aren’t yet familiar with UWP, you can check out this article by Tyler Whitney.
As many of you developers out there are aware, sometimes you have to build or bring with you a number of application infrastructure items before you can even get started with the core application logic. For example, you might need some helpers, services and base classes that make your job easier or allow you to start with your base patterns, such as, MVC, MVVM, etc.
So, I have decided to create a series of posts that build up a bunch of common, important parts of an application that you might want to have in place before you even start developing your core functionality. I have discussed a number of topics in my previous posts:
If you haven’t already read the previous posts, I recommend you do since they all build on each other.
Every developer knows the pain of distributing their application to someone who isn’t running it in debug mode in Visual Studio and is having a problem with the software. “Oh, I haven’t done anything.”, “It used to work!” or “This software just isn’t working.” are common statements that we hear, right?
Today, we are going to discuss how we can add logging support to our application so that we can get better information from a user to help find and fix any issues that they are experiencing.
How To Add MetroLog
We could roll our own logging system, but why when you can use another third party system? Okay, I know why you might want to roll your own so that you have complete control over the code. You can add features, etc. Correct? Usually.
However, for this post, I am going to use a NuGet package called MetroLog to implement some very basic logging. If you want to learn more about this package, you can simply go to their project in GitHub.
But for now, we are just going to install the NuGet package and use the library.
First, right click on your projects References and select Manage NuGet Packages….
Now, within the NuGet screen, Browse for metrolog, select the MetroLog package from the list and then select Install.
A dialog will be presented asking for you to review the changes that are going to be made to your project. Select OK.
Once it is installed, you will see a reference to it.
Now, let’s create our service that we will use in our application.
First, create a new folder by right-clicking on the Services directory, select Add, select New Folder and name the folder LoggingServices.
In our new folder, let’s create a couple of class files, by right-clicking on the LoggingServices folder, selecting Add and then selecting Class… Create two classes called: ILoggingServices.cs and LoggingServices.cs
Open up ILoggingServices.cs and let’s create an interface that has a simple method called WriteLine that accepts a string message, a log level and any exception information that we have captured. It will probably look something like this:
LogLevel is defined in the MetroLog library, so don’t worry about that for now. It basically just sets the level at which this message should be logged.
Now, let’s open the LoggingServices.cs and add some code there. Here is a list of properties that I would like to add to the LoggingServices class:
- Instance – for implementation of a singleton pattern.
- RetainDays – the number of days the logs will remain on the file system. This allows us to keep the footprint pretty minimal. Well, depending on the size of each log file, I guess.
- Enabled – this property can be used to turn on and off logging all together.
It might look something like this:
Next, let’s create our constructor.
The very first thing we do is implement the constructor piece of our singleton pattern by initializing our Instance. Second, I thought it might be helpful for you to see the enum that is defined for logging levels for the MetroLog library. Basically, Trace is the lowest level and Fatal is the highest level.
As you can see, we can configure the MetroLog library to log everything if we are in Debug mode and only levels Info through Fatal for a release version of the application. This will keep it simple for us.
Okay, now, let’s add the interface method to write the message to the log:
You will notice that I have added the WriteLine<T> method that accepts a string, a LogLevel and exception. If the logging service is enabled, it will check if the logging level passed in is enabled and then attempt to log the information type. Most all of the levels are the same structure, except that for the LogLevel.Debug. I decided to add a Debug.WriteLine so that I could see some of the debug messages in my console window in Visual Studio. Pretty simple huh?
Now, how do we use this service? Well, for this post, let’s just add a simple log entry each time we start the application. To do this, let’s open App.xaml.cs. Now, go down to the OnStartAsync method. At the top of the function, let’s add our LoggingServices call.
Run the application. The log should have been written to with our “Application starting…” string. Let’s take a look at it. By default, MetroLog saves the log to the following location:
C:\Users\[login]\AppData\Local\Packages\[package family name]\LocalState\MetroLogs
Where [login] is the name of the logged in user account and [package family name] is the ID generated for the application. How do you find the [package family name]? If you open the application manifest, Package.appxmanifest, you will see the Package family name at the bottom of the Packaging tab.
If you go to the folder, you should see the log files available.
And, if you open the log, it should look like this…
If you want to tweak how the log file looks, I think there are ways to define the format. You can check that out by hitting up the site.
NOTE: The Template10 library has a very basic logging service built in too called LoggingService. We could probably rename ours to make it easier to differentiate, but that is up to you. For now, I will keep ours as LoggingServices.
Now, you can add logging statements to all your code. Remember, the log entries that get written out to the log depends on the DefaultConfiguration defined on startup.
What We Accomplished
So, to summarize what we accomplished in this blog post:
- Added the MetroLog NuGet package to project
- Added a Logging Service to our services
- Demonstrated how to add a call to the logging service
- How we can view the logs created from the logging service
In part 5 of this series, I will add some analytics service to our bag of services. This will allow us to keep track of how users are using the system. This can be very useful when trying to analyze issues with the software and how it is being used. This data can then help us to focus and prioritize our efforts on building and maintaining our software based on how it is used.
I hope you enjoyed this post. If so, please share it with others.