Adding usernames and urls to the Sitecore default Log files

When troubleshooting issues, the log files become one of our best allies, however, sometimes the information we get is not enough to clearly pinpoint the issue. Therefore, adding some additional information to the errors can be beneficial. In this post, I will show how you can create a custom LogFileAppender to use instead of the default one used with an OOTB Sitecore 9.1 installation.

First, let's check how the default LogFileAppender is configured in a Sitecore 9.1 instance. If we check the Sitecore.config we'll see something like this: 


 <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender, Sitecore.Logging">  
       <file value="$(dataFolder)/logs/log.{date}.{time}.txt" />  
       <appendToFile value="true" />  
       <rollingStyle value="Size" />  
       <maxSizeRollBackups value="-1" />  
       <maximumFileSize value="10MB" />  
       <layout type="log4net.Layout.PatternLayout">  
           <conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n" />  
       </layout>  
       <encoding value="utf-8" />  
      </appender>  

As we can see, the type is type="log4net.Appender.RollingFileAppender, Sitecore.Logging" , let's now create our custom Appender that will inherit from the RollingFileAppender used by the default Appender:

 namespace Foundation.Accounts.Loggers  
 {  
   public class CustomerInfoSitecoreLogFileAppender : log4net.Appender.RollingFileAppender  
   {  
     protected AuthenticatedUserRepository AuthenticatedUserRepository { get; }  
     public CustomerInfoSitecoreLogFileAppender()  
     {   
       AuthenticatedUserRepository = ServiceLocator.ServiceProvider.GetService<AuthenticatedUserRepository>();  
     }  
     protected override void Append(log4net.spi.LoggingEvent loggingEvent)  
     {  
       // Only add customer data for ERROR and FATAL entries  
       if (!IsValidSite(Sitecore.Context.Site) ||  
         loggingEvent.Level != log4net.spi.Level.ERROR  
         && loggingEvent.Level != log4net.spi.Level.FATAL)  
       {  
         base.Append(loggingEvent);  
         return;  
       }  
       var user = AuthenticatedUserRepository.GetAuthenticatedUser();  
        
       log4net.spi.LoggingEventData data = loggingEvent.GetLoggingEventData();  
       var username = string.IsNullOrEmpty(user?.Name) ? "User not logged in" : user.Name;  
       data.Message += $"\r\nLogin: {username}\r\nUrl: {HttpContext.Current?.Request.Url.AbsoluteUri}";  
       log4net.spi.LoggingEvent ev = new log4net.spi.LoggingEvent(data);  
       base.Append(ev);  
         
     }  
     private bool IsValidSite(SiteContext siteContext)  
     {  
       return siteContext?.Name!=null && !siteContext.Name.Equals("shell", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("login", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("admin", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("service", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("modules_shell", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("modules_website", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("website", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("schedule", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("system", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("publisher", StringComparison.OrdinalIgnoreCase);  
     }  
   }  
 }  

Let's now break this code into smaller pieces. First, let's remember our goal which is to add additional details to the default log, like username and context URL. We don't need to add additional information if the log level is INFO, WARNING, etc. That's why we use this piece of code:

 if (!IsValidSite(Sitecore.Context.Site) ||   
      loggingEvent.Level != log4net.spi.Level.ERROR   
      && loggingEvent.Level != log4net.spi.Level.FATAL)   
     {   
      base.Append(loggingEvent);   
      return;   
     }   

As we can see in the snippet above, we are also checking if the site is valid, we only want to add additional details if the context site is our website, therefore, I've added an additional method that checks if the site is shell, admin, scheduler, etc.

 private bool IsValidSite(SiteContext siteContext)   
    {   
     return siteContext?.Name!=null && !siteContext.Name.Equals("shell", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("login", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("admin", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("service", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("modules_shell", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("modules_website", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("website", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("schedule", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("system", StringComparison.OrdinalIgnoreCase) && !siteContext.Name.Equals("publisher", StringComparison.OrdinalIgnoreCase);   
    }   

Now that we have filtered out unneeded log entries, we can proceed to add the additional info we need:

     var user = AuthenticatedUserRepository.GetAuthenticatedUser();   
     log4net.spi.LoggingEventData data = loggingEvent.GetLoggingEventData();   
     var username = string.IsNullOrEmpty(user?.Name) ? "User not logged in" : user.Name;   
     data.Message += $"\r\nLogin: {username}\r\nUrl: {HttpContext.Current?.Request.Url.AbsoluteUri}";   
     log4net.spi.LoggingEvent ev = new log4net.spi.LoggingEvent(data);   
     base.Append(ev);   
        

The last piece is to patch the Sitecore.config so that our new LogFileAppender is used:

 <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:env="http://www.sitecore.net/xmlconfig/env/">  
  <sitecore>  
   <log4net>  
     <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender, Sitecore.Logging">  
       <patch:attribute name="type">YOUR.NAMESPACE.CustomerInfoSitecoreLogFileAppender, YOUR.DLL</patch:attribute>  
     </appender>  
   </log4net>  
  </sitecore>  
 </configuration>  

All this will basically allow you to add the username and context url to all the entries with level ERROR and FATAL to your Sitecore logs.

Happy Sitecoring!

Comments