This post is split into two parts:
- Part 1: The custom appender for log4net (this post)
- Part 2: The custom ASR report for viewing the logs including a custom detail screen
A Note of Namespace
You will need a reference to Sitecore.Logging. The namespaces for the log4net classes are all in log4net (e.g. log4net.Appender) but they exist in the Sitecore.Logging.dll file. Don’t get confused and add the log4net.dll file. This is not the correct library. All the namespaces and class names are the same or similar, but they will not work as expected.Building an ADONetAppender
Out of the box, Sitecore comes with an Appender named ADONetAppender (if you have just AdoNetAppender - you have the wrong DLL). Using this Appender is just fine, but I wanted to include some more fields. Originally I had done it in a helper class that I would call before logging an error. But by creating my own Appender, I could handle it all in one place.The first thing I did was to inherit from the built in appender:
public class AdoNetAppender : log4net.Appender.ADONetAppender {
protected override void Append(log4net.spi.LoggingEvent loggingEvent) {
log4net.helpers.PropertiesCollection col = loggingEvent.Properties;
col["{PROPERTYNAME}"] = “Value”;
base.Append(loggingEvent);
    public class AdoNetAppender : log4net.Appender.ADONetAppender {
        public string connectionStringName { get; set; }
        public override void ActivateOptions() {
            this.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
            base.ActivateOptions();
        }
        private static Language tryParseLanguage(string name) {
            Sitecore.Diagnostics.Assert.ArgumentNotNull((object)name, "name");
            Language result;
            if (tryParseLanguage(name, out result))
                return result;
            throw new ArgumentException(string.Format("Could not parse the language '{0}'. Note that a custom language name must be on the form: isoLanguageCode-isoRegionCode-customName. The language codes are two-letter ISO 639-1, and the regions codes are are two-letter ISO 3166. Also, customName must not exceed 8 characters in length. Valid example: en-US-East. For the full list of requirements, see: http://msdn2.microsoft.com/en-US/library/system.globalization.cultureandregioninfobuilder.cultureandregioninfobuilder.aspx", (object)name));
        }
        private static bool tryParseLanguage(string name, out Language result) {
            Sitecore.Diagnostics.Assert.ArgumentNotNull((object)name, "name");
            result = (Language)null;
            if (name.Equals("__Standard Values", StringComparison.OrdinalIgnoreCase) || name.Equals("__language", StringComparison.OrdinalIgnoreCase) || !LanguageManager.IsValidLanguageName(name))
                return false;
            if (LanguageManager.LanguageRegistered(name) || LanguageManager.RegisterLanguage(name)) {
                return true;
            }
            return true;
        }
        private Language getLanguage() {
            Language language1 = Sitecore.Context.Items["sc_Language"] as Language;
            if (language1 != null)
                return language1;
            string name = string.Empty;
            Sitecore.Sites.SiteContext site = Sitecore.Context.Site;
            if (site != null) {
                name = WebUtil.GetCookieValue(site.GetCookieKey("lang"), site.Language);
                Assert.IsNotNull((object)name, "languageName");
            }
            if (name.Length == 0)
                name = Settings.DefaultLanguage;
            try {
                Language language2 = tryParseLanguage(name);
                if (Sitecore.Configuration.State.Sites.IsSiteResolved)
                    Sitecore.Context.Items["sc_Language"] = (object)language2;
                return language2;
            } catch (Exception) { }
            return null;
        }
        protected override void Append(log4net.spi.LoggingEvent loggingEvent) {
            log4net.helpers.PropertiesCollection col = loggingEvent.Properties;
            if (!object.ReferenceEquals(Environment.MachineName, null)) {
                col["machine"] = Environment.MachineName;
            }
            HttpContext context = null;
            User currentUser = null;
            Item contextItem = null;
            Language language = null;
            try { context = HttpContext.Current; } catch { }
            try { currentUser = Sitecore.Context.User; } catch { }
            try { contextItem = Sitecore.Context.Item; } catch { }
            if (loggingEvent.LoggerName != "System.RuntimeType") {
                try { language = Sitecore.Context.Language; } catch { }
                if (language != null) {
                    col["Language"] = language;
                }
            }
            try {
                if (currentUser != null) {
                    col["CurrentUser"] = currentUser.DisplayName;
                    var roles = "";
                    foreach (var role in currentUser.Roles) {
                        roles += role.DisplayName + ",";
                    }
                    roles = roles.Substring(0, roles.Length - 1); // remove last comma
                    col["Roles"] = roles;
                }
            } catch { }
            if (contextItem != null) {
                col["SitecoreItemID"] = contextItem.ID.ToString();
                col["SitecoreItemName"] = contextItem.Name;
            }
            if (context != null) {
                try {
                    string ipAddress = context.Request.ServerVariables["HTTP_CLIENT_IP"];
                    if (String.IsNullOrEmpty(ipAddress)) {
                        ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                    }
                    if (String.IsNullOrEmpty(ipAddress)) {
                        ipAddress = context.Request.ServerVariables["REMOTE_ADDR"];
                    }
                    if (!String.IsNullOrEmpty(ipAddress)) {
                        col["IpAddress"] = ipAddress;
                    }
                    if (!String.IsNullOrEmpty(context.Request.ServerVariables["X-FORWARDED-FOR"])) {
                        col["ForwardedIpAddress"] = context.Request.ServerVariables["X-FORWARDED-FOR"];
                    }
                    string referrer = context.Request.ServerVariables["HTTP_REFERER"];
                    col["HttpReferrer"] = referrer;
                    string currentUrl = context.Request.Url.AbsoluteUri;
                    col["HttpUrl"] = currentUrl;
                    string method = context.Request.HttpMethod;
                    col["HttpMethod"] = method;
                    if (method.ToLower() == "post") {
                        var formVariables = "";
                        if (context.Request.Form.Count > 0) {
                            foreach (string key in context.Request.Form) {
                                var value = context.Request.Form[key];
                                formVariables += "|   " + key + " : " + value.ToString() + System.Environment.NewLine;
                            }
                        }
                        col["FormVariables"] = formVariables;
                    }
                    HttpCookieCollection cookies = context.Request.Cookies;
                    string userAgent = context.Request.ServerVariables["HTTP_USER_AGENT"];
                    col["HttpUserAgent"] = userAgent;
                    string queryString = context.Request.ServerVariables["QUERY_STRING"];
                    if (!String.IsNullOrEmpty(queryString)) {
                        col["HttpQueryString"] = queryString;
                    }
                    if (cookies != null && cookies.Count > 0) {
                        var strCookies = "";
                        for (int c = 0; c < cookies.Count; c++) {
                            HttpCookie cookie = cookies.Get(c);
                            strCookies += String.Format(CultureInfo.CurrentCulture, "|  HTTP Cookie: {0} = {1}{2}", cookie.Name, cookie.Value, Environment.NewLine);
                        }
                        col["HttpCookies"] = strCookies;
                    }
                } catch { }
            }
            base.Append(loggingEvent);
        }
    }
The log4net configuration
Next I needed to add an appender into the log4net configuration section. I did this in the usual Sitecore include file. It belongs under configuration/sitecore/log4net and references the custom AdoNetAppender class I created:<appender name="AdoNetAppender" type="Rizzi.SC.AdoLogging.Appenders.AdoNetAppender, Rizzi.SC.AdoLogging">
<level value="ERROR" />
<param name="ConnectionType" value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<param name="connectionStringName" value="log4net"/>
Next is the CommandText. This is the insert statement for inserting the actual data into the table:
<param name="CommandText" value="INSERT INTO [Log]([Date],[Thread],[Level],[Logger],[Message],[Exception],[MachineName], [CurrentUser], [Roles], [SitecoreItemID], [SitecoreItemName], [Language], [IpAddress], [ForwardedIpAddress], [HttpReferrer], [HttpUrl], [HttpMethod], [FormVariables], [HttpUserAgent], [HttpQueryString], [HttpCookies]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception, @MachineName, @CurrentUser, @Roles, @SitecoreItemID, @SitecoreItemName, @Language, @IpAddress, @ForwardedIpAddress, @HttpReferrer, @HttpUrl, @HttpMethod, @FormVariables, @HttpUserAgent, @HttpQueryString, @HttpCookies)" />
<param name="Parameter">
<param name="ParameterName" value="@log_date" />
<param name="DbType" value="DateTime" />
<param name="DbType" value="String" />
<param name="Size" value="255" />
The ConverionPattern starts with a % variable. They all seem to be one character (regardless of what the documentation might say). For example. %t is the tread, %p is the loglevel, etc. And, yes, the letters don’t seem to have any bearing on what they do. They are case sensitive.
For the Log Level:
<param name="Layout" type="log4net.Layout.PatternLayout">
    <param name="ConversionPattern" value="%p" />
</param>
<param name="Layout" type="log4net.Layout.PatternLayout">
    <param name="ConversionPattern" value="%d{yyyy'-'MM'-'dd HH':'mm':'ss'.'fff}" />
</param>
<param name="ConversionPattern" value="%P{MachineName}" />
<root>
            <appender-ref ref="AdoNetAppender" />
</root>
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <log4net>
            <appender name="AdoNetAppender" type="Rizzi.SC.AdoLogging.Appenders.AdoNetAppender, Rizzi.SC.AdoLogging">
                <!--<appender name="AdoNetAppender" type="log4net.Appender.ADONetAppender">-->
                <!--<bufferSize value="1" />-->
                <level value="ERROR" />
                <param name="ConnectionType" value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
                <param name="connectionStringName" value="log4net"/>
                <param name="CommandText" value="INSERT INTO [Log]([Date],[Thread],[Level],[Logger],[Message],[Exception],[MachineName], [CurrentUser], [Roles], [SitecoreItemID], [SitecoreItemName], [Language], [IpAddress], [ForwardedIpAddress], [HttpReferrer], [HttpUrl], [HttpMethod], [FormVariables], [HttpUserAgent], [HttpQueryString], [HttpCookies]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception, @MachineName, @CurrentUser, @Roles, @SitecoreItemID, @SitecoreItemName, @Language, @IpAddress, @ForwardedIpAddress, @HttpReferrer, @HttpUrl, @HttpMethod, @FormVariables, @HttpUserAgent, @HttpQueryString, @HttpCookies)" />
                <param name="Parameter">
                    <param name="ParameterName" value="@log_date" />
                    <param name="DbType" value="DateTime" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%d{yyyy'-'MM'-'dd HH':'mm':'ss'.'fff}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@thread" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%t" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@log_level" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="50" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%p" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@logger" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%c" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@message" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="-1" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%m" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@exception" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="-1" />
                    <layout type="log4net.Layout.ExceptionLayout" />
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@MachineName" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="4000" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{MachineName}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@CurrentUser" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{CurrentUser}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@Roles" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{Roles}" />
                    </param>
                </param>
                <parameter>
                    <parameterName value="@SitecoreItemID" />
                    <dbType value="String" />
                    <size value="255" />
                    <layout type="log4net.Layout.PatternLayout">
                        <conversionPattern value="%P{SitecoreItemID}" />
                    </layout>
                </parameter>
                <param name="Parameter">
                    <param name="ParameterName" value="@SitecoreItemName" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{SitecoreItemName}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@Language" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{Language}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@IpAddress" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{IpAddress}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@ForwardedIpAddress" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{ForwardedIpAddress}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@HttpReferrer" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="-1" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{HttpReferrer}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@HttpUrl" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="-1" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{HttpUrl}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@HttpMethod" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{HttpMethod}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@FormVariables" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="-1" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{FormVariables}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@HttpUserAgent" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="255" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{HttpUserAgent}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@HttpQueryString" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="-1" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{HttpQueryString}" />
                    </param>
                </param>
                <param name="Parameter">
                    <param name="ParameterName" value="@HttpCookies" />
                    <param name="DbType" value="String" />
                    <param name="Size" value="-1" />
                    <param name="Layout" type="log4net.Layout.PatternLayout">
                        <param name="ConversionPattern" value="%P{HttpCookies}" />
                    </param>
                </param>
            </appender>
            <root>
                <appender-ref ref="AdoNetAppender" />
            </root>
        </log4net>
    </sitecore>
</configuration>
Creating the database
Here is the schema of the table:CREATE TABLE [dbo].[Log](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [Date] [datetime] NOT NULL,
    [Thread] [varchar](255) NOT NULL,
    [Level] [varchar](255) NOT NULL,
    [Logger] [varchar](255) NOT NULL,
    [Message] [varchar](max) NOT NULL,
    [Exception] [varchar](max) NOT NULL,
    [MachineName] [varchar](4000) NOT NULL,
    [CurrentUser] [varchar](255) NOT NULL,
    [Roles] [varchar](1000) NOT NULL,
    [SitecoreItemID] [varchar](255) NOT NULL,
    [SitecoreItemName] [varchar](255) NOT NULL,
    [Language] [varchar](255) NOT NULL,
    [IpAddress] [varchar](255) NOT NULL,
    [ForwardedIpAddress] [varchar](255) NOT NULL,
    [HttpReferrer] [varchar](max) NOT NULL,
    [HttpUrl] [varchar](max) NOT NULL,
    [HttpMethod] [varchar](255) NOT NULL,
    [FormVariables] [varchar](max) NOT NULL,
    [HttpUserAgent] [varchar](255) NOT NULL,
    [HttpQueryString] [varchar](max) NOT NULL,
    [HttpCookies] [varchar](max) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Troubleshooting and Log4net’s Log
After completing all of this, you may find yourself in a place where nothing is being logged to the table. Here are a few things you can do.- 
Try adding <bufferSize value="1" />to the appender configuration file. This will make sure records are written right away. For me, this didn’t seem to make a difference either way, so mine is commented at the moment.
 
- 
Enable Log4net’s log. This requires two configuration changes:
Add the following Application Setting (not a Sitecore setting):
 
<add key="log4net.Internal.Debug" value="true" />
<system.diagnostics>
        <trace autoflush="true">
            <listeners>
                <add    name="textWriterTraceListener"
            type="System.Diagnostics.TextWriterTraceListener"
initializeData="[PATH]\data\log4net.txt" />
            </listeners>
        </trace>
    </system.diagnostics>
 
How do you access to log files on SQL? Do you use some Windows client, Web application or just SQL Enterprise Manager?
ReplyDeleteYou can use SQL Enterprise Manager - and as someone very familiar with T-SQL, I usually do - but for the Sitecore users I created a custom report using the Advanced System Reporter. This will be covered in part 2 of this guide.
Deletehttp://www.rizzifrank.com/2016/10/sitecore-log4net-ado-logging-part-2-asr.html
DeleteThis comment has been removed by a blog administrator.
ReplyDeleteObviously, this will depend on the cards you’ve been dealt. For instance, in the hand proven here, your best option|the greatest choice|your only option} would be to keep the 4 thecasinosource.com and go for a straight flush. Other options embrace maintaining the lowest pair or maintaining the 2 suited cards. However, these different options are not as excessive paying as a straight flush, which pays 50. As said before, charts are your greatest pal phrases of|in relation to} determining which hands will work greatest in your favour.
ReplyDelete