Sitecore Audit Trail – the easy way

One feature that my customers always ask me about, is the ability to know who as changed that specific page and when….

There are several reasons for this magic question, for example, the legal aspects of what you wrote on your website at a specific moment in time, could be used by customers to sue your client… a classic example is the T&C or privacy policy page that contains content provided by the legal team for an obvious reason…

In the case you are not aware and you think that customers forgot what you wrote, they may use the https://web.archive.org a kind of web history that keep track of most of the changes…

obviously, that’s a public tool, and unless you have not written the author name in the meta tag (quite unusual) you won’t know who changed that page…

Sitecore comes in your help with two OOTB approaches:

  1. Sitecore versions combined with Workflow will force your content author to create a new version of the item everytime that something got changed on an item so that you can have a more granular view of what has been changed from who… unfortunately versions do not play very well with publishing and obviously it increases the complexity and is harder to know if a specific version when exactly was published and when it was overseeded… in addition workflow are notoriously complex and make the team upset…
  2. History table, this is an OOTB feature being in place from Sitecore 6 until 9 (I think…) that keep all the changes that authors do to any item… Adam in his blog https://blog.coates.dk/2015/05/27/sitecore-history-table-how-to-control-how-long-the-entries-are-kept/ explain to you how to configure the duration period for keeping items in the history table… Note that from the history table there is no tool or UI that allow you to see what happened, but you need to go in the table on SQL and look for what you were searching for… obviously keeping history on the history table for 2-3 years, if you have a big team, it is going to affect the performance of your Sitecore instance…

However, my recommendation, in case you are on an enterprise project and workflow and versions are not an option is to log all the changes into a log file and then process the log file content in a NO SQL data repository… this approach comes with several advantages:

  1. it is scalable, you can have as many changes you want…
  2. it won’t affect performances of the CMS and it won’t pollute your DB
  3. it gives you data structured as you want… so that you can build your UI to see items changed today, or who is the content author that make more changes, review all the changes done from one user…
  4. Ability to have a free text search, eg. find out who has done any mis-spell and when on the web site…
  5. when an item was published and the content of the item
  6. it is super simple to hook into your code

the simpler way to get your logging hooked up is described in this brilliant blog: http://info.exsquared.com/ex-squared-blog/logging-changes-in-sitecore-made-by-content-authors

basically it attach to some Sitecore events, and use log4net to log on a text file, obviously you can configure it also on a different appender….

Next step, would be reading the Txt file with the audit information and upload the content on a NoSQL data structure for making it simple to search on…

Happy Logging, and end of excuses for content authors who update dodgy content 🙂

Advertisements

How to store Sitecore Logs on the ApplicationLog

There are several ways to store Sitecore Logs, depending on your specific scenario, one would fit better than an other.

Generally if your servers are monitored with products such SCOM or NewRelic that keep track of the application logs and allow you to define events and retention policy on the logs it make sense to write Sitecore Logs on the application log.

Configuring Sitecore to write the logs on the Application logs is pretty simple, what you need is to define a new Log4Net appender that point to  log4net.Appender.EventLogAppender, Sitecore.Logging

<log4net>
       <appender name =" SitecoreEventLogAppender" type="log4net.Appender.EventLogAppender, Sitecore.Logging"   xdt:Transform= "Insert " >
         <param name =" ApplicationName" value="SitecoreCD" />
         <layout type =" log4net.Layout.PatternLayout">
              <conversionPattern value =" %4t %d{ABSOLUTE} %-5p %m%n"/>
         </layout>
       </appender>
       <root xdt:Locator =" Condition(/configuration/log4net/root)" xdt:Transform="Replace" >
         <priority value =" INFO" />
         <appender-ref ref =" SitecoreEventLogAppender"  />
       </root>
</log4net>

Having said that there are some security issues to create for the first time an entry on the Application Log (for your application name) and you can either follow the Sitecore KB https://kb.sitecore.net/articles/124281 or you can decide to create the first entry on the application log with a console application running it as Administrator…. In the case you would go for the second option, don’t forget to change the Application Name to the value that you need….

Here you can download a simple Console Application to create the first log on the application Log.

Sitecore Logging – Log4Net – CastleWindsor

As most of you probably already know, Sitecore (until 6.6.0) use an old version of Log4Net in the Sitecore.Logging assembly, you can read more about this issue here

So there are 2 options:

1) Use Sitecore Logging as your logging strategy

2) Use CastleWindsor Logging facilities

In order to implement the second option you have to:

Implement your logging facility:

public class  XXXContainerInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container,
Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{

//Load your Custom Configuration from a different config “using a more advanced pattern expression

container.AddFacility&lt;Castle.Facilities.Logging.LoggingFacility&gt;(f =&gt; f.LogUsing(LoggerImplementation.Log4net).WithConfig(“log4net.xml”));

container.Register(
Component.For&lt;XXXX.ILoggingSetup&gt;()
.Named(“loggingSetup”)
.ImplementedBy&lt;XXXX.Log4net.Log4NetLoggingSetup&gt;()
.LifeStyle.Singleton,

}

}

Prepare your config:

<castle>
<facilities>
<facility id=”logging” type=”Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging” loggingApi=”log4net”/>
</facilities>
<components>
<component id=”loggingSetup” service=”XXX.ILoggingSetup, AssemblyName” type=”XXXX.Log4NetLoggingSetup, Asos.Framework” lifestyle=”AssemblyName”/>
<component id=”loggingContext” service=”XXX.ILoggingContext, AssemblyName” type=”XXXX.Log4NetLoggingContext, AssemblyName” lifestyle=”singleton”/>
</components>
</castle>

The Log4Net Section will still be required and shared between your Castle Logging and SiteCore Logging…

<section name=”log4net” type=”log4net.Config.Log4NetConfigurationSectionHandler, log4net”/>

<log4net>
<appender name=”eventLogAppender” type=”log4net.Appender.EventLogAppender, log4net” logName=”Application”>
<param name=”LogName” value=”Application”/>
<param name=”ApplicationName” value=”XXXXX”/>
<layout type=”log4net.Layout.PatternLayout”>
<conversionPattern value=”%appdomain [%thread] %-5level – %message%newline [%properties]%newline %exception”/>
</layout>
</appender>
<root>
<level value=”ERROR”/>
<appender-ref ref=”eventLogAppender”/>
</root>
</log4net>

Be also aware that the SitecoreLogging does not support the latest log4net formatting syntax like %exception

and Sitecore formatting expression prefer the OldLog4Net syntax with all abbreviated letters

<conversionPattern value=”%4t %d{ABSOLUTE} %-5p %m%n” />

More references about this issue can be found:

http://devlicio.us/blogs/casey/archive/2008/06/18/logging-with-castle-windsor-the-logging-facility-and-log4net.aspx

http://damikulik.blogspot.co.uk/2011/05/integrate-sitecorelogging-with-castle.html

Categories: