4

we have implemented a custom log and writing to a custom log file. The Problem is, that the log is created several times a day with filenames like this:

MyCustom.log.20161208.165109
MyCustom.log.20161208.165845
MyCustom.log.20161208.175134
MyCustom.log.20161208.184432

The Definition is like this:

    <appender name="MyCustomLogAppender" type="log4net.Appender.SitecoreLogFileAppender, Sitecore.Logging">
  <file value="$(dataFolder)/logs/MyCustom.log.{date}.txt" />
  <appendToFile value="true" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n" />
  </layout>
  <encoding value="utf-8" />
</appender>

Can anyone tell me what I need to configure, in order to receive just one file per day? Or in other words, WHEN does a new file get created?

Bgl86
  • 727
  • 8
  • 20
  • 2
    Does it happen on your local environment or prod? Is your app pool recycled? As long as your application is not restarted, there should be only 1 log file if you don't use rolling file appender. – Marek Musielak Dec 12 '16 at 11:43
  • Thank you Marek for that hint, I did not know that there is a new log created every time the app pool has been recycled. – Bgl86 Dec 12 '16 at 11:48

2 Answers2

3

Sitecore uses log4net as it's underlying logging framework, which means you can make use of any of the standard appenders. Although Sitecore have added a custom SitecoreLogFileAppender appender, you can instead simply make use of the RollingFileAppender and roll the log files based on date.

You can find samples in the log4net config examples section of the documentation.

Specifically with Sitecore, change the appender(s) to the following:

<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender, Sitecore.Logging">
  <file value="$(dataFolder)/logs/log" />
  <appendToFile value="true" />
  <rollingStyle value="Date" />
  <maxSizeRollBackups value="30" />
  <datePattern value=".yyyyMMdd'.txt'" />
  <staticLogFileName value="false" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n" />
  </layout>
  <encoding value="utf-8" />
</appender>

Some specifics about the changes in the above configuration:

  • file : The name of the file to log to. Note that we supplement this using below settings.
  • rollingStyle : The format to roll each file on.
  • maxSizeRollBackups : This has been set to 30 above, you can remove this node if you want. This value ensures that anyt logs older than 30 days get automatically deleted by log4net.
  • datePattern : This sets the format of the date to roll the files on. Note that the file suffix is included here in single quotes. See this previous answer for more details.
  • staticLogFileName : If set to true then the latest log file will always have the same name, but note that due to your file value there is no file suffix.

The files will now be generated in format log.yyyMMdd.txt in the same log folder as before and not be subject to having different files generated per application restart/app pool recycle.

Community
  • 1
  • 1
jammykam
  • 16,940
  • 2
  • 36
  • 71
1

You can easily overcome this problem if you override the standard behavior of SitecoreLogFileAppender and create a new CustomLogFileAppender class which is inherited from FileAppender. Unfortunately SitecoreLogFileAppender does not offer you the possibility to override only a specific method which you need to amend for your requirements. Therefore you need to copy all code from SitecoreLogFileAppender into your newly created file appender.

