Каков наилучший подход к ведению журнала?

Мое (локальное, windows/mono) приложение записывает важные события в текстовый файл. В случае внезапного сбоя / сбоя / принудительного выхода никакие данные не должны оставаться незаписанными (насколько это возможно). Таким образом, в настоящее время я использую простой метод добавления к текстовому файлу:

Public Shared Sub LogAppEvent(ByVal EventData As String)
    Dim Config As ConfigHandler = ConfigHandler.GetSingleton()
    Dim AppLog As New IO.StreamWriter(Config.GetUserFilesRootDir() & ConfigOptions.AppLogName, True)
    AppLog.WriteLine(String.Format("[{0}] {1}", Date.Now.ToString(), EventData))
    AppLog.Close()
End Sub

Это крайне неоптимально, но события журнала довольно редки. Вы бы порекомендовали перейти на System.Diagnostics класс регистрации?

Или, может быть, вы предложите другое решение?

5 ответов

Решение

Если такой сверхосновный подход функционально достаточен для ваших нужд, вы можете придерживаться его. Но вы можете задать себе несколько вопросов, чтобы убедиться:

  • Возможно ли одновременное ведение журнала событий несколькими потоками? Эта функция не безопасна

  • Требуется ли уведомление об ошибке?

  • Есть ли риск, что файлы журнала растут без границ без автоматического сокращения?

  • Вы выиграете от более обширного ведения журнала, чтобы у вас было больше информации о событиях, приводящих к ошибке?

  • Получите ли вы дополнительную информацию об ошибках (трассировка стека, сведения об исключениях и т. Д.)

  • Ваша программа работает на нескольких компьютерах? Если так, как журналы попадают к вам?

  • Есть ли необходимость / значение в инструментах, помогающих анализировать файлы журналов (как по отдельности, так и при поиске шаблонов, таких как распространенные ошибки во многих файлах журналов)?

Если вы решите, что у вас есть другие требования, существует ряд бесплатных каркасов ведения журналов, таких как NLog или log4net, которые могут помочь записывать более подробные журналы, и есть несколько коммерческих продуктов, таких как GIBRALTAR и SmartInspect, которые могут помочь с управлением и анализом журналов.,

Как упоминалось ранее, NLog и log4net являются хорошими средами ведения журналов. Как упоминал Джефф выше, System.Diagnostics также является разумным выбором (не только для входа в EventLog). Пытаясь повысить ценность моего ответа, вместо того, чтобы просто попугайировать то, что уже было сказано, вы можете улучшить ведение журнала System.Diagnostics с помощью TraceSources и бесплатной библиотеки Ukadc.Diagnostics из codeplex.

С помощью TraceSources вы можете создавать "именованные регистраторы", подобно тому, как вы можете это делать в NLog и log4net. Эти TraceSources можно настроить для регистрации на определенных уровнях (в зависимости от TraceSource), и их можно отправлять в различные пункты назначения (TraceListeners). Все TraceSources могут регистрироваться для одного и того же слушателя, или некоторые могут регистрироваться для некоторых слушателей, в то время как другие регистрируются для других слушателей. Любой TraceSource также может быть отправлен нескольким TraceListener.

Вот как вы должны использовать TraceSources в коде (предположим, что TraceSource "abc" был настроен в файле app.config для записи "Info" и сообщений с более высоким приоритетом и был настроен для входа в файл "log.txt").

public class MyClass
{
  static TraceSource ts = new TraceSource("abc"); //Common idiom for NLog and log4net, not sure if as common for TraceSource

  public void Func1(int x)
  {
    ts.Information("Entering Func1");
    ts.Verbose("x = {0}", x); //Won't log if "abc" is configured to log Info and HIGHER messgaes
    ts.Information("Exiting Func1");
  }
}

Одно из огромных преимуществ Ukadc.Diagnostics по сравнению с "простыми" TraceSources заключается в том, что вы можете настроить форматирование вывода в стиле NLog/log4net, чтобы иметь гораздо больший контроль над тем, какие поля отображаются в выходных данных журнала и в каком формате.

Три полезные вещи из NLog/log4net, которые недоступны в System.Diagnostics:

  1. Возможность автоматически регистрировать информацию о сайте звонка (метод / функция)

  2. Дополнительный контекст ведения журнала (GDC - глобальные свойства ведения журнала, MDC - свойства ведения журнала потоков на языке NLog/log4net). System.Diagnostics имеет Trace.CorrelationManager.LogicalOperationStack, который похож на NDC.

  3. Иерархические регистраторы.

