Logging in .NET and ASP.NET Core

Logging in .NET and ASP.NET Core

Logging in an ASP.NET Web API relies on the built-in ILogger interface provided by Microsoft, which supports various built-in and third-party logging providers (sinks). Core Concepts

  • ILogger: The interface used within your application code to write log messages.

  • ILoggerFactory / ILoggerProvider: These manage the creation of ILogger instances and determine where logs are sent (e.g., Console, Debug window, files, databases, or cloud services).

  • Log Levels: Messages are categorized by severity: Trace, Debug, Information, Warning, Error, and Critical.

Severities are ranked from most to least detailed:

  • Trace (0): Very detailed, often sensitive data.

  • Debug (1): Short-term usefulness during development.

  • Information (2): General flow of the application.

  • Warning (3): Abnormal flow that doesn't stop the app.

  • Error (4): Failures that stop the current operation.

  • Critical (5): Total system failures

  • Dependency Injection (DI): In modern ASP.NET Core, the logging infrastructure is automatically available via DI, making it easy to inject ILogger<T> into controllers and services.

Step-by-Step Implementation (ASP.NET Core)

The default web API template automatically sets up console, debug, and EventSource providers, with configuration managed in appsettings.json1. Configuration in appsettings.json You can control the minimum log level for different categories here.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information", // Default minimum level for all categories
      "Microsoft": "Warning"    // Overrides the default for Microsoft framework logs
    }
  }
}

2. Injecting and Using ILogger in a Controller Inject the logger into your controller's constructor. The category name defaults to the class's fully qualified name, aiding organization.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

[ApiController]
[Route("api/[controller]")]
public class SampleController : ControllerBase
{
    private readonly ILogger<SampleController> _logger;

    // Inject ILogger via constructor
    public SampleController(ILogger<SampleController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IActionResult Get()
    {
        // Use extension methods to log messages at specific levels
        _logger.LogInformation("GET request received for SampleController.");

        try
        {
            // ... application logic ...
            return Ok();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An error occurred during the GET request.");
            return StatusCode(500, "Internal server error");
        }
    }
}

Built-in vs. Third-Party Providers

By default, logs are sent to the Console and Debug output. For more robust needs like writing to files or databases, popular third-party libraries include:

  • Serilog: Known for structured logging (logs as searchable JSON rather than plain text).
  • NLog: Highly flexible with many targets (File, Email, Database).
  • log4net: A classic, modular framework often used in legacy systems.

Implementation of log in file using Serilog

1. Install Required Packages

Use the NuGet Package Manager or the .NET CLI to install the essential library:

  • Serilog.AspNetCore
  • Serilog.Sinks.File

2. Configure and Register Serilog

Method1: Configure and Register Serilog in Program.cs

// configure serilog to write in a file 
Log.Logger = new LoggerConfiguration().MinimumLevel.Debug()
    .WriteTo.File("log/villaLog.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

// register Serilog
builder.Host.UseSerilog();

Method2: Configure via appsettings.json (Recommended)

Add a Serilog section to your appsettings.json file. This allows you to change logging settings without recompiling the code

//JSON
{
  "Serilog": {
    "Using": [ "Serilog.Sinks.File" ],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "File",
        "Args": {
          "path": "Logs/webapi-.log",
          "rollingInterval": "Day",
          "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
        }
      }
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
  }
}

  • path: Specifies the folder and filename (e.g., Logs/webapi-20241027.log).

  • rollingInterval: Set to Day to create a new log file every 24 hours.

Initialize in Program.cs Update your Program.cs to use Serilog as the primary logging provider.

using Serilog;

var builder = WebApplication.CreateBuilder(args);

// Configure Serilog to read from appsettings.json
builder.Host.UseSerilog((context, configuration) => 
    configuration.ReadFrom.Configuration(context.Configuration));

builder.Services.AddControllers();
// ... other services

var app = builder.Build();

// Optional: Streamlined request logging (logs every HTTP request)
app.UseSerilogRequestLogging(); 

app.UseAuthorization();
app.MapControllers();
app.Run();

3. Use the Logger in Controllers

Integrate ILogger<T> via dependency injection in your controllers to use Serilog

Advanced Features

  • HTTP Logging Middleware: You can log entire HTTP requests and responses (headers, bodies, status codes) by adding app.UseHttpLogging() in Program.cs.

  • Step1: - Add Service

   builder.Services.AddHttpLogging(options =>
   {
       options.LoggingFields = Microsoft.AspNetCore.HttpLogging.HttpLoggingFields.All;
       options.RequestBodyLogLimit = 4096; // Set limits to avoid performance issues
       options.ResponseBodyLogLimit = 4096;
   });

  • Step2: Use Middleware (in Program.cs, before other middleware like UseAuthorization):

    app.UseHttpLogging();
    
    
  • Configuration: Adjust log levels without changing code by editing the Logging section in appsettings.json.

  • Redaction: Prevent sensitive data (like passwords or PII) from appearing in logs using data redaction tools.

Custom Logging

You can use DI for Logging vai custom class & its interfce

step by step Implementation using Example

1. Create ILogging Interface and Logging class

// ILogging Interface 
namespace WebApplication1.Logging
{
    public interface ILogging
    {
        public void Log(string message, string type);
    }
}

// Logging class which implement ILogging
namespace WebApplication1.Logging
{
    public class Logging : ILogging
    {
        public void Log(string message, string type)
        {
            if(type == "error")
            {
                Console.WriteLine("Error - " +message);
            }
            else
            {
                Console.WriteLine(message);
            }
        }
    }
}

2. register a service using lifetime

// In Program.cs 
builder.Services.AddSingleton<ILogging, Logging>();

3. Use in controller

namespace WebApplication1.Controllers
{
    
    [Route("api/[controller]")]
    [ApiController]
    public class VillaController : ControllerBase
    {
        // Inject logging via constrouctor
        private readonly ILogging _logger;

        public VillaController(ILogging logger)
        {
            _logger = logger;
        }

        [ProducesResponseType(StatusCodes.Status200OK)]
        [HttpGet]
        public ActionResult<IEnumerable<VillaDTO>>GetVillas()
        {
            // Implement in Http Method 
            _logger.Log("Getting all Villa.", "");
            // _logger.Log("Get villa Error with id: " + id, "error");
            return Ok(VillaStore.VillaList);
        }
        

more details: in i want to use another Logging class LoggingV2 we can do as well

step1: Create LoggingV2 class which Implemets same ILogging Interface.

namespace WebApplication1.Logging
{
    public class LoggingV2 : ILogging
    {
        public void Log(string message, string type)
        {
            if(type == "error")
            {
                Console.BackgroundColor = ConsoleColor.Red;
                Console.WriteLine("Error - "+ message);
                Console.BackgroundColor= ConsoleColor.Black;
            }else if(type == "warning")
            {
                Console.BackgroundColor = ConsoleColor.DarkYellow;
                Console.WriteLine("Warning - " + message);
                Console.BackgroundColor = ConsoleColor.Black;
            }
            else
            {
                Console.WriteLine(message);
            }
        }
    }
}

step 2: register service using lifecycle

//builder.Services.AddSingleton<ILogging, Logging>();
builder.Services.AddSingleton<ILogging, LoggingV2>();

DONE ✅✅

Comments

Popular posts from this blog