Custom Error Handling in C#
An exception is an abnormal condition that occurs during program execution. In other way ,It represents a process for handling run-time errors. Exception in c# allows your program to respond to these issues without crashing and provides a way to transfer control from one part of a program to another.
There are many ways we can describes the error. so, error can generally be classified into two main types.
Compile Time Error:
These types of error generally occur during the compilation of the program, means code has syntax issue or other problem that happen
Syntax issue: basically, it can be mistake in the code that violate the language grammar like missing semicolon unmatched parentheses.
Type Errors: Assigning a value of one type to a variable of another incompatible type.
Name Errors: Using undeclared variables or classes.
Run Time Error:
These type occue generally occure while the program is running ,due to unexpected· DivideByZeroException: Occurs when attempting to divide by zero.
NullReferenceException: Occurs when trying to access a member of a null object.
IndexOutOfRangeException: These exception Occurs when trying to access an element outside the bounds of an array or collection.
FileNotFoundException: These exception Occurs when trying to access a file that does not exist. Condition
Logical Error:
Logical error is type of error in program that causes it to produce incorrect outout or behavior even program compile successfully without any error.
There are some way where we can manage exception:
Simple Type
In this type we can manage exception using try and catch block .
Try:
Tyr block we can place actual code that might throw an error inside a try
block. This is where you anticipate problems.
try {
// Code that might throw an exception
int result = 20 / 0; // This will cause a divide by zero error
}
Catch
We can define catch block . this block catch the exception through by try block;
catch (DivideByZeroException ex) {
Console.WriteLine("You can't divide by zero!");
}
Or
catch (Exception ex) {
Console.WriteLine("You can't divide by zero!");
}
Finally Block (optional): This block runs regardless of whether an exception occurred or not. It’s often used for cleanup tasks.
finally {
Console.WriteLine("This runs no matter what.");
}
Example:
try {
int result = 10 / 0; // This will throw an exception
}
catch (DivideByZeroException ex) {
Console.WriteLine("You can't divide by zero!"); // Handle the error
}
catch (Exception ex) {
Console.WriteLine("You can't divide by zero!"); // Handle the error
}
finally {
Console.WriteLine("Cleanup code can go here."); // Always runs
}
Global Exception handling customs way
in this example we are going to use ASP .Net core 8 web api
STEP 1: Create a asp.net core web api
Step 2 In this step, create another project named Logger
and add a class file.
In this step, create two classes: one interface named ILoggerManager
and another class file named LoggerManager
. Install the package named NLog
ILoggerManager:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Logger
{
public interface ILoggerManager
{
void LogInfo(string message);
void LogWarn(string message);
void LogDebug(string message);
void LogError(string message);
}
}
LoggerManager
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Logger
{
public class LoggerService
{
public class LoggerManager : ILoggerManager
{
private static ILogger logger = LogManager.GetCurrentClassLogger();
public LoggerManager()
{
}
public void LogDebug(string message)
{
logger.Debug(message);
}
public void LogError(string message)
{
logger.Error(message);
}
public void LogInfo(string message)
{
logger.Info(message);
}
public void LogWarn(string message)
{
logger.Warn(message);
}
}
}
}
STEP 4: In main application create a folder name like any Extension and create a one file name ExceptionMiddlewareExtensions
using Logger;
using Microsoft.AspNetCore.Diagnostics;
using System.Net;
namespace ExceptionHandeling.Extension
{
public static class ExceptionMiddlewareExtensions
{
public static void ConfigureExceptionHandler(this IApplicationBuilder app, ILoggerManager logger)
{
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature != null)
{
string rootLogFolder = "Logs"; // Change this to your desired root log folder path
string monthFolderName = DateTime.Now.ToString("yyyy-MM"); // Get the current year and month as "yyyy-MM" format
string logFilePath = Path.Combine(rootLogFolder, monthFolderName, "error.log");
// Ensure the directory for the log file exists
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
using (StreamWriter writer = new StreamWriter(logFilePath, true))
{
writer.WriteLine(); //
writer.WriteLine($"Date and Time: {DateTime.Now}");
writer.WriteLine($"Error: {contextFeature.Error}");
writer.WriteLine($"Path: {contextFeature.Path}");
writer.WriteLine($"Endpoint: {contextFeature.Endpoint}");
writer.WriteLine("******************************************************************"); //
}
await context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error",
}.ToString());
}
});
});
}
public static void ConfigureCustomExceptionMiddleware(this IApplicationBuilder app)
{
// app.UseMiddleware<ExceptionMiddleware>();
}
}
}
Create a model class name ErrorDetails
using System.Text.Json;
using System.Text.Json;
namespace ExceptionHandeling.Models
{
public class ErrorDetails
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return JsonSerializer.Serialize(this);
}
}
}
Note: This code sets up a global error handler for an ASP.NET Core application. An unhandled exception logs error details to a file organized by year and month. A generic “Internal Server Error” message is sent back to the client. This approach maintains stability and provides information for debugging.
Step 5: in program.cs add these file
builder.Services.AddSingleton();
var logger = app.Services.GetRequiredService();
app.ConfigureExceptionHandler(logger);
Step: 6 Run the program and see the output
Exception Handling in ASP MVC 5
In this example either we can handel exception using inbuild attribute like [handelError]
or we can create a custom exception handling
Create a custom file name LogCustomExceptionFilter
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace elic.CustomFilter
{
public class LogCustomExceptionFilter : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
var exceptionMessage = filterContext.Exception.Message;
var stackTrace = filterContext.Exception.StackTrace;
var controllerName = filterContext.RouteData.Values["controller"].ToString();
var actionName = filterContext.RouteData.Values["action"].ToString();
string Message = "Date :" + DateTime.Now.ToString() + ", Controller: " + controllerName + ", Action:" + actionName +
"Error Message : " + exceptionMessage
+ Environment.NewLine + "Stack Trace : " + stackTrace;
//saving the data in a text file called Log.txt
//You can also save this in a dabase
File.AppendAllText(HttpContext.Current.Server.MapPath("~/Log/Log.txt"), Message);
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult()
{
ViewName = "Error"
};
}
}
}
}
You can call customs filter in either controller level action level or Globally throughout the application .
For Controller you can import simply; [LogCustomExceptionFilter].
External libraries or frameworks that enhance exception handling, like NLog.
for more details: apitpoint
GitHub Repository