namespace MyProject.Core
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Web;
    using log4net.Appender;
    using log4net.spi;

    /// <summary>
    /// Custom log file appender
    /// </summary> 
    public class CustomLogFileAppender : FileAppender
    {
        /// <summary>
        /// The m_current date
        /// </summary>
        private DateTime m_currentDate;

        /// <summary>
        /// The m_original file name
        /// </summary>
        private string m_originalFileName;

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomLogFileAppender"/> class.
        /// </summary>
        public CustomLogFileAppender()
        {
            this.m_currentDate = DateTime.Now;
        }

        /// <summary>
        /// Gets or sets the file.
        /// </summary>
        /// <value>
        /// The file.
        /// </value>
        public override string File
        {
            get
            {
                return base.File;
            }

            set
            {
                if (this.m_originalFileName == null)
                {
                    string str = value;
                    Dictionary<string, string> variables = ConfigReader.GetVariables();
                    foreach (string index in variables.Keys)
                    {
                        string oldValue = "$(" + index + ")";
                        str = str.Replace(oldValue, variables[index]);
                    }

                    this.m_originalFileName = this.MapPath(str.Trim());
                }

                base.File = this.m_originalFileName;
            }
        }

        /// <summary>
        /// Makes the path.
        /// </summary>
        /// <param name="part1">The part1.</param>
        /// <param name="part2">The part2.</param>
        /// <param name="separator">The separator.</param>
        /// <returns>
        /// Complete path.
        /// </returns>
        public static string MakePath(string part1, string part2, char separator)
        {
            if (string.IsNullOrEmpty(part1))
            {
                return part2 ?? string.Empty;
            }

            if (string.IsNullOrEmpty(part2))
            {
                return part1 ?? string.Empty;
            }

            if ((int)part1[part1.Length - 1] == (int)separator)
            {
                part1 = part1.Substring(0, part1.Length - 1);
            }

            if ((int)part2[0] == (int)separator)
            {
                part2 = part2.Substring(1);
            }

            return part1 + (object)separator + part2;
        }

        /// <summary>
        /// Appends the specified logging event.
        /// </summary>
        /// <param name="loggingEvent">The logging event.</param>
        protected override void Append(LoggingEvent loggingEvent)
        {
            DateTime now = DateTime.Now;
            if (this.m_currentDate.Day != now.Day || this.m_currentDate.Month != now.Month || this.m_currentDate.Year != now.Year)
            {
                lock (this)
                {
                    this.CloseFile();
                    this.m_currentDate = DateTime.Now;
                    this.OpenFile(string.Empty, false);
                }
            }

            base.Append(loggingEvent);
        }

        /// <summary>
        /// Opens the file.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="append">if set to <c>true</c> [append].</param>
        protected override void OpenFile(string fileName, bool append)
        {
            fileName = this.m_originalFileName;
            fileName = fileName.Replace("{date}", this.m_currentDate.ToString("yyyyMMdd"));
            fileName = fileName.Replace("{time}", this.m_currentDate.ToString("HHmmss"));
            fileName = fileName.Replace("{processid}", CustomLogFileAppender.GetCurrentProcessId().ToString());

            base.OpenFile(fileName, append);
        }

        /// <summary>
        /// Gets the current process identifier.
        /// </summary>
        /// <returns>
        /// The process Id.
        /// </returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int GetCurrentProcessId();

        /// <summary>
        /// Gets the name of the timed file.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>
        /// Filename with timestamp.
        /// </returns>
        private string GetTimedFileName(string fileName)
        {
            int num = fileName.LastIndexOf('.');
            if (num < 0)
            {
                return fileName;
            }

            return
                string.Concat(
                    new object[4]
                        {
                            (object)fileName.Substring(0, num), (object)'.', (object)this.m_currentDate.ToString("HHmmss"),
                            (object)fileName.Substring(num)
                        });
        }

        /// <summary>
        /// Determines whether the specified file name is locked.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>
        /// Locked or not.
        /// </returns>
        private bool IsLocked(string fileName)
        {
            if (!System.IO.File.Exists(fileName))
            {
                return false;
            }

            try
            {
                FileStream fileStream = System.IO.File.OpenWrite(fileName);
                if (fileStream == null)
                {
                    return true;
                }

                fileStream.Close();
                return false;
            }
            catch (Exception ex)
            {
                string message = ex.Message;
                return true;
            }
        }

        /// <summary>
        /// Maps the path.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>
        /// Mapped path.
        /// </returns>
        private string MapPath(string fileName)
        {
            if (fileName == string.Empty || fileName.IndexOf(":/", System.StringComparison.Ordinal) >= 0 || fileName.IndexOf("://", System.StringComparison.Ordinal) >= 0)
            {
                return fileName;
            }

            var index = fileName.IndexOfAny(new char[2] { '\\', '/' });
            if (index >= 0 && (int)fileName[index] == 92)
            {
                return fileName.Replace('/', '\\');
            }

            fileName = fileName.Replace('\\', '/');
            if (HttpContext.Current != null)
            {
                return HttpContext.Current.Server.MapPath(fileName);
            }

            return (int)fileName[0] == 47 ? SitecoreLogFileAppender.MakePath(HttpRuntime.AppDomainAppPath, fileName.Replace('/', '\\'), '\\') : fileName;
        }
    }
}

Please find more information here: https://sitecore.unic.com/2015/01/27/create-a-single-sitecore-log-file-per-day

Vlad Iobagiu
  • 4,118
  • 3
  • 13
  • 22