ASP.NET Core 7 API Performance Report: Logging Mastery with Serilog

Akash Jaiswal
6 min readSep 7, 2023

--

Hello, everyone! I hope you’re having a fantastic day. In this article, we’ll delve into the world of advanced logging in your ASP.NET Core 7 projects. Our focus will be on configuring logging to permanently store log data on disk, and for this purpose, we’ll utilize the powerful Serilog library. Moreover, we’ll construct a practical example by generating performance report logs for your API endpoints.

So, before we dive into the technical details, let’s first understand why logging is crucial for your ASP.NET Core 7 projects.

The Significance of Logging

Before we dive into the technical stuff, let’s talk about why logging is so important in ASP.NET Core 7:

  1. Debugging and Troubleshooting: Logs act like detectives, showing you what went wrong when your code misbehaves.
  2. Performance Monitoring: They help you keep an eye on how your application performs, like checking response times and finding slow parts in your code.
  3. Security and Auditing: Logs can catch suspicious activities or security issues, making your app safer.
  4. Compliance: In many industries, detailed logging is a must to meet rules and regulations.

Now that we’ve established the importance of logging, let’s introduce you to Serilog and how it fits into our advanced logging setup.

What is Serilog?

Serilog is like a super useful tool for keeping track of what’s happening in your .NET projects. What makes it special is that you can customize how it collects, processes, and stores log information. It’s kind of like having a super organized filing system for all your logs.

One cool thing about Serilog is that it can make your logs easy to search and analyze, thanks to something called structured logging. In our case, we’ll use Serilog to save log data permanently on your computer.

Now that you know a bit about Serilog, let’s get into how we make it work. Assuming you’ve already set up your ASP.NET Core 7 project, we’ll jump right into setting up Serilog for logging.

Configuring Serilog for Advanced Logging

First and foremost, you need to install the Serilog.AspNetCore package from NuGet or via the terminal:

dotnet add package Serilog.AspNetCores

Now, let’s set up Serilog in your project.

  1. Add the following Dependency Injection (DI) configuration code in your Program.cs:
// ... Other Configuration

Log.Logger = new LoggerConfiguration()
.WriteTo.File("Log/application.log", rollingInterval: RollingInterval.Day)
.CreateLogger();

builder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddSerilog();
});

var app = builder.Build();

// ... Rest of your code

app.Run();

Log.CloseAndFlush(); // Don't forget to add this.

With this code, you’ve successfully configured Serilog for advanced logging in your ASP.NET project.

Henceforth, you can log from any corner of your project with simplicity:

  1. Import Serilog: using Serilog;
  2. Save log data: Log.Information("Your log data here");

Now that we’ve mastered setting up Serilog for logging, let’s advance to a practical demonstration of crafting performance report logs for your API endpoints.

Crafting Performance Report Logs for API Endpoints

To monitor the performance of your API endpoints, we’ll employ middleware. This middleware commences a timer for each incoming request and halts it before responding to the client. Please take note that this measures server-side processing time and may not account for external factors like internet speed or client-side processing.

Let’s delve into the code:

  1. Create a Middlewares folder in your project directory.
  2. In the Middlewares folder, create a file named PerfomanceLoggingMiddleware.cs with the following code:
using System.Collections.Concurrent;
using System.Diagnostics;
using Serilog;

namespace YourNamespace.Middlewares; // Change with your namespace

public class PerfomanceLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<PerfomanceLoggingMiddleware> _logger;
private static readonly ConcurrentDictionary<string, (long totalTime, int requestCount)> _routeStats = new();

public PerfomanceLoggingMiddleware(RequestDelegate next, ILogger<PerfomanceLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}

