Запишіть у журнал подій додатків Windows


166

Чи є спосіб записати в цей журнал подій:

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

Або принаймні якийсь інший журнал Windows за замовчуванням, де мені не потрібно реєструвати джерело подій ?




1
"Ви повинні створити та налаштувати джерело події, перш ніж записати перший запис із джерелом."
Джертер

Здається, я не можу. Отже, чи є хороший резервний метод попередження про те, що програма не може записувати до журналів Windows? Плоский файл здається гарним, але де? Папка додатків все ще потребуватиме деяких дозволів. Моя програма - це послуга Windows.
Джертер

3
Якщо ваша програма є службою Windows, то джерело подій буде створено для вас автоматично. Ви можете отримати доступ до нього через ServiceBase.EventLog. За замовчуванням ім'я Джерела - ім'я служби.
Майк Зборай

Відповіді:


237

Так, є спосіб записати в журнал подій, який ви шукаєте. Вам не потрібно створювати нове джерело, просто скористайтеся наявним джерелом, яке часто має те саме ім'я, як ім'я EventLog, а також у деяких випадках, як Приклад журналу подій, можна отримати без адміністративних привілеїв *.

* Інші випадки, коли ви не можете отримати доступ до нього безпосередньо, це, наприклад, Security EventLog, до якого доступ має лише операційна система.

Я використовував цей код, щоб записати безпосередньо в журнал подій Застосування:

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

Як бачимо, джерело EventLog - те саме, що і ім'я EventLog. Причину цього можна знайти в Центрі подій @ Windows Dev Center (я виділив жирною частиною, що стосується імені джерела):

Кожен журнал у ключі 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 / ...
Jerther

@CodeCaster - звідки ми можемо отримати доступ до цих журналів? Я маю на увазі місце, де воно зберігається?
Арвінд Чурасія

1
@ Арвінд це питання не має нічого спільного з моєю відповіддю, і це зовсім нове запитання.
CodeCaster

11

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

Однак можна використовувати джерело "Application" без. Проте в моєму тесті під Windows 2012 Server r2 я отримую такий запис журналу, використовуючи джерело "Application":

Опис для ідентифікатора події xxxx з вихідного додатка неможливо знайти. Або компонент, який викликає цю подію, не встановлений на вашому локальному комп'ютері, або установка пошкоджена. Ви можете встановити або відремонтувати компонент на локальному комп'ютері. Якщо подія виникла на іншому комп'ютері, інформацію про дисплей потрібно було зберегти разом із подією. До події була включена така інформація: {my message entry message} ресурс повідомлення присутній, але повідомлення не знайдено в таблиці рядків / повідомлень

Я визначив наступний метод для створення джерела:

    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 замість цього спробувати / catch, але не впевнений, що ми можемо уникнути улову.

Можна також створити джерело зовні, наприклад, у підвищеній потужності:

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
для цього потрібно зареєструвати Джерело події, і таким чином він не відповідає на питання. вибачте.
Джертер

Основна ідея цього питання полягає у використанні джерела події "Application".
rcarrillopadron
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.