Save Activity Log in ASP.NET Core Web API like a Pro

In Any Web Application activity logs are the most important and common thing. Today we will learn how can we save activity log in ASP.NET Core Web API project. There are many ways to perform this task but we are going to discuss one of the efficient ways with you.

We will keep record of activity logs by overriding our savechanges() and savechangesasync() method.

Tools:

For this project, I’m using Visual studio community 2019, windows os, and .NET Core Version 5.

Let’s Start:

To save logs we have to create a table in our database. Create a class and name it “Activitylog.cs”.

public class ActivityLog 
    {
        public Guid Id { get; set; }
        public Guid? UserId { get; set; }
        public DateTime? Date { get; set; }
        public string Operation { get; set; }
        public string TableName { get; set; }
        public string EntityId { get; set; }
        public string EntityDataJson { get; set; }
    }

In this class we have multiple properties like UserId for who performed the action, Date when performed the action, Opertion what the operation was, TableName in which table, EntityId on which record action has been performed, and EntityDataJson to save all operation Data.

Now we have to override our SaveChangesAsync() and SaveChanges() method. So whenever any operation will be performed on database and we call SaveChanges() or SaveChangesAsync() to save the changes into our database the Activity log will be automatically saved.

Lets open our “ApplicationDbContext.cs” class.  And create a private method that will save all activity logs into the database and we will call this function into both SaveChanges() or SaveChangesAsync() functions.

private void OnBeforeSaving()
        {
            foreach (var entry in ChangeTracker.Entries().ToList())
            {
                if (entry.Entity.GetType().Name != "IdentityUserToken`1" 
                    && entry.Entity.GetType().Name != "SessionLog" 
                    && entry.Entity.GetType().Name != "EmailTemplate" 
                    && entry.Entity.GetType().Name != "EmailPlaceholder"
                    && entry.Entity.GetType().Name != "ApplicationRole" 
                    && entry.Entity.GetType().Name != "ExceptionLog"
                    && entry.Entity.GetType().Name != "IdentityUserRole`1"
                    && entry.Entity.GetType().Name != "Module")
                {
                    if (entry.State == EntityState.Added)
                    {
                        var log = new ActivityLog();
                        log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                        {
                            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                        });
                        Guid userid;
                        var usercheck = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
                        if (!string.IsNullOrEmpty(usercheck))
                            userid = Guid.Parse(usercheck);
                        else
                            userid = new Guid();
                        if (userid != Guid.Empty)
                            log.UserId = userid;
                        log.EntityId = entry.CurrentValues["Id"].ToString();
                        log.Operation = "Add";
                        log.Date = DateTime.Now;
                        log.TableName = entry.Entity.GetType().Name;
                        this.ActivityLogs.Add(log);
                    }
                    else if (entry.State == EntityState.Modified)
                    {
                        if(entry.Entity.GetType().Name == "ApplicationUser" ||entry.Entity.GetType().Name == "UserInformation" || entry.Entity.GetType().Name == "State"|| entry.Entity.GetType().Name == "County"|| entry.Entity.GetType().Name == "City")
                        {
                            var log = new ActivityLog();
                            log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                            {
                                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                            });
                            Guid userid;
                            var usercheck = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
                            if (!string.IsNullOrEmpty(usercheck))
                                userid = Guid.Parse(usercheck);
                            else
                                userid = new Guid();
                            if (userid != Guid.Empty)
                                log.UserId = userid;
                            log.EntityId = entry.CurrentValues["Id"].ToString();
                            log.Operation = "Update";
                            log.Date = DateTime.Now;
                            log.TableName = entry.Entity.GetType().Name;
                            this.ActivityLogs.Add(log);
                        }
                        else
                        {
                            var deleteValue = entry.CurrentValues["Active"].ToString();
                            if (deleteValue == "False" || deleteValue == "false")
                            {
                                var log = new ActivityLog();
                                log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                                {
                                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                                });
                                var userid = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
                                    //userid = new Guid(entry.Property("UserId").CurrentValue.ToString());
                                if (!string.IsNullOrEmpty(userid))
                                    log.UserId = Guid.Parse(userid);
                                log.EntityId = entry.CurrentValues["Id"].ToString();
                                log.Operation = "Delete";
                                log.Date = DateTime.Now;
                                log.TableName = entry.Entity.GetType().Name;
                                this.ActivityLogs.Add(log);
                            }
                            else
                            {
                                var log = new ActivityLog();
                                log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                                {
                                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                                });
                                var userid = Guid.Parse(_httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
                                //var userid = new Guid(entry.Property("UserId").CurrentValue.ToString());
                                if (userid != Guid.Empty)
                                    log.UserId = userid;
                                log.EntityId = entry.CurrentValues["Id"].ToString();
                                log.Operation = "Update";
                                log.Date = DateTime.Now;
                                log.TableName = entry.Entity.GetType().Name;
                                this.ActivityLogs.Add(log);
                            }
                        }
                    }
                    else if (entry.State == EntityState.Deleted)
                    {
                        var log = new ActivityLog();
                        log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                        {
                            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                        });
                        var userid = 
                      Guid.Parse(_httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
                        log.UserId = userid;
                        if (!string.IsNullOrEmpty(entry.CurrentValues["Id"].ToString()))
                            log.EntityId = entry.CurrentValues["Id"].ToString();
                        log.Operation = "Delete";
                        log.Date = DateTime.Now;
                        log.TableName = entry.Entity.GetType().Name;
                        this.ActivityLogs.Add(log);
                    }
                }  
            }
        }

