Creating A Logging Library For Microsoft Application Insights
Before You Read
This article mostly revolves around the .NET
ecosystem, so familiarity with the following terms will be helpful:
Startup
: The class plays a central role in configuring the application’s services and middleware. It’s where you set up the application’s behavior, including how it responds to HTTP requests, interacts with databases, and integrates with other services.Dependency Injection
: It is a design pattern used to achieveInversion of Control (IoC)
between classes and their dependencies. In.NET Core applications
, DI is a core feature integrated into the framework, making it easier to manage object lifetimes, dependencies, and configurations.Application Insights
: It is an Azure service that provides real-time monitoring and performance insights for your applications. It collects telemetry data such as request times, error rates, and usage patterns to help diagnose issues and optimize performance. By integrating Application Insights, you can gain valuable visibility into your application’s health and user interactions.Extension Methods
: A way to add method to existing classes without altering the source code.
Pretext
While working at Microsoft, I was contributing in developing a solution to create an extensible logging
library for various services to enhance the telemetry stored in Application Insights
. Features like storing the request/response body, masking sensitive data, storing telemetry in a different storage account instead of Application Insights and more were commonly reimplemented across services. Instead of reinventing the wheel, we needed to come up with a standardized solution.
How to approach the problem ?
Since I was working at Microsoft, the ecosystem was built in C# and .NET
, so we didn’t have to worry about varying tech stacks. So, we needed to come up with a standardized way to integrate features and enhance the telemetry. After some investigation, it was found that the Application Insights team had a vision for enhancing telemetry, offering two main approaches:
- Add or Modify properties to any telemetry sent from your app by implementing
ITelemetryInitializer
. - Filtering can modify or discard telemetry before it’s sent from the SDK by implementing
ITelemetryProcessor
.
These classes provide control over modifying telemetry data (e.g., adding fields like request body) and controlling the flow of telemetry data (e.g., moving data to separate storage instead of Application Insights). You can read more about them in the official documentation
Now that we know that is possible how do we make it easy for the users to use the libary, Without Much Changes In The Application!
Bootstrapping Method
In .NET solutions, appsettings.json is used to store all application-related settings. We will provide a section in appsettings called TelemetrySetting, which will include boolean flags and other details used by the library to control feature activation.
A basic example of the JSON configuration is provided below. Similarly, for any new feature, just add the JSON config inside the TelemetrySetting and control the features you want to enable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"TelemetrySetting": {
"RequestBodyCapture": {
"Enable": true
},
"IngnoreTelemetry": {
"Enable": true,
"PathsToIgnore": [
"*"
]
},
"MaskTelemetry": {
"Enable": true,
"Regex": [
"^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$"
]
}
}
}
Enabling The Features
Just passing the settings is not enough. We need to add the corresponding ITelemetryInitializers
and ITelemetryProcessors
to the dependency pipeline, how do we do that ?
First of all, for any new feature we dont want people to manually add the corresponding implemented version of the Initializer and Processors in Dependency Injection pipeline, so lets provide an extension method on the IServiceCollection
. To simplify this process, we provide an extension method on IServiceCollection
that handles the heavy lifting for the developer. The startup will look like this:
1
2
3
4
5
6
7
8
9
10
11
12
var builder = WebApplication.CreateBuilder(args);
var telemetrySettings = builder
.Configuration
.GetSection("TelemetrySetting")
.Get<TelemetrySetting>(); // Define the TelemetrySetting model
builder.Services.BootstrapCustomTelemetryLibrary(telemetrySettings); // the extension method
app.Run();
Now implement all the logic to add the features in the extension method
1
2
3
4
5
6
7
8
9
10
11
12
13
public static IServiceCollection BootstrapCustomTelemetryLibrary(
this IServiceCollection services,
TelemetrySetting telemetrySettings)
{
if (telemetrySettings.RequestBodyCapture.Enable) {
services.AddSingleton<ITelemetryInitializer, RequestBodyCaptureInitializer>(); // RequestBodyCaptureInitializer implements ITelemetryInitializer
}
if (telemetrySettings.IgnoreTelemetry.Enable) {
services.AddApplicationInsightsTelemetryProcessor<IgnoreTelemetryFilter>(); // IgnoreTelemetryFilter implements ITelemetryProcessor
}
}
The End
I will not go into the details of the implementation of the individual features, I am sure you can figure those out. This article highlights how leveraging existing .NET
features and the extensibility of the Application Insights SDK
allowed us to develop a lightweight library, avoiding the need to reinvent the wheel and saving significant developer effort across the organization.