Иерархические регистраторы означают, что вы можете настроить регистратор "предков", и любые "потомки" регистраторов будут наследовать эти параметры. Например, допустим, что у вас есть класс, для которого полностью (namespace) квалифицированное имя типа - Namespace1.Namespace2.Class. С помощью NLog/log4net вы можете настроить регистрационную информацию (уровень, место назначения) для "Пространства имен1", и если вы запрашиваете регистратор на основе полного имени любого типа в Пространстве имен1, он наследует настройки Пространства имен1. Вы можете достичь чего-то похожего с помощью TraceSources, посмотрев, как Castle реализовал свою абстракцию ведения журнала на основе TraceSource. В частности, посмотрите на функцию Initialize. Работать довольно легко (в вашей собственной тонкой оболочке вокруг TraceSource) и, в качестве дополнительного преимущества, немного упрощает настройку ваших TraceSource, поскольку вам не нужно настраивать каждый отдельный TraceSource по отдельности. Обратите внимание, что вы можете легко добавить возможность иметь "корневую" конфигурацию, настроив TraceSource с именем "" и добавив некоторый код в схему Castle по умолчанию к "" конфигурации, если фактические предки не найдены. Затем вы можете, например, настроить "*" для входа в систему, скажем, Verbose, а затем специально настроить определенные TraceSources (либо по классу, либо по пространству имен), чтобы они были отключены или находились на другом уровне. Без иерархических регистраторов, чтобы сделать то же самое с TraceSources, потребовалось бы настроить каждый TraceSource на запись в "Verbose", которую вы хотите в "verbose".

В то время как я много говорил о регистраторах для классов и пространств имен, NLog, log4net и TraceSources также позволяют определять имена ваших регистраторов как произвольные строки. Таким образом, вы холодным образом определяете иерархию логгера по функциональной области, а не по пространству имен / классу:

Database
Database.Connect
Database.Query
Database.Update
Database.SQL
Amazon
Amazon.Books
Amazon.Books.Fiction
Amazon.Books.Nonfiction
Amazon.Electronics
Amazon.Electronics.Video
Amazon.Electronics.Music
Amazon.Electronics.Computer

Таким образом, вы можете включить ведение журнала "Amazon" и весь журнал содержимого Amazon (без необходимости явной настройки каждого "дочернего" TraceSource) и компонента Database. Или вы можете включить Amazon и Amazon.Electronics, и только Amazon.Books (и дочерние элементы) будут регистрироваться.

Наконец, если вы используете NLog или log4net, стоит упомянуть, что NLog только что выпустил новую версию NLog 2.0 (в бета-версии).

Nlog и log4Net - это популярный выбор для входа в проекты.net.

Да, вы можете рассмотреть System.Diagnostics. Пока вы не пишете большое количество событий, преимущество журнала событий Windows заключается в том, что у администраторов есть одно место для поиска всех событий из всех приложений.

Вот некоторый код VB.NET, который может помочь вам, если вы решите пойти по этому пути:

Imports System.Diagnostics

Public Function WriteToEventLog(ByVal Entry As String, _
   Optional ByVal AppName As String = "VB.NET Application", _
   Optional ByVal EventType As _
   EventLogEntryType =  EventLogEntryType.Information, _
   Optional ByVal LogName As String = "Application") As Boolean

'*************************************************************
 'PURPOSE: Write Entry to Event Log using VB.NET
 'PARAMETERS: Entry - Value to Write
 '            AppName - Name of Client Application. Needed 
 '              because before writing to event log, you must 
 '              have a named EventLog source. 
 '            EventType - Entry Type, from EventLogEntryType 
 '              Structure e.g., EventLogEntryType.Warning, 
 '              EventLogEntryType.Error
 '            LogName: Name of Log (System, Application; 
 '              Security is read-only) If you 
 '              specify a non-existent log, the log will be
 '              created

 'RETURNS:   True if successful, false if not

 'EXAMPLES: 
 '1. Simple Example, Accepting All Defaults
 '    WriteToEventLog "Hello Event Log"

 '2.  Specify EventSource, EventType, and LogName
 '    WriteToEventLog("Danger, Danger, Danger", "MyVbApp", _
 '                      EventLogEntryType.Warning, "System")
 '
 'NOTE:     EventSources are tightly tied to their log. 
 '          So don't use the same source name for different 
 '          logs, and vice versa
        '******************************************************

        Dim objEventLog As New EventLog()

        Try
            'Register the App as an Event Source
            If Not objEventLog.SourceExists(AppName) Then

                objEventLog.CreateEventSource(AppName, LogName)
            End If

            objEventLog.Source = AppName

            'WriteEntry is overloaded; this is one
            'of 10 ways to call it
            objEventLog.WriteEntry(Entry, EventType)
            Return True
        Catch Ex As Exception
            Return False

        End Try

    End Function

Я использую открытую переменную и таймер, добавляя новые строки в текстовый файл.

Например

Public Class Form1

    Public WriteToLog = ""

    Dim LogFilePath As String = My.Application.Info.DirectoryPath & "\applog.txt"

....

    Public Function Log_to_txt(ByVal text As String)

        Dim CurDate As String
        CurDate = Format(Now, "General Date")

        WriteToLog = WriteToLog & "[" & CurDate & "] " & text & vbCrLf

        Return True

    End Function

...

Log_to_txt("Application started...")

...

    Private Sub WriteToLogTimer_Tick(sender As Object, e As EventArgs) Handles WriteToLogTimer.Tick

        Dim CatchLog As String = WriteToLog

        WriteToLog = ""

        Try

            My.Computer.FileSystem.WriteAllText(LogFilePath, CatchLog, True)

        Catch ex As IOException

        End Try

    End Sub
Другие вопросы по тегам