Реализация файла журнала Rolling в ASP.NET и EntLib 5.0

Я использую EntLib 5 для создания скользящего плоского файла. При этом не используется прослушиватель трассировки скользящего плоского файла, а используется прослушиватель трассировки плоского файла с некоторыми изменениями в коде. Прослушиватель трассировки Rolling Flat File не использовался из-за проблем с его внутренним дизайном, поскольку он не соответствует требованиям здесь.

Вот раздел конфигурации:

    <configuration>
    <configSections>
        <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true"/>
        <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
            <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
                <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
                <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
                    <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
                    <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
                    <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
                    <section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
                </sectionGroup>
            </sectionGroup>
        </sectionGroup>
    </configSections>
    <loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
  <listeners>
   <add name="Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    fileName="./Logs/MyLog.log" formatter="Text Formatter" />
  </listeners>
  <formatters>
   <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    template="Timestamp: {timestamp(local)}{newline}&#xA;Message: {message}{newline}&#xA;Category: {category}{newline}&#xA;Priority: {priority}{newline}&#xA;EventId: {eventid}{newline}&#xA;Severity: {severity}{newline}&#xA;Title:{title}{newline}&#xA;Machine: {localMachine}{newline}&#xA;App Domain: {localAppDomain}{newline}&#xA;ProcessId: {localProcessId}{newline}&#xA;Process Name: {localProcessName}{newline}&#xA;Thread Name: {threadName}{newline}&#xA;Win32 ThreadId:{win32ThreadId}{newline}&#xA;Extended Properties: {dictionary({key} - {value}{newline})}"
    name="Text Formatter" />
  </formatters>
  <categorySources>
   <add switchValue="All" name="General">
    <listeners>
     <add name="Flat File Trace Listener" />
    </listeners>
   </add>
  </categorySources>
  <specialSources>
   <allEvents switchValue="All" name="All Events" />
   <notProcessed switchValue="All" name="Unprocessed Category" />
   <errors switchValue="All" name="Logging Errors &amp; Warnings">
    <listeners>
     <add name="Flat File Trace Listener" />
    </listeners>
   </errors>
  </specialSources>
 </loggingConfiguration>
    <appSettings>
        <add key="LogFolder" value="./Logs"/>
        <add key="LogFileName" value="MyLog.log"/>
    </appSettings>
    <system.web>

Затем я создаю статический класс со следующим кодом для настройки процесса ведения журнала:

public static class LoggingHelper {
private static string date = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:yyyyMMdd}", DateTime.Now);

public static void SetLogFile(string logFileFolder,string logFileName) {
    if (!string.IsNullOrEmpty(logFileName)) {
        string strfileName = logFileFolder + "/" + date + logFileName;
        LoggingHelper.SetTraceLogPath(strfileName, "FlatFile TraceListener", "General", "My Login System");
        LoggingHelper.WriteLogFile("General", "Log file path " + logFileFolder + "/" + logFileName, " My Login System");
    }
}  

public static void SetTraceLogPath(string logFile, string logFileName, string category, string message) {
    ConfigurationFileMap configFileMap = new ConfigurationFileMap();
    configFileMap.MachineConfigFilename = "Web.config";
    Configuration entLibConfig = WebConfigurationManager.OpenWebConfiguration("/ASP.NET_Logging");
    LoggingSettings loggingSettings = (LoggingSettings)entLibConfig.GetSection(LoggingSettings.SectionName);

    FlatFileTraceListenerData data = loggingSettings.TraceListeners.Get("Flat File Trace Listener") as FlatFileTraceListenerData;
    data.FileName = logFile;
    entLibConfig.Save();
    LogEntry objLog = new LogEntry();
    objLog.TimeStamp = System.DateTime.Now;
    objLog.Categories.Add(category);
    objLog.Message = message;
    objLog.Priority = 1;
    Logger.Write(objLog);

}

public static void WriteLogFile(string category, string msg, string title) {
    try {
        LogEntry le = new LogEntry();
        le.TimeStamp = System.DateTime.Now;
        le.Categories.Add(category);
        le.Severity = TraceEventType.Information;
        le.Priority = 1;
        le.Message = msg;
        le.Title = title;
        le.Priority = 1;
        Logger.Write(le);
    } catch (LoggingException ex) {
        LoggingHelper.WriteLogFile("General", "Error in writing log file " + ex.ToString(), "My Login System");
    }
}}

SetLogFile вызывается в методе Application_Start на странице Global.aspx следующим образом:

void Application_Start(object sender, EventArgs e) 
{
    // Code that runs on application startup
    LoggingHelper.SetLogFile(ConfigurationSettings.AppSettings["LogFolder"].Trim     (),      ConfigurationSettings.AppSettings["LogFileName"].Trim());
    LoggingHelper.WriteLogFile("General", "*** Application_Start ***", "");
}