In this function, we are iterating on the change tracker class this class in the entity framework has all the information about changes that we have made with entities. After iterating this we will save the changes into the Activity logs.

Now, let’s call the function in the methods.

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
            CancellationToken cancellationToken = new CancellationToken())
        {
            OnBeforeSaving();
            return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
        }
        public override int SaveChanges(bool acceptAllChangesOnSuccess)
        {
            OnBeforeSaving();
            return base.SaveChanges(acceptAllChangesOnSuccess);
        }

After calling the private method whenever the save changes are applied after any entity manipulation. Activity will be saved with the user record. This is one of the optimized and professional approaches to do this.

Understanding Logging in ASP.NET Core Web API

When it comes to building web applications, logging plays a crucial role in ensuring that everything is running smoothly. Whether it’s tracking down an error or monitoring user activity, logging can provide valuable insights into the workings of your application. In this article, we’ll delve into the world of logging in ASP.NET Core Web API, and how to save activity logs like a pro.

To begin with, let’s define what logging is and why it’s important in web applications. Logging is the process of capturing events that occur within an application, such as errors, warnings, and information messages. These events can be used to troubleshoot issues, analyze user behavior, and monitor application performance.

There are different types of logs that can be generated, each with its own purpose. For instance, error logs capture information about errors that occur within an application, such as exceptions, crashes, or system failures. Information logs provide details about successful operations, while warning logs notify you of potential issues that could cause problems in the future.

ASP.NET Core Web API comes with a built-in logging framework that provides a range of features for capturing and storing logs. This framework offers a variety of logging providers that can be used to store logs in different formats, such as console, file, and database.

Configuring logging in ASP.NET Core Web API is a straightforward process. You can customize logging settings using the built-in logging middleware, which provides a set of options for controlling the logging behavior. For example, you can set the logging level to control the amount of detail captured, or configure log filtering to exclude certain events.

When it comes to logging levels, ASP.NET Core Web API provides a range of options, from the most verbose (Trace) to the most critical (Critical). Each level captures a different level of detail, allowing you to fine-tune your logging to match your specific needs.

Best Practices for Saving Activity Logs

Now that we’ve covered the basics of logging in ASP.NET Core Web API, let’s explore some best practices for saving activity logs. Following these best practices can help ensure that your logs are secure, performant, and easy to manage.

First and foremost, it’s important to log only what’s necessary. Logging too much information can quickly overwhelm your system, making it difficult to sift through logs to find the information you need. So, be sure to log only the events that are essential to your application.

Next, it’s important to secure your logs to prevent unauthorized access. Logs can contain sensitive information, such as user data or system configurations, so it’s crucial to ensure that only authorized personnel have access to them. This can be accomplished by encrypting logs or using access controls to restrict access to authorized personnel only.

Another important consideration is log retention. Retaining logs for too long can consume valuable disk space, while retaining them for too little time can make it difficult to troubleshoot issues that may have occurred in the past. Therefore, it’s important to define a log retention policy that meets your needs, based on factors such as the size of your system and your regulatory requirements.

When it comes to performance, it’s important to ensure that logging doesn’t impact the performance of your system. One way to do this is to use asynchronous logging, which can significantly reduce the impact of logging on your system’s performance. Additionally, you can optimize your logging code by using lightweight data structures and avoiding expensive operations.

Finally, it’s a good idea to centralize your logs. Storing logs in a centralized location can simplify log management, allowing you to easily search, filter, and analyze logs. Additionally, centralized logging can provide valuable insights into the behavior of your entire system, allowing you to identify trends and patterns that may be difficult to spot in individual logs.

Conclusion:

There are many approaches to do this. 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

Related Posts