public async Task Invoke(HttpContext context)
{
// Measure the start time of the request
var stopwatch = Stopwatch.StartNew();

// Let the request proceed through the pipeline
await _next(context);

// Measure the end time of request
stopwatch.Stop();

// Get the route (path) of the request
var route = context.Request.Path.Value;

// Calculate average time taken for this route
_routeStats.AddOrUpdate(route!, (stopwatch.ElapsedMilliseconds, 1), (_, data) =>
{
var (totalTime, requestCount) = data;
return (totalTime + stopwatch.ElapsedMilliseconds, requestCount + 1);
});

// var (averageTime, requestCount) = _routeStats[route!];

// Check if the response is success.
if (context.Response.StatusCode == 200)
{
// Log the performance data for the route
var (averageTime, requestCount) = _routeStats[route!];

// Foramt for log data
var logData = $"Route: {route}, Time: {stopwatch.ElapsedMilliseconds}ms Average Time: {averageTime / requestCount}ms, Total Requests: {requestCount}";
// Log the performance data using Serilog
Log.Information(logData);
}

}

}

That’s a brief breakdown of what each log entry means:

  • Route: The route of the API endpoint being logged.
  • Time: The time taken to process and respond to the request for this specific instance.
  • Average Time: The average time taken for this route over multiple requests.
  • Total Requests: The total number of requests processed for this route.

3. Now, integrate this middleware into your Program.cs:

var app = builder.Build();

// Make sure to place this middleware at the top for accurate timing of other middlewares as well.
app.UseMiddleware<PerfomanceLoggingMiddleware>();

// ... Rest of your code

app.Run();

With this middleware seamlessly integrated, you’ll be able to log the performance of each API endpoint within your application.

Testing the Waters

Let’s put our setup to the test by crafting some sample APIs that respond with delays of 500ms, 1000ms, and 1500ms, respectively.

using Microsoft.AspNetCore.Mvc;

namespace YourNamescape.Controllers; // Change with your namespace

[Route("api/[controller]")]
[ApiController]
public class DelayController : ControllerBase
{
[HttpGet("delay500")]
public async Task<IActionResult> Delay500()
{
await Task.Delay(500);
return Ok(new { message = "successful" });
}

[HttpGet("delay1000")]
public async Task<IActionResult> Delay1000()
{
await Task.Delay(1000);
return Ok(new { message = "successful" });
}

[HttpGet("delay1500")]
public async Task<IActionResult> Delay1500()
{
await Task.Delay(1500);
return Ok(new { message = "successful" });
}
}

Now, let’s execute each of these APIs to witness how our API endpoints’ performance gets logged.

application.log
application.log

As highlighted in the log data image, you can see the custom log entries related to API performance. These entries provide valuable insights into the duration of each API request’s processing.

Managing Log Data with Separate Files

If you ever feel like you have too many logs piling up and it’s getting hard to keep track of them, here’s a smart idea: you can change the way we handle log data in the PerfomanceLoggingMiddlewarecode. Instead of mixing all the logs together, we can make a special log file just for performance data. This makes it much easier to organize and study how your program is performing, especially when you have a lot of logs, like in big projects. And the best part is, you can do this without needing to use Serilog for this specific task.

Here’s the modified code you need for that:

// ..... same as previous code

// Check if the response is success.
if (context.Response.StatusCode == 200)
{
// Log the performance data for the route
var(averageTime, requestCount) = _routeStats[route!];

// Log the performance data using Serilog
var logData = $"Route: {route}, Time: {stopwatch.ElapsedMilliseconds}ms Average Time: {averageTime / requestCount}ms, Total Requests: {requestCount}";

Log.Information(logData);

// Write the log data to the "Perfomance.log" file
File.AppendAllText("Log/perfomance.log", logData + Environment.NewLine); // add this line of code for writing the log into a completly seprate file
}

// .........
Perfomance.log data shown in the image
perfomance.log

In the image above, you can see the contents of the “performance.log” file, showcasing the detailed performance data recorded for each API endpoint. This approach allows you to keep your performance logs neatly organized in their own file, making it easier to analyze and manage the data.

With these enhancements, you now have an efficient logging and performance monitoring setup. You’re well-prepared to optimize your application’s performance and diagnose any issues that may arise.

That concludes our exploration of advanced logging with Serilog and crafting performance monitoring in ASP.NET Core 7. If you have any questions or require further code examples, please don’t hesitate to reach out in the comments. Your feedback and engagement are immensely valued. If you found this article helpful, consider showing your appreciation by giving it a clap and sharing it with your peers. Happy coding!

--

--