Затем я просто вызываю метод WriteLogFile везде, где требуется ведение журнала, например:

protected void btn_Page2_Click(object sender, EventArgs e) {
    LoggingHelper.WriteLogFile("General", "btn_Page2_Click on Default.aspx", "");

    Response.Redirect("SecongPage.aspx");

}

Это отлично работает, но для незначительной проблемы. При первом доступе к веб-странице файл журнала не создается в формате Logfilename.log, а просто создается MyLog.log или любое другое имя, указанное в Web.config, и файл журнала создается вне указанной папки, в этом case 'Log', впервые. Для последующих запросов он создаст желаемый файл с желаемым именем в настроенной папке. Этот шаблон повторяется ежедневно, на следующий день для первого запроса журнал будет записан во вчерашний файл, а после следующего запроса создается новый файл с сегодняшней датой, и все сообщения журнала будут записаны в этот файл.

Что я сделал в своем коде, из-за чего это произошло?

Мы используем приведенный выше код с небольшими изменениями для входа в наши приложения WinForms без проблем.

В настоящее время веб-сайт не развернут в IIS. Поскольку эта работа еще не завершена, мы используем встроенный сервер разработки из VS 2008. Другой повод для беспокойства заключается в том, что код пытается обновить Web.config, по крайней мере, один раз во время запуска. Не знаю, можно ли так поступить!

Это очень простой пример с двумя страницами, на первой странице есть кнопка, при нажатии которой происходит переход на вторую страницу. Я пытаюсь внедрять файл журнала на ежедневной основе, а Rolling Flat File, который поставляется с EntLib, не соответствует требованиям, поскольку он не будет создавать файл ежедневно с требуемой датой для этого дня.

С Уважением.


person Codehelp    schedule 10.05.2012    source источник
comment
После entLibConfig.Save(); попробуйте обновить раздел: ConfigurationManager.RefreshSection(LoggingSettings.SectionName);.   -  person Randy supports Monica    schedule 10.05.2012
comment
Пробовал, с теми же результатами. Есть другие идеи?   -  person Codehelp    schedule 10.05.2012
comment
Я запутался: единственный раз, когда вы пытаетесь установить файл журнала, это при запуске приложения - это правильно?   -  person Randy supports Monica    schedule 10.05.2012


Ответы (1)


после долгих поисков. Это то, что я придумал. Идея состоит в том, чтобы использовать список трассировки FlatFile вместо встроенного Rolling Flat File.

Отказ от ответственности: у одного из моих коллег был этот код с собой, он не помнит, откуда он его взял, поэтому, если вы являетесь владельцем или написали код, похожий на этот, то спасибо вам .

Пожалуйста, не голосуйте за этот ответ, поскольку я не являюсь его владельцем. Тем не менее, он здесь, чтобы помочь всем, кто ищет похожую реализацию.

Здесь используется EntLib 4, но также может использоваться с EntLib 5.

Вот часть журнала из моего Web.config:

<loggingConfiguration name="Logging Application Block" tracingEnabled="true"
defaultCategory="User" logWarningsWhenNoCategoriesMatch="true">
<listeners>
  <add fileName="trace.log" header="----------------------------------------"
    footer="----------------------------------------" formatter=""
    listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    name="FlatFile TraceListener" />
  <add fileName="MyLoggingSystem.log" header="" footer="" formatter="Text Formatter"
    listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    name="MyLoggingApp FlatFile TraceListener " />
  <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.WmiTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.WmiTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    name="WMI TraceListener" />
</listeners>
<formatters>
  <add template="[{timestamp}]  [{category}] {message}" type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    name="Text Formatter" />
</formatters>
<categorySources>
  <add switchValue="All" name="Developer">
    <listeners>
      <add name="FlatFile TraceListener" />
    </listeners>
  </add>
  <add switchValue="All" name="General" />
  <add switchValue="All" name="Instrumentation">
    <listeners>
      <add name="WMI TraceListener" />
    </listeners>
  </add>
  <add switchValue="All" name="EskoLoggingApp">
    <listeners>
      <add name="MyLogging FlatFile TraceListener " />
    </listeners>
  </add>
  <add switchValue="All" name="User">
    <listeners>
      <add name="FlatFile TraceListener" />
    </listeners>
  </add>
</categorySources>
<specialSources>
  <allEvents switchValue="All" name="All Events" />
  <notProcessed switchValue="All" name="Unprocessed Category" />
  <errors switchValue="All" name="Logging Errors &amp; Warnings">
    <listeners>
      <add name="FlatFile TraceListener" />
    </listeners>
  </errors>
</specialSources>
</loggingConfiguration>

