Scrivi nel registro eventi dell'applicazione Windows


166

C'è un modo per scrivere nel registro eventi:

inserisci qui la descrizione dell'immagine

O almeno, qualche altro registro predefinito di Windows, dove non devo registrare una fonte di eventi ?




1
"È necessario creare e configurare l'origine evento prima di scrivere la prima voce con l'origine."
Jerther,

Sembra che non ci riesca. Quindi, esiste un buon metodo di fallback per avvertire che l'applicazione non può scrivere nei registri di Windows? Un file flat sembra buono ma, dove? La cartella dell'applicazione avrebbe comunque bisogno di alcune autorizzazioni. La mia applicazione è un servizio Windows.
Jerther,

3
Se l'applicazione è un servizio Windows, viene automaticamente creata un'origine evento. Puoi accedervi tramite ServiceBase.EventLog. Il nome predefinito dell'origine è ServiceName.
Mike Zboray,

Risposte:


238

Sì, c'è un modo per scrivere nel registro eventi che stai cercando. Non è necessario creare una nuova fonte, basta semplicemente usare quella esistente, che spesso ha lo stesso nome del nome dell'EventLog e anche, in alcuni casi come l'applicazione del registro eventi, può essere accessibile senza i privilegi di amministratore *.

* Altri casi, in cui non è possibile accedervi direttamente, sono il Security EventLog, ad esempio, a cui si accede solo dal sistema operativo.

Ho usato questo codice per scrivere direttamente nel registro eventi Applicazione:

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

Come puoi vedere, l'origine EventLog è uguale al nome di EventLog. La ragione di ciò può essere trovata in Event Sources @ Windows Dev Center (ho in grassetto la parte che si riferisce al nome della fonte):

Ogni registro nella chiave Eventlog contiene sottochiavi chiamate origini eventi. La fonte dell'evento è il nome del software che registra l'evento. Spesso è il nome dell'applicazione o il nome di un sottocomponente dell'applicazione se l'applicazione è grande. È possibile aggiungere un massimo di 16.384 origini di eventi al registro.


1
Ma il testo che hai citato dice che devi registrare l'origine evento sotto la chiave del registro eventi.
Raymond Chen,

1
Quello che intendevo dire è che il nome del registro eventi è spesso lo stesso nome dell'applicazione, pertanto è possibile registrare una voce del registro eventi direttamente nel registro eventi senza creare una nuova fonte. Ho messo in grassetto il testo richiesto per ulteriori letture.
cloud120

3
Tecnicamente l'atto di creare la chiave di registro è la registrazione della fonte dell'evento. La denominazione della chiave dopo il nome dell'applicazione è una convenzione per evitare conflitti. La tua risposta è sostanzialmente la stessa di questa risposta .
Raymond Chen,

7
Grazie per il tuo tempo Raymond Chen, siamo qui per cercare di risolvere o suggerire qualcosa che potrebbe aiutare gli altri. In questo caso, credo di aver risposto alla domanda sull'argomento: "Esiste un modo per scrivere in questo registro eventi: o almeno, qualche altro registro predefinito di Windows, in cui non devo registrare una fonte di eventi?". -> Ho risposto: Sì lo è e l'ho condiviso con te. Nonostante il fatto che potrebbe causare conflitti come hai detto, esiste un modo.
cloud120,

7
Stai rispondendo alla domanda "Esiste un modo per farlo senza registrare una fonte di eventi?" e la tua risposta dice "Crea questa chiave di registro per registrare una fonte di eventi". È anche identico a una risposta esistente.
Raymond Chen,

14

È possibile utilizzare la classe EventLog, come spiegato in Procedura: scrivere nel registro eventi dell'applicazione (Visual C #) :

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

Tuttavia, dovrai configurare questa fonte "MySource" usando i privilegi di amministratore:

Utilizzare WriteEvent e WriteEntry per scrivere eventi in un registro eventi. È necessario specificare una fonte di eventi per scrivere eventi; è necessario creare e configurare l'origine evento prima di scrivere la prima voce con l'origine.


2
Questo è il problema che ho: non riesco a creare l'origine perché non ho quei privilegi, ma devo ancora registrare quel problema da qualche parte
Jerther,

2
Quindi utilizzare un programma di installazione ( stackoverflow.com/questions/1484605/… ) o accedere al file.
CodeCaster

1
Grazie. Questo mi ha portato a questa altra domanda SO: stackoverflow.com/questions/3930529/...
Jerther

@CodeCaster - Da dove possiamo accedere a questi registri? Intendo la posizione in cui è memorizzato?
Arvind Chourasiya,

1
@Arvind che la domanda non ha nulla a che fare con la mia risposta ed è una domanda completamente nuova.
CodeCaster

11

Come indicato in MSDN (ad es. Https://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog(v=vs.110).aspx ), il controllo di una fonte inesistente e la creazione di una fonte richiede admin privilegio.

È comunque possibile utilizzare l' origine "Applicazione" senza. Nel mio test in Windows 2012 Server r2, tuttavia ottengo la seguente voce di registro utilizzando l'origine "Applicazione":

Impossibile trovare la descrizione per ID evento xxxx dall'applicazione di origine. Il componente che genera questo evento non è installato sul computer locale o l'installazione è danneggiata. È possibile installare o riparare il componente sul computer locale. Se l'evento ha avuto origine su un altro computer, le informazioni sul display dovevano essere salvate con l'evento. Le seguenti informazioni sono state incluse con l'evento: {il mio messaggio di entrata evento} la risorsa messaggio è presente ma il messaggio non è stato trovato nella tabella string / message

Ho definito il seguente metodo per creare la fonte:

    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;
    }

Lo sto chiamando con currentAppName = AppDomain.CurrentDomain.FriendlyName

Potrebbe essere possibile utilizzare la classe EventLogPermission invece di questo tentativo / cattura, ma non sono sicuro che possiamo evitare la cattura.

È anche possibile creare la fonte esternamente, ad esempio in Powershell elevato:

New-EventLog -LogName Application -Source MyApp

Quindi, l'uso di "MyApp" nel metodo sopra NON genererà eccezioni e EventLog può essere creato con tale origine.


10

Questa è la classe logger che uso. Il metodo Log () privato ha EventLog.WriteEntry()in esso, che è come si scrive effettivamente nel registro eventi. Sto includendo tutto questo codice qui perché è utile. Oltre alla registrazione, questa classe assicurerà anche che il messaggio non sia troppo lungo per scrivere nel registro eventi (troncerà il messaggio). Se il messaggio fosse troppo lungo, otterrai un'eccezione. Il chiamante può anche specificare l'origine. Se il chiamante non lo fa, questa classe otterrà la fonte. Spero che sia d'aiuto.

A proposito, puoi ottenere un ObjectDumper dal web. Non volevo postare tutto questo qui. Ho preso il mio da qui: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
E questo codice lo dimostra. Qual è il danno nel condividere questo con lui? Non potrebbe essere utile all'OP e ad altri?
Bob Horn,

5
Non è possibile scrivere nel registro eventi senza creare un'origine eventi , quindi questo codice non lo mostra.
CodeCaster

2
Avrei ancora bisogno di creare la fonte dell'evento ma hai pubblicato la tua risposta prima che il titolo della domanda fosse aggiornato. Tuttavia, non sapevo del limite di lunghezza, grazie.
Jerther,

-4

provare

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

3
ciò richiede la registrazione di una fonte di eventi e quindi non risponde alla domanda. spiacente.
Jerther,

L'idea principale di questa domanda è quella di utilizzare l'origine eventi "Applicazione".
rcarrillopadron,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.