Запись в журнал событий приложений Windows


166

Есть ли способ записи в этот журнал событий:

введите описание изображения здесь

Или, по крайней мере, какой-то другой журнал Windows по умолчанию, где мне не нужно регистрировать источник события ?




1
«Вы должны создать и настроить источник события, прежде чем писать первую запись с источником».
Джертер

Кажется, я не могу. Итак, есть ли хороший запасной метод, чтобы предупредить, что приложение не может записывать в журналы Windows? Плоский файл кажется хорошим, но где? Папка приложения все еще нуждается в некоторых разрешениях. Мое приложение - это служба Windows.
Джертер

3
Если ваше приложение является службой Windows, то для вас автоматически создается источник событий. Вы можете получить к нему доступ через ServiceBase.EventLog. Имя источника по умолчанию - ServiceName.
Майк Зборай

Ответы:


238

Да, есть способ записи в журнал событий, который вы ищете. Вам не нужно создавать новый источник, просто используйте существующий, который часто имеет то же имя, что и имя EventLog, а также, в некоторых случаях, например, журнал событий Application, может быть доступен без прав администратора *.

* Другими случаями, когда вы не можете получить к нему доступ напрямую, является, например, Security EventLog, доступ к которому имеет только операционная система.

Я использовал этот код для записи непосредственно в журнал событий приложения:

using (EventLog eventLog = new EventLog("Application")) 
{
    eventLog.Source = "Application"; 
    eventLog.WriteEntry("Log message example", EventLogEntryType.Information, 101, 1); 
}

Как видите, источник EventLog совпадает с именем EventLog. Причину этого можно найти в источниках событий @ Центр разработки Windows (я выделил часть, которая относится к имени источника):

Каждый журнал в ключе Eventlog содержит подключи, называемые источниками событий. Источник события - это имя программного обеспечения, которое регистрирует событие. Часто это имя приложения или имя подкомпонента приложения, если приложение большое. Вы можете добавить в реестр не более 16 384 источников событий.


1
Но текст, который вы цитировали, говорит о том, что вы должны зарегистрировать источник событий под ключом журнала событий.
Рэймонд Чен

1
Я имел в виду, что имя журнала событий часто совпадает с именем приложения, поэтому вы можете зарегистрировать запись журнала событий непосредственно в EventLog, не создавая новый источник. Я выделил цитируемый текст для дальнейшего чтения.
cloud120

3
Технически акт создания раздела реестра заключается в регистрации источника события. Наименование ключа после имени приложения является соглашением, позволяющим избежать конфликтов. Ваш ответ в основном такой же, как этот ответ .
Рэймонд Чен

7
Спасибо за ваше время, Рэймонд Чен, мы здесь, чтобы попытаться решить или предложить что-то, что может помочь другим. В этом случае мне кажется, что я ответил на вопрос темы: «Есть ли способ записи в этот журнал событий: или, по крайней мере, в какой-то другой журнал Windows по умолчанию, где мне не нужно регистрировать источник событий?». -> Я ответил: да, и я поделился этим с вами. Несмотря на то, что это может вызвать конфликты, как вы сказали, существует способ.
cloud120

7
Вы отвечаете на вопрос "Есть ли способ сделать это без регистрации источника события?" и ваш ответ говорит: «Создайте этот раздел реестра, чтобы зарегистрировать источник события». Это также идентично существующему ответу.
Рэймонд Чен

14