У меня есть еще два раздела, имя файла и папка для сохранения файла:

<appSettings>
    <add key="LogFolder" value="./Logs" />
    <add key="LogFileName" value="MyLoginSystem" />
</appSettings>

Создайте такой класс:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using System.Diagnostics;
using System.Globalization;
using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
using Microsoft.Practices.EnterpriseLibrary.Logging.Filters;

/// <summary>
/// Summary description for Utility
/// </summary>
public static class Utility {
    /// <summary>
    /// Instance for log writer 
    /// </summary> 
    private static LogWriter writer;

    /// <summary>
    /// Instance for log writer 
    /// </summary> 
    private static LogWriter wmiWriter;

    /// <summary>
    /// Instance for log writer 
    /// </summary> 
    private static LogWriter eskoLoggingWriter;

    /// <summary>
    /// Intialize the Trace listners without using app.config file
    /// </summary>
    /// <param name="fileName">log file name</param>
    public static void Initialize(string fileName) {
        CreateLogWriterFromCode(fileName);
    }


    /// <summary>
    /// Log User Messages
    /// </summary>
    /// <param name="message">Message text</param>
    /// <param name="prior">Logger Priority</param>
    /// <param name="traceType">Trave event type</param>
    public static void LogUserMessage(string message, int prior, System.Diagnostics.TraceEventType traceType) {
        LogEntry(message, "User", prior, traceType);
    }

    /// <summary>
    /// Any type of Log Entry 
    /// </summary>
    /// <param name="message">Message text</param>
    /// <param name="category">Logger category</param>
    /// <param name="prior">Priory logger </param>
    /// <param name="traceType">Trace event type</param>
    public static void LogEntry(string message, string category, int prior, System.Diagnostics.TraceEventType traceType) {
        try {
            LogEntry log = null;
            log = new LogEntry();
            log.Message = message;
            log.Categories.Add(category);
            log.Severity = traceType;
            log.Priority = prior;
            log.TimeStamp = DateTime.Now;
            if (writer != null) {
                writer.Write(log);
            } else {
                eskoLoggingWriter.Write(log);
            }
            // wmiWriter.Write(log);
        } catch (LoggingException ex) {
            LogEntry(ex.Message, "Developer", prior, traceType);
        }
    }

    /// <summary>
    /// Log Developer Messages 
    /// </summary>
    /// <param name="message">Message text</param>
    /// <param name="prior">Logger priority</param>
    /// <param name="traceType">Trace event type</param>
    public static void LogDeveloperMessage(string message, int prior, System.Diagnostics.TraceEventType traceType) {
        LogEntry(message, "Developer", prior, traceType);
    }

    /// <summary>
    /// Close splash screen
    /// </summary>
    public static void ExitSplash() {
        Logger.Reset();
    }

    /// <summary>
    /// Disposing log write instance
    /// </summary>
    public static void Dispose() {
        if (writer != null) {
            writer = null;
        }
    }

    /// <summary>
    /// Creating log writer programatically 
    /// </summary>
    /// <param name="fileName">Full path of log file</param>
    static private void CreateLogWriterFromCode(string fileName) {
        if (writer != null) {
            writer = null;
        }
        //// The formatter is responsible for the look of the message. Notice the tokens: {timestamp}, {newline}, {message}, {category}
        TextFormatter formatter = new TextFormatter
            ("[{timestamp}]" + "[{category}]" + "{message}{newline}");

        //// Log messages to a log file. Use the formatter mentioned above specified.

        FlatFileTraceListener logFileListener = new FlatFileTraceListener(fileName, "", "", formatter);
        //// My collection of TraceListeners. 
        LogSource mainLogSource =
            new LogSource("FlatFile TraceListener", SourceLevels.All);
        mainLogSource.Listeners.Add(logFileListener);

        //// Assigning a non-existant LogSource for Logging Application Block 
        //// Specials Sources I don't care about.Used to say "don't log".
        LogSource nonExistantLogSource = new LogSource("Empty");

        //// I want all messages with a category of "User" or "Developer" to get distributed
        //// to all TraceListeners in my mainLogSource.
        IDictionary<string, LogSource> traceSources =
                      new Dictionary<string, LogSource>();
        traceSources.Add("User", mainLogSource);
        traceSources.Add("Developer", mainLogSource);

        // Let's glue it all together.
        // No filters at this time.
        // I won't log a couple of the Special
        // Sources: All Events and Events not
        // using "Error" or "Debug" categories.
        writer = new LogWriter(new ILogFilter[0],
                        traceSources,
                        nonExistantLogSource,
                        nonExistantLogSource,
                        mainLogSource,
                        "ALL",
                        false,
                        true);

    }

