Escribir en el registro de eventos de la aplicación de Windows

164

¿Hay alguna manera de escribir en este registro de eventos?

ingrese la descripción de la imagen aquí

O al menos, algún otro registro predeterminado de Windows, donde no tengo que registrar un origen de eventos ?

Jerther
fuente
1
"Debe crear y configurar el origen del evento antes de escribir la primera entrada con el origen".
Jerther
Parece que no puedo. Entonces, ¿hay un buen método alternativo para advertir que la aplicación no puede escribir en los registros de Windows? Un archivo plano parece bueno pero, ¿dónde? La carpeta de la aplicación aún necesitaría algunos permisos. Mi aplicación es un servicio de Windows.
Jerther
3
Si su aplicación es un servicio de Windows, se crea automáticamente un origen de eventos para usted. Puede acceder a través de ServiceBase.EventLog. El nombre predeterminado de la Fuente es ServiceName.
Mike Zboray

Respuestas:

236

Sí, hay una manera de escribir en el registro de eventos que está buscando. No necesita crear una nueva fuente, simplemente use la existente, que a menudo tiene el mismo nombre que el nombre de EventLog y también, en algunos casos, como la aplicación de registro de eventos, puede ser accesible sin privilegios administrativos *.

* Otros casos, en los que no puede acceder directamente, son Security EventLog, por ejemplo, al que solo accede el sistema operativo.

Utilicé este código para escribir directamente en la aplicación de registro de eventos:

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

Como puede ver, la fuente de EventLog es la misma que el nombre de EventLog. La razón de esto se puede encontrar en Event Sources @ Windows Dev Center (en negrita la parte que se refiere al nombre de la fuente):

Cada registro en la clave Eventlog contiene subclaves llamadas orígenes de eventos. El origen del evento es el nombre del software que registra el evento. A menudo es el nombre de la aplicación o el nombre de un subcomponente de la aplicación si la aplicación es grande. Puede agregar un máximo de 16,384 orígenes de eventos al registro.

nube120
fuente
1
Pero el texto que citó dice que debe registrar el origen del evento bajo la clave de registro de eventos.
Raymond Chen
1
Lo que quise decir es que el nombre del registro de eventos suele ser el mismo nombre de la aplicación, por lo que puede registrar una entrada del registro de eventos directamente en el registro de eventos sin crear una nueva fuente. En negrita, el texto escrito para obtener más lecturas.
cloud120
3
Técnicamente, el acto de crear la clave de registro es registrar el origen del evento. Nombrar la clave después del nombre de la aplicación es una convención para evitar conflictos. Su respuesta es básicamente la misma que esta respuesta .
Raymond Chen
77
Gracias por su tiempo, Raymond Chen, estamos aquí para tratar de resolver o sugerir algo que pueda ayudar a otros. En este caso, creo que respondí la pregunta del tema: "¿Hay alguna forma de escribir en este registro de eventos: O al menos, algún otro registro predeterminado de Windows, donde no tengo que registrar un origen de eventos?". -> Respondí: Sí, lo compartí contigo. A pesar del hecho de que podría causar conflictos como dijiste, existe una manera.
cloud120
77
Está respondiendo la pregunta "¿Hay alguna manera de hacerlo sin registrar una fuente de evento?" y su respuesta dice "Cree esta clave de registro para registrar un origen de evento". También es idéntico a una respuesta existente.
Raymond Chen
14

Puede usar la clase EventLog, como se explica en Cómo: Escribir en el registro de eventos de la aplicación (Visual C #) :

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

Sin embargo, deberá configurar esta fuente "MySource" utilizando privilegios administrativos:

Use WriteEvent y WriteEntry para escribir eventos en un registro de eventos. Debe especificar un origen de evento para escribir eventos; debe crear y configurar el origen del evento antes de escribir la primera entrada con el origen.

CodeCaster
fuente
2
Este es el problema que tengo: no puedo crear la fuente porque no tengo esos privilegios, pero todavía necesito registrar ESE problema en alguna parte
Jerther
2
Luego use un instalador ( stackoverflow.com/questions/1484605/… ) o inicie sesión en el archivo.
CodeCaster
1
Gracias. Esto me llevó a esta otra pregunta SO: stackoverflow.com/questions/3930529/…
Jerther
@CodeCaster: ¿desde dónde podemos acceder a estos registros? Me refiero a la ubicación donde se almacena?
Arvind Chourasiya
1
@Arvind esa pregunta no tiene nada que ver con mi respuesta y es una pregunta completamente nueva.
CodeCaster
11

Como se indica en MSDN (por ejemplo, https://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog(v=vs.110).aspx ), verificar una fuente no existente y crear una fuente requiere administración privilegio.

Sin embargo, es posible utilizar la fuente "Aplicación" sin. Sin embargo, en mi prueba en Windows 2012 Server r2, obtengo la siguiente entrada de registro utilizando la fuente "Aplicación":

No se puede encontrar la descripción del evento ID xxxx de la aplicación de origen. El componente que genera este evento no está instalado en su computadora local o la instalación está dañada. Puede instalar o reparar el componente en la computadora local. Si el evento se originó en otra computadora, la información de la pantalla debía guardarse con el evento. La siguiente información se incluyó con el evento: {mi mensaje de entrada de evento} el recurso del mensaje está presente pero el mensaje no se encuentra en la tabla de cadena / mensaje

Definí el siguiente método para crear la fuente:

    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 estoy llamando con currentAppName = AppDomain.CurrentDomain.FriendlyName

Puede ser posible usar la clase EventLogPermission en lugar de este intento / captura, pero no estoy seguro de que podamos evitar la captura.

También es posible crear la fuente externamente, por ejemplo, en Powershell elevado:

New-EventLog -LogName Application -Source MyApp

Luego, usar 'MyApp' en el método anterior NO generará una excepción y EventLog se puede crear con esa fuente.

EricBDev
fuente
10

Esta es la clase de registrador que uso. El método privado Log () tiene EventLog.WriteEntry(), que es cómo realmente escribe en el registro de eventos. Incluyo todo este código aquí porque es útil. Además del registro, esta clase también se asegurará de que el mensaje no sea demasiado largo para escribir en el registro de eventos (truncará el mensaje). Si el mensaje fuera demasiado largo, obtendría una excepción. La persona que llama también puede especificar la fuente. Si la persona que llama no lo hace, esta clase obtendrá la fuente. Espero eso ayude.

Por cierto, puede obtener un ObjectDumper de la web. No quería publicar todo eso aquí. Tengo el mío de aquí: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;
        }
    }
}
Bob Horn
fuente
3
Y este código muestra eso. ¿Qué hay de malo en compartir esto con él? ¿No podría ser útil para el OP y otros?
Bob Horn
55
No puede escribir en el registro de eventos sin crear un origen de eventos , por lo que este código no lo muestra.
CodeCaster
2
Todavía necesitaría crear la fuente del evento, pero publicó su respuesta antes de que se actualizara el título de la pregunta. Aún así, no sabía sobre el límite de longitud, gracias.
Jerther
-4

tratar

   System.Diagnostics.EventLog appLog = new System.Diagnostics.EventLog();
   appLog.Source = "This Application's Name";
   appLog.WriteEntry("An entry to the Application event log.");
ITevfik
fuente
3
esto necesita registrar un origen de evento y, por lo tanto, no responde la pregunta. lo siento.
Jerther
La idea principal de esta pregunta es utilizar el origen del evento "Aplicación".
rcarrillopadron