Вы можете использовать класс EventLog, как описано в разделе Как сделать запись в журнал событий приложения (Visual C #) :

var appLog = new EventLog("Application");
appLog.Source = "MySource";
appLog.WriteEntry("Test log message");

Однако вам необходимо настроить этот источник «MySource», используя права администратора:

Используйте WriteEvent и WriteEntry для записи событий в журнал событий. Вы должны указать источник событий для записи событий; Вы должны создать и настроить источник события, прежде чем писать первую запись с источником.


2
Вот проблема, которая у меня есть: я не могу создать источник, потому что у меня нет этих привилегий, но мне все равно нужно где-то
регистрировать эту

2
Затем используйте установщик ( stackoverflow.com/questions/1484605/… ) или войдите в файл.
CodeCaster

1
Спасибо. Это привело меня к другому SO вопросу: stackoverflow.com/questions/3930529/…
Джертер,

@CodeCaster - Откуда мы можем получить доступ к этим журналам? Я имею в виду место, где оно хранится?
Арвинд Чоурасия

1
@ Задумайтесь, что этот вопрос не имеет ничего общего с моим ответом, и это совершенно новый вопрос.
CodeCaster

11

Как указано в MSDN (например, https://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog(v=vs.110).aspx ), проверка несуществующего источника и создание источника требует администратора привилегия.

Однако можно использовать источник «Приложение» без. Однако в моем тесте под Windows 2012 Server r2 я получил следующую запись в журнале с использованием источника «Приложение»:

Описание для идентификатора события xxxx из исходного приложения не может быть найдено. Либо компонент, который вызывает это событие, не установлен на локальном компьютере, либо установка повреждена. Вы можете установить или восстановить компонент на локальном компьютере. Если событие возникло на другом компьютере, отображаемая информация должна была быть сохранена вместе с событием. Следующая информация была включена в событие: {мое сообщение о вхождении события} ресурс сообщения присутствует, но сообщение не найдено в таблице строк / сообщений

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

    private string CreateEventSource(string currentAppName)
    {
        string eventSource = currentAppName;
        bool sourceExists;
        try
        {
            // searching the source throws a security exception ONLY if not exists!
            sourceExists = EventLog.SourceExists(eventSource);
            if (!sourceExists)
            {   // no exception until yet means the user as admin privilege
                EventLog.CreateEventSource(eventSource, "Application");
            }
        }
        catch (SecurityException)
        {
            eventSource = "Application";
        }

        return eventSource;
    }

Я называю это с currentAppName = AppDomain.CurrentDomain.FriendlyName

Может быть возможно использовать класс EventLogPermission вместо этого try / catch, но не уверен, что мы сможем избежать этого.

Также возможно создать источник извне, например, в расширенном Powershell:

New-EventLog -LogName Application -Source MyApp

Затем использование MyApp в вышеописанном методе НЕ будет генерировать исключение, и EventLog может быть создан с этим источником.


10

Это класс регистратора, который я использую. В нем есть закрытый метод Log () EventLog.WriteEntry(), как вы и пишете в журнал событий. Я включил весь этот код здесь, потому что это удобно. В дополнение к ведению журнала этот класс также будет следить за тем, чтобы сообщение не было слишком длинным для записи в журнал событий (оно будет урезать сообщение). Если сообщение было слишком длинным, вы получите исключение. Абонент также может указать источник. Если вызывающая сторона этого не сделает, этот класс получит источник. Надеюсь, поможет.

Кстати, вы можете получить ObjectDumper из Интернета. Я не хотел публиковать все это здесь. Я получил мой отсюда:C:\Program Files (x86)\Microsoft Visual Studio 10.0\Samples\1033\CSharpSamples.zip\LinqSamples\ObjectDumper

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Xanico.Core.Utilities;

namespace Xanico.Core
{
    /// <summary>
    /// Logging operations
    /// </summary>
    public static class Logger
    {
        // Note: The actual limit is higher than this, but different Microsoft operating systems actually have
        //       different limits. So just use 30,000 to be safe.
        private const int MaxEventLogEntryLength = 30000;

        /// <summary>
        /// Gets or sets the source/caller. When logging, this logger class will attempt to get the
        /// name of the executing/entry assembly and use that as the source when writing to a log.
        /// In some cases, this class can't get the name of the executing assembly. This only seems
        /// to happen though when the caller is in a separate domain created by its caller. So,
        /// unless you're in that situation, there is no reason to set this. However, if there is
        /// any reason that the source isn't being correctly logged, just set it here when your
        /// process starts.
        /// </summary>
        public static string Source { get; set; }

        /// <summary>
        /// Logs the message, but only if debug logging is true.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="debugLoggingEnabled">if set to <c>true</c> [debug logging enabled].</param>
        /// <param name="source">The name of the app/process calling the logging method. If not provided,
        /// an attempt will be made to get the name of the calling process.</param>
        public static void LogDebug(string message, bool debugLoggingEnabled, string source = "")
        {
            if (debugLoggingEnabled == false) { return; }

            Log(message, EventLogEntryType.Information, source);
        }

        /// <summary>
        /// Logs the information.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="source">The name of the app/process calling the logging method. If not provided,
        /// an attempt will be made to get the name of the calling process.</param>
        public static void LogInformation(string message, string source = "")
        {
            Log(message, EventLogEntryType.Information, source);
        }

        /// <summary>
        /// Logs the warning.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="source">The name of the app/process calling the logging method. If not provided,
        /// an attempt will be made to get the name of the calling process.</param>
        public static void LogWarning(string message, string source = "")
        {
            Log(message, EventLogEntryType.Warning, source);
        }

        /// <summary>
        /// Logs the exception.
        /// </summary>
        /// <param name="ex">The ex.</param>
        /// <param name="source">The name of the app/process calling the logging method. If not provided,
        /// an attempt will be made to get the name of the calling process.</param>
        public static void LogException(Exception ex, string source = "")
        {
            if (ex == null) { throw new ArgumentNullException("ex"); }

            if (Environment.UserInteractive)
            {
                Console.WriteLine(ex.ToString());
            }

            Log(ex.ToString(), EventLogEntryType.Error, source);
        }

        /// <summary>
        /// Recursively gets the properties and values of an object and dumps that to the log.
        /// </summary>
        /// <param name="theObject">The object to log</param>
        [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Xanico.Core.Logger.Log(System.String,System.Diagnostics.EventLogEntryType,System.String)")]
        [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "object")]
        public static void LogObjectDump(object theObject, string objectName, string source = "")
        {
            const int objectDepth = 5;
            string objectDump = ObjectDumper.GetObjectDump(theObject, objectDepth);

            string prefix = string.Format(CultureInfo.CurrentCulture,
                                          "{0} object dump:{1}",
                                          objectName,
                                          Environment.NewLine);

            Log(prefix + objectDump, EventLogEntryType.Warning, source);
        }

        private static void Log(string message, EventLogEntryType entryType, string source)
        {
            // Note: I got an error that the security log was inaccessible. To get around it, I ran the app as administrator
            //       just once, then I could run it from within VS.

            if (string.IsNullOrWhiteSpace(source))
            {
                source = GetSource();
            }

            string possiblyTruncatedMessage = EnsureLogMessageLimit(message);
            EventLog.WriteEntry(source, possiblyTruncatedMessage, entryType);

            // If we're running a console app, also write the message to the console window.
            if (Environment.UserInteractive)
            {
                Console.WriteLine(message);
            }
        }

        private static string GetSource()
        {
            // If the caller has explicitly set a source value, just use it.
            if (!string.IsNullOrWhiteSpace(Source)) { return Source; }

            try
            {
                var assembly = Assembly.GetEntryAssembly();

                // GetEntryAssembly() can return null when called in the context of a unit test project.
                // That can also happen when called from an app hosted in IIS, or even a windows service.

                if (assembly == null)
                {
                    assembly = Assembly.GetExecutingAssembly();
                }


                if (assembly == null)
                {
                    // From http://stackoverflow.com/a/14165787/279516:
                    assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
                }

                if (assembly == null) { return "Unknown"; }

                return assembly.GetName().Name;
            }
            catch
            {
                return "Unknown";
            }
        }

        // Ensures that the log message entry text length does not exceed the event log viewer maximum length of 32766 characters.
        private static string EnsureLogMessageLimit(string logMessage)
        {
            if (logMessage.Length > MaxEventLogEntryLength)
            {
                string truncateWarningText = string.Format(CultureInfo.CurrentCulture, "... | Log Message Truncated [ Limit: {0} ]", MaxEventLogEntryLength);

                // Set the message to the max minus enough room to add the truncate warning.
                logMessage = logMessage.Substring(0, MaxEventLogEntryLength - truncateWarningText.Length);

                logMessage = string.Format(CultureInfo.CurrentCulture, "{0}{1}", logMessage, truncateWarningText);
            }

            return logMessage;
        }
    }
}

3
И этот код показывает это. Что плохого в том, чтобы поделиться этим с ним? Разве это не может быть полезным для ОП и других?
Боб Хорн

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

2
Мне все еще нужно создать источник события, но вы опубликовали свой ответ до обновления названия вопроса. Тем не менее, я не знал об ограничении длины, спасибо.
Джертер

-4

пытаться

   System.Diagnostics.EventLog appLog = new System.Diagnostics.EventLog();
   appLog.Source = "This Application's Name";
   appLog.WriteEntry("An entry to the Application event log.");

3
это требует регистрации источника события и, следовательно, не отвечает на вопрос. Прости.
Джертер

Основная идея этого вопроса - использовать источник событий «Приложение».
rcarrillopadron
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.