Logging Exceptions in ASP.NET Core Web API

Table of Contents

Logging Exceptions in ASP.NET Core is a very important thing even in any web application. When we have a lot of users on our application so it will be very easy for us to track the exception and solve this by simply checking the exception records. We do not have to test a lot to find the error.

In this tutorial, we will learn how we can log errors into our database in the ASP.NET Core Web API project.

Tools:

We are using Visual Studio Community 2019, .NET Core version 5, and MSSQL Database.

Lets start:

To add exception logs we have to create a table in the database. Create a class/model.

public class ExceptionLog
    {
        public Guid Id { get; set; }
        public Guid? UserId { get; set; }
        public DateTime DataTime { get; set; }
        public string Reference { get; set; }
        public string IPInfo { get; set; }
        public string ErrorDescription { get; set; }
        public string Data { get; set; }
        public string StackTrace { get; set; }
    }

And Add this to “ApplicationDbContext.cs”.

public DbSet<ExceptionLog> ExceptionLogs { get; set; }

Now we have to create a service that will be called on every exception and save the exception logs into the database.

Create an interface.

public interface IExceptionServices
    {
        public Task<ResponseViewModel<object>> CreateLog(Exception ex, object requestBody);
    }

In this interface, we have a signature of the method that is taking two parameters as arguments one is exception and other is object.

Let’s implement this method.

public class ExceptionServices:IExceptionServices
    {
        public IConfiguration _configuration { get; }
        private readonly ApplicationDbContext _context;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly UserManager<ApplicationUser> userManager;
        public ExceptionServices(ApplicationDbContext context,
            IHttpContextAccessor httpContextAccessor,
            IConfiguration configuration,
            UserManager<ApplicationUser> userManager
            )
        {
            _context = context;
            _httpContextAccessor = httpContextAccessor;
            _configuration = configuration;
            this.userManager = userManager;
        }
        public async Task<ResponseViewModel<object>> CreateLog(Exception ex, object requestBodyJson)
        {
            var exceptionLogObj = new ExceptionLog();
            exceptionLogObj.Data = JsonConvert.SerializeObject(requestBodyJson, new JsonSerializerSettings
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            });
            exceptionLogObj.DataTime = DateTime.Now;
            exceptionLogObj.ErrorDescription = ex.InnerException != null ? ex.InnerException.Message : 
            ex.Message;
            exceptionLogObj.IPInfo = 
            _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
            exceptionLogObj.StackTrace = ex.StackTrace;
            if (!string.IsNullOrEmpty(_httpContextAccessor.HttpContext.User.Identity.Name))
            {
                var user = await 
                this.userManager.FindByNameAsync(this._httpContextAccessor.HttpContext.User.Identity.Name);
                exceptionLogObj.UserId = user.Id;
            }
            exceptionLogObj.Reference = $"{_httpContextAccessor.HttpContext.Request.Path} , 
            {_httpContextAccessor.HttpContext.Request.Method}";
            // Or you can also instantiate inside using
            //using (ApplicationDbContext context = new ApplicationDbContext(null, optionsBuilder.Options))
            //{
            this._context.Add(exceptionLogObj);
            await this._context.SaveChangesAsync();
            //}
            return new ResponseViewModel<object>
            {
                Status = false,
                Message = ex.Message,
                Data = null,
                StatusCode = System.Net.HttpStatusCode.BadRequest.ToString()
            };
        }
    }

In this service, we are getting the exception and the other data like error description, IP Address, and stack trace and saving all the data into the database.

Now configure this service into the “Startup.cs” class.

services.AddTransient<IExceptionServices, ExceptionServices>();

After configuring this service into the startup class. We will inject this service into our all services or controllers so that we can save the exceptions logs.

public class CityServices: ICityServices
    {
        private readonly ApplicationDbContext _context;
        private readonly IExceptionServices _exceptionServices;
        private readonly IConfiguration configuration;

        public CityServices(ApplicationDbContext context,IExceptionServices exceptionServices,IConfiguration config)
        {
            _context = context;
            _exceptionServices = exceptionServices;
            configuration = config;
        }
        public async Task<ResponseViewModel<object>> UploadData(IFormFile file)
        {
            try
            {

                
                var fileextension = Path.GetExtension(file.FileName);
                var filename = Guid.NewGuid().ToString() + fileextension;
                var filepath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot","uscities", filename);
                
                using (FileStream fs = System.IO.File.Create(filepath))
                {
                    file.CopyTo(fs);
                }
                if(fileextension == ".csv")
                {
                    await AddCsv(filepath);
                }
                else
                {
                   await AddExcel(filepath);
                }
                return new ResponseViewModel<object>
                {
                    Status = true,
                    Message = "Cities Updated Successfully",
                    StatusCode = System.Net.HttpStatusCode.OK.ToString()
                };
            }
            catch (Exception e)
            {
                await _exceptionServices.CreateLog(e, null);
                throw e;
            }
        }
}

Now before throwing an exception we will call the method to save exception. Like we have described above.

Conclusion:

There are many approaches to do this save exception logs. But this is the most optimized and professional approach to do this task. Try this tutorial and if you do not understand anything or face any issue while applying this method. Do not hesitate to comment below. MYCODEBIT team will try to respond ASAP.

Leave a Reply

Your email address will not be published.