    /// <summary>
    /// Creating log writer programatically 
    /// </summary>
    static public void CreateLogWriterFromCode(string folder, string fileName) {
        if (eskoLoggingWriter == null) {
            //// The formatter is responsible for the look of the message. Notice the tokens: {timestamp}, {newline}, {message}, {category}
            TextFormatter formatter = new TextFormatter
                ("[{timestamp}]" + "[{category}]" + "{message}{newline}");

            //// Log messages to a log file. Use the formatter mentioned above specified.

            string date = String.Format("{0:yyyyMMdd}", DateTime.Now);
            FlatFileTraceListener logFileListener = new FlatFileTraceListener(folder + "/" + fileName + "_" + date + ".log", "", "", formatter);
            WmiTraceListener wmiListener = new WmiTraceListener();
            //// My collection of TraceListeners. 
            LogSource mainLogSource =
                new LogSource("FlatFile TraceListener", SourceLevels.All);
            LogSource wmiMainLogSource = new LogSource("WmiTraceListener", SourceLevels.All);
            mainLogSource.Listeners.Add(logFileListener);
            wmiMainLogSource.Listeners.Add(wmiListener);

            //// Assigning a non-existant LogSource for Logging Application Block 
            //// Specials Sources I don't care about.Used to say "don't log".
            LogSource nonExistantLogSource = new LogSource("Empty");

            //// I want all messages with a category of "User" or "Developer" to get distributed
            //// to all TraceListeners in my mainLogSource.
            IDictionary<string, LogSource> traceSources =
                          new Dictionary<string, LogSource>();
            traceSources.Add("User", mainLogSource);
            traceSources.Add("Developer", mainLogSource);

            IDictionary<string, LogSource> wmiTraceSources =
                                     new Dictionary<string, LogSource>();
            wmiTraceSources.Add("Instrumentation", wmiMainLogSource);

            // Let's glue it all together.
            // No filters at this time.
            // I won't log a couple of the Special
            // Sources: All Events and Events not
            // using "Error" or "Debug" categories.
            eskoLoggingWriter = new LogWriter(new ILogFilter[0],
                            traceSources,
                            nonExistantLogSource,
                            nonExistantLogSource,
                            mainLogSource,
                            "ALL",
                            false,
                            true);

            wmiWriter = new LogWriter(new ILogFilter[0],
                           wmiTraceSources,
                           nonExistantLogSource,
                           nonExistantLogSource,
                           wmiMainLogSource,
                           "All",
                           true,
                           true);
        }
    }
}

В Global.asax добавьте эти строки в Application_Start

void Application_Start(object sender, EventArgs e) {
        // Code that runs on application startup
        Utility.CreateLogWriterFromCode(ConfigurationSettings.AppSettings["LogFolder"].Trim(), ConfigurationSettings.AppSettings["LogFileName"].Trim());
        Utility.LogUserMessage("******** Application_Start Event My Login application started   ******", 1, System.Diagnostics.TraceEventType.Information);
}

Теперь, куда бы вы ни хотели записать сообщение, используйте

Utility.LogUserMessage

Возможно, вам придется внести незначительные изменения в класс Utility, если используется EntLib 5. Это должно быть тривиально.

Итак, у вас есть ежедневный файл журнала с правильными данными за этот день. Еще раз спасибо анонимусу, который первым написал класс Utility. Надеюсь это поможет.

С Уважением.

person Codehelp    schedule 15.05.2012
comment
Я знаю, что это не ваш код, но похоже, что код использует программную конфигурацию Enterprise Library, поэтому я не думаю, что вам нужна конфигурация XML. Мне кажется странным иметь Dispose метод в статическом классе; возможно, синглтон, реализующий IDisposable, был бы лучшим подходом? Также Dispose метод Disposes writer только тогда, когда может быть 3 писателя (writer, wmiWriter и eskoLoggingWriter). ExitSplash вызывает Logger.Reset, который удаляет внутренний LogWriter, но похоже, что вы не используете внутренний LogWriter (писатели создаются вручную). - person Randy supports Monica; 15.05.2012
comment
Я также не вижу, как выполняется сворачивание файла журнала, если только вы не полагаетесь на сброс пула приложений для запуска события Application_Start. В Enterprise Library 5 LogWriter теперь является абстрактным, поэтому код не будет работать, как в Enterprise Library 5. - person Randy supports Monica; 15.05.2012
comment
Извините за все комментарии - просто пытаюсь помочь. :) Если вы полагаетесь на перезапуск приложения для прокрутки журналов, вы можете использовать переменную среды, которая устанавливается при запуске приложения, и отбросить весь (или большую часть) настраиваемого кода. См. Добавить дату в имя файла журнала в Блокировка приложения для ведения журнала - person Randy supports Monica; 15.05.2012