Wednesday, April 6, 2022

How to resolve Global Exception Logger with Dependency Injection in ASP.NET Web API?

Introduction

In this article, you will learn how to resolve Global Exception Logger with dependency injection in ASP.NET Web API using Autofac.

How to resolve Global Exception Logger with dependency injection

Step 1: Create the custom exception logger by inheriting the IExceptionLogger interface to write your custom logging logic.

/// 
/// The main class GlobalExceptionLogger.
/// Handles the exception logging requests.
/// 
public class GlobalExceptionLogger : IExceptionLogger
{
    /// 
    /// 
    /// 
    public ILogger Logger { get; set; }
    /// 
    /// Initializes a new instance of the  class.
    /// 
    public GlobalExceptionLogger()
    {
        Logger = NullLogger.Instance;
    }
    /// 
    /// Logs the exception.
    /// 
    ///The exception context.
    ///
    /// 
    public async Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)
    {
        var ex = context.Exception;
        string message = $"{ex.Message}--{ex.Source}\n{ex.StackTrace}\n{ex.TargetSite}\n";
        await Task.Run(() =>
        {
            Logger.Error(ex, message);
        });
    }

}

Step 2: Register your custom exception logger class in the Autofac container to resolve it through dependency injection.

/// 
/// The main class AutofacConfig.
/// Provides Autofac DI configuration of the API.
/// 
public static class AutofacConfig
{

    #region Autofac Container
    private static Lazy builder =
      new Lazy(() =>
      {
          var autofacbuilder = new ContainerBuilder();
          RegisterTypes(autofacbuilder);
          return autofacbuilder.Build();
      });

    /// 
    /// Configured Autofac Container.
    /// 
    public static IContainer Container => builder.Value;
    #endregion

    /// 
    /// Registers the type mappings with the autofac container builder.
    /// 
    ///The autofac container builder to configure.
    /// 
    /// 
    public static void RegisterTypes(ContainerBuilder builder)
    {
       string baseDirectoryPath = AppDomain.CurrentDomain.BaseDirectory + "bin";
        if (!Directory.Exists(baseDirectoryPath))
            baseDirectoryPath = AppDomain.CurrentDomain.BaseDirectory;

        builder.RegisterModule(new LoggingModule());
        builder.RegisterModule(new FileStoreModule());
        //builder.RegisterModule(new CloudJobManager.CloudJobManagerModule());
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
        //builder.RegisterType().InstancePerLifetimeScope();

        var assemblies = Directory.EnumerateFiles(baseDirectoryPath, "*.dll", SearchOption.TopDirectoryOnly)
            .Where(filePath => Path.GetFileName(filePath).StartsWith("MyApp"))
            .Select(Assembly.LoadFrom).Where(assemblyType =>
            (assemblyType.FullName.StartsWith("MyApp") && !assemblyType.FullName.Contains("MyApp.Framework") &&
            !assemblyType.FullName.Contains("MyApp.Reporting.API")
            )).ToArray();

        builder.RegisterAssemblyTypes(assemblies)
        .AsImplementedInterfaces().InstancePerLifetimeScope();

        builder.RegisterType().AsSelf().AsImplementedInterfaces();
        builder.RegisterType().AsSelf().AsImplementedInterfaces();

        builder.RegisterType<ReportService>().As<IReportService>().InstancePerRequest();

    }
}

Step 3: Replace the custom exception logger in the HttpConfiguration services to use it in the place of the default ASP.NET exception logger.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        config.DependencyResolver = new AutofacWebApiDependencyResolver(AutofacConfig.Container);

        // Web API configuration and services
        //config.Services.Replace(typeof(IExceptionLogger), new  GlobalExceptionLogger());
        //config.Services.Replace(typeof(IExceptionHandler), new GenericExceptionHandler());

        // Inject our exception logger and handler
        config.Services.Replace(typeof(IExceptionHandler), config.DependencyResolver.GetService(typeof(GenericExceptionHandler)));
        config.Services.Replace(typeof(IExceptionLogger), config.DependencyResolver.GetService(typeof(GlobalExceptionLogger)));

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}
Conclusion

Whenever an unhandled error occurs then you have a chance to log it. The information regarding the can be stored somewhere for review. There you can write the issue to a log or write custom logic.