Monitoring for ASP.NET APIs with NLog, Azure, and Application Insights
Context
By the end of this post, you will gave all your application logs published in Application Insights in Azure. We will walk through the steps to set up monitoring for your Asp.Net Web APIs, using NLog, and Azure Application Insights, and we will be having health checks logging http requests along with their status code. We will have a bit of work to do, so let’s cover some context first.
Traditionally what we used to do to implement logging in our applications was to create an interface like ICustomeLogger, and implement it with whatever we liked using NLog, Log4Net or any other logging framework. We did that to minimise the change in our applications if we decided to change the logging framework later on.
As a result of that, every application ended up repeating exactly the same pattern over and over again. Since then Microsoft came up with an ILogger interface (inside Microsoft.Extensions.Logging) which can be used as a consistent interface across all Apps and APIs.
We will then inject ILogger anywhere we need to log errors or any messages, and it will go straight to Application Insights in Azure or a local file when testing on your local machine.
By the way, this topic belongs to the series to set up an Asp.NET API for production use.
- API Route Versioning
- Configuration Management
- Secret Management
- Monitoring
- Database
- Documentation
- CORS
- Request Validation
- Global Exception Handling
- URL Rewriting
- Deploy .NET API to Azure App Service
1. Add NLog and ApplicationInsight Packages
At the end also make sure you have the required packages to support the above implementation:
- Microsoft.ApplicationInsights.AspNetCore
- Microsoft.ApplicationInsights.NLogTarget
- NLog
- NLog.Web.AspNetCore
- NLog.WindowsIdentity
2. Implement Microsoft.Extensions.Logging.ILogger
Starting very simple, and first things first, we need to implement ILogger. As we are using NLog, we will be relying on NLog config files to configure log content and message formats (we will cover nlog.config files in detail later). Our API will be tested and deployed in multiple environment including local machine and production, and hence we define two config files. The reason for that, is on our local machine, we probably don’t want the messages to be logged in any Azure Application Insight instance, and hence the log destinations will be different and so are the config files, to keep them separate and clean.
- nlog.config
- nlog.production.config
For this reason, when we are implementing the ILogger, we will inject which file to read the configs from, based on the environment. Let’s call this method, GetLogConfigFileName.
Next we will need to implement the main Log method, like below:
There are a few points that need to be covered here.
1. Dynamic Log Messages: When we log messages or errors, ideally we should have some dynamic information logged along the main message. Parameters such as UserId, Environment, User Agent, etc. These will help us to troubleshoot problems later on when they happen.
Be mindful of Personally Identifiable Information and Secrets
You should not log information that would identify information about individual customers such as name, address, date of birth, credentials. However for troubleshooting purposes, we may log UserId, as long as it’s a GUID and not an email address. This will in turn help to troubleshoot edge cases, when only a handful of customers have specific errors.
Going back to logging dynamic information with our log messages, you can see we inject those values into log messages using WithProperty method, meaning it will replace the placeholder variable UserId for example, with the actual UserId variable extracted from the http request or user context.
But where did we define these variable and literals? They come from the message format inside nlog.config, like below:
As you can see we have placeholders inside the message such as event-properties:item=UserId, which basically says: read the property value UserId as it is supplied using WithProperty method dynamically.
2. Exceptions: In Application Insights, it’s important to have log records to be classified properly, information as information, and exception as exception. This is important for troubleshooting, monitoring and analytical purposes. Hence we specifically set the exception of the log if the exception is not empty: logEventInfo.Exception = exception;
And here is the complete Logger class using NLog:
3. Add NLogger to the Middleware Pipeline
Next step is to inject the ILogger into your dependency injection ecosystem and middleware pipeline using the below method which you can call as part of Asp.Net 5.0 middleware set up.
Please note that RequestContext and UserContext are simply two of our customer classes carrying user and context information around.
4. nlog.config and nlog.production.config
We then need to add these two NLog configuration files to the root of our Asp.Net 5.0 Web API. The context is the same as what we had earlier. The only element that would change from environment to environment is which target we keep in the rules section of our config file. As you can see for local development (nlog.config), we log to allfile target, and for production, we log to logToApplicationInsights target.
The other point is around the instrumentation key, which we can get from Azure Application Insight instance (on the right side of the overview tab).
Note
Configuration, plumbing and troubleshooting your software foundation take a considerable amount of time in your product development. Consider using Pellerex which is a complete foundation for your enterprise software products, providing source-included Identity and Payment functions across UI (React), API (.NET), Pipeline (Azure DevOps) and Infrastructure (Kubernetes).
5. Integrate Multiple API Logs into One ApplicationInsight instance
Although it is tempting to create a separate instance of ApplicationInsights per API, however sometimes it makes sense to integrate them all together as part of one ecosystem, so you can have an overall look at the logs of a product which consists of multiple applications.
To achieve that, all you need to do is have the same InstrumentationKey in the nlog files of all the APIs you wish to see the logs of, in that instance of ApplicationInsights.
6. Decide What Types of Messages Log
From a monitoring point of view, apart of errors, exceptions, and all the custom messages you log manually, you also might like to see the requests, status code, and also how long they take to finish. For that purpose, you should also log some diagnostics messages automatically in your APIs.
To achieve that, simply go to your appsettings.json, and define the below in your logging section.
As a result of this, apart from all other messages specified, you also get some telemetry logs indicating the status code of the requests and also how long they took to return a response.
Pellerex Foundation: For Your Next Enterprise Software
How are you building your current software today? Build everything from scratch or use a foundation to save on development time, budget and resources? For an enterprise software MVP, which might take 8–12 months with a small team, you might indeed spend 6 months on your foundation. Things like Identity, Payment, Infrastructure, DevOps, etc. they all take time, while contributing not much to your actual product. These features are needed, but they are not your differentiators.
Pellerex does just that. It provides a foundation that save you a lot development time and effort at a fraction of the cost. It gives you source-included Identity, Payment, Infrastructure, and DevOps to build Web, Api and Mobile apps all-integrated and ready-to-go on day 1.
Check out Pellerex and talk to our team today to start building your next enterprise software fast.