"Sie müssen die Ereignisquelle erstellen und konfigurieren, bevor Sie den ersten Eintrag mit der Quelle schreiben."
Jerther
Scheint, ich kann nicht. Gibt es eine gute Fallback-Methode, um zu warnen, dass die Anwendung nicht in die Windows-Protokolle schreiben kann? Eine flache Datei scheint gut, aber wo? Der Anwendungsordner würde noch einige Berechtigungen benötigen. Meine Anwendung ist ein Windows-Dienst.
Jerther
3
Wenn Ihre Anwendung ein Windows-Dienst ist, wird automatisch eine Ereignisquelle für Sie erstellt. Sie können darauf zugreifen ServiceBase.EventLog. Der Standardname der Quelle ist der Dienstname.
Mike Zboray
Antworten:
247
Ja, es gibt eine Möglichkeit, in das gesuchte Ereignisprotokoll zu schreiben. Sie müssen keine neue Quelle erstellen, sondern verwenden einfach die vorhandene, die häufig denselben Namen wie der Name des EventLogs hat und in einigen Fällen wie die Ereignisprotokollanwendung auch ohne Administratorrechte * zugänglich ist.
* Andere Fälle, in denen Sie nicht direkt darauf zugreifen können, sind beispielsweise das Security EventLog, auf das nur das Betriebssystem zugreift.
Ich habe diesen Code verwendet, um direkt in das Ereignisprotokoll zu schreiben. Anwendung:
using (EventLog eventLog = new EventLog("Application"))
{
eventLog.Source = "Application";
eventLog.WriteEntry("Log message example", EventLogEntryType.Information, 101, 1);
}
Wie Sie sehen können, entspricht die EventLog-Quelle dem Namen des EventLog. Der Grund hierfür ist in Event Sources @ Windows Dev Center zu finden (ich habe den Teil fett gedruckt, der sich auf den Quellennamen bezieht):
Jedes Protokoll im Ereignisprotokollschlüssel enthält Unterschlüssel, die als Ereignisquellen bezeichnet werden. Die Ereignisquelle ist der Name der Software, die das Ereignis protokolliert. Dies ist häufig der Name der Anwendung oder der Name einer Unterkomponente der Anwendung, wenn die Anwendung groß ist. Sie können der Registrierung maximal 16.384 Ereignisquellen hinzufügen.
Der von Ihnen zitierte Text besagt jedoch, dass Sie die Ereignisquelle unter dem Ereignisprotokollschlüssel registrieren müssen.
Raymond Chen
1
Ich habe damit gemeint, dass der Name des Ereignisprotokolls häufig mit dem Namen der Anwendung identisch ist. Aus diesem Grund können Sie einen Ereignisprotokolleintrag direkt im EventLog registrieren, ohne eine neue Quelle zu erstellen. Ich habe den zitierten Text für weitere Lesungen fett gedruckt.
Cloud120
3
Technisch der Akt des Registrierungsschlüssels zu schaffen ist die Registrierung der Ereignisquelle. Das Benennen des Schlüssels nach dem Anwendungsnamen ist eine Konvention, um Konflikte zu vermeiden. Ihre Antwort ist im Grunde die gleiche wie diese Antwort .
Raymond Chen
7
Vielen Dank für Ihre Zeit, Raymond Chen. Wir sind hier, um zu versuchen, etwas zu lösen oder vorzuschlagen, das anderen helfen könnte. In diesem Fall habe ich die Themenfrage beantwortet: "Gibt es eine Möglichkeit, in dieses Ereignisprotokoll zu schreiben: Oder zumindest in ein anderes Windows-Standardprotokoll, in dem ich keine Ereignisquelle registrieren muss?". -> Ich antwortete: Ja, und ich habe es mit Ihnen geteilt. Trotz der Tatsache, dass es Konflikte geben könnte, wie Sie sagten, gibt es einen Weg.
Cloud120
7
Sie beantworten die Frage "Gibt es eine Möglichkeit, dies zu tun, ohne eine Ereignisquelle zu registrieren?" und Ihre Antwort lautet "Erstellen Sie diesen Registrierungsschlüssel, um eine Ereignisquelle zu registrieren." Es ist auch identisch mit einer vorhandenen Antwort.
var appLog = new EventLog("Application");
appLog.Source = "MySource";
appLog.WriteEntry("Test log message");
Sie müssen diese Quelle "MySource" jedoch mit Administratorrechten konfigurieren :
Verwenden Sie WriteEvent und WriteEntry, um Ereignisse in ein Ereignisprotokoll zu schreiben. Sie müssen eine Ereignisquelle angeben, um Ereignisse zu schreiben. Sie müssen die Ereignisquelle erstellen und konfigurieren, bevor Sie den ersten Eintrag mit der Quelle schreiben.
Es ist jedoch möglich, die Quelle "Anwendung" ohne zu verwenden. In meinem Test unter Windows 2012 Server r2 erhalte ich jedoch den folgenden Protokolleintrag unter Verwendung der Quelle "Anwendung":
Die Beschreibung für die Ereignis-ID xxxx aus der Quellanwendung wurde nicht gefunden. Entweder ist die Komponente, die dieses Ereignis auslöst, nicht auf Ihrem lokalen Computer installiert, oder die Installation ist beschädigt. Sie können die Komponente auf dem lokalen Computer installieren oder reparieren. Wenn das Ereignis von einem anderen Computer stammt, mussten die Anzeigeinformationen mit dem Ereignis gespeichert werden. Die folgenden Informationen wurden in das Ereignis aufgenommen: {Meine Ereigniseintragsnachricht} Die Nachrichtenressource ist vorhanden, aber die Nachricht wurde nicht in der Zeichenfolge / Nachrichtentabelle gefunden
Ich habe die folgende Methode definiert, um die Quelle zu erstellen:
privatestringCreateEventSource(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;
}
Ich rufe es mit currentAppName = AppDomain.CurrentDomain.FriendlyName auf
Es ist möglicherweise möglich, die EventLogPermission-Klasse anstelle dieses try / catch zu verwenden, aber wir sind uns nicht sicher, ob wir den catch vermeiden können.
Es ist auch möglich, die Quelle extern zu erstellen, z. B. in erhöhter Powershell:
New-EventLog -LogName Application -Source MyApp
Wenn Sie dann 'MyApp' in der obigen Methode verwenden, wird KEINE Ausnahme generiert, und das EventLog kann mit dieser Quelle erstellt werden.
Dies ist die Logger-Klasse, die ich verwende. Die private Log () -Methode enthält EventLog.WriteEntry(), wie Sie tatsächlich in das Ereignisprotokoll schreiben. Ich füge den gesamten Code hier ein, weil er praktisch ist. Zusätzlich zur Protokollierung stellt diese Klasse auch sicher, dass die Nachricht nicht zu lang ist, um in das Ereignisprotokoll geschrieben zu werden (sie schneidet die Nachricht ab). Wenn die Nachricht zu lang war, erhalten Sie eine Ausnahme. Der Anrufer kann auch die Quelle angeben. Wenn der Aufrufer dies nicht tut, erhält diese Klasse die Quelle. Ich hoffe es hilft.
Übrigens können Sie einen ObjectDumper aus dem Web erhalten. Ich wollte das alles nicht hier posten. Ich habe meine von hier: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;
namespaceXanico.Core
{
///<summary>/// Logging operations///</summary>publicstaticclassLogger
{
// 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.privateconstint 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>publicstaticstring 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>publicstaticvoidLogDebug(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>publicstaticvoidLogInformation(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>publicstaticvoidLogWarning(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>publicstaticvoidLogException(Exception ex, string source = "")
{
if (ex == null) { thrownew 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")]
publicstaticvoidLogObjectDump(object theObject, string objectName, string source = "")
{
constint 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);
}
privatestaticvoidLog(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);
}
}
privatestaticstringGetSource()
{
// 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.privatestaticstringEnsureLogMessageLimit(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;
}
}
}
Und dieser Code zeigt das. Was schadet es, das mit ihm zu teilen? Könnte es nicht hilfreich für das OP und andere sein?
Bob Horn
6
Sie können nicht in das Ereignisprotokoll schreiben, ohne eine Ereignisquelle zu erstellen. Dieser Code zeigt dies also nicht an.
CodeCaster
2
Ich müsste noch die Ereignisquelle erstellen, aber Sie haben Ihre Antwort gepostet, bevor der Fragentitel aktualisiert wurde. Trotzdem wusste ich nichts über das Längenlimit, danke.
Jerther
-4
Versuchen
System.Diagnostics.EventLog appLog = new System.Diagnostics.EventLog();
appLog.Source = "This Application's Name";
appLog.WriteEntry("An entry to the Application event log.");
ServiceBase.EventLog
. Der Standardname der Quelle ist der Dienstname.Antworten:
Ja, es gibt eine Möglichkeit, in das gesuchte Ereignisprotokoll zu schreiben. Sie müssen keine neue Quelle erstellen, sondern verwenden einfach die vorhandene, die häufig denselben Namen wie der Name des EventLogs hat und in einigen Fällen wie die Ereignisprotokollanwendung auch ohne Administratorrechte * zugänglich ist.
* Andere Fälle, in denen Sie nicht direkt darauf zugreifen können, sind beispielsweise das Security EventLog, auf das nur das Betriebssystem zugreift.
Ich habe diesen Code verwendet, um direkt in das Ereignisprotokoll zu schreiben. Anwendung:
using (EventLog eventLog = new EventLog("Application")) { eventLog.Source = "Application"; eventLog.WriteEntry("Log message example", EventLogEntryType.Information, 101, 1); }
Wie Sie sehen können, entspricht die EventLog-Quelle dem Namen des EventLog. Der Grund hierfür ist in Event Sources @ Windows Dev Center zu finden (ich habe den Teil fett gedruckt, der sich auf den Quellennamen bezieht):
quelle
Sie können die EventLog-Klasse verwenden, wie unter Gewusst wie : Schreiben in das Anwendungsereignisprotokoll (Visual C #) erläutert :
var appLog = new EventLog("Application"); appLog.Source = "MySource"; appLog.WriteEntry("Test log message");
Sie müssen diese Quelle "MySource" jedoch mit Administratorrechten konfigurieren :
quelle
Wie in MSDN angegeben (z. B. https://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog(v=vs.110).aspx ), erfordert das Überprüfen einer nicht vorhandenen Quelle und das Erstellen einer Quelle den Administrator Privileg.
Es ist jedoch möglich, die Quelle "Anwendung" ohne zu verwenden. In meinem Test unter Windows 2012 Server r2 erhalte ich jedoch den folgenden Protokolleintrag unter Verwendung der Quelle "Anwendung":
Ich habe die folgende Methode definiert, um die Quelle zu erstellen:
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; }
Ich rufe es mit currentAppName = AppDomain.CurrentDomain.FriendlyName auf
Es ist möglicherweise möglich, die EventLogPermission-Klasse anstelle dieses try / catch zu verwenden, aber wir sind uns nicht sicher, ob wir den catch vermeiden können.
Es ist auch möglich, die Quelle extern zu erstellen, z. B. in erhöhter Powershell:
Wenn Sie dann 'MyApp' in der obigen Methode verwenden, wird KEINE Ausnahme generiert, und das EventLog kann mit dieser Quelle erstellt werden.
quelle
Dies ist die Logger-Klasse, die ich verwende. Die private Log () -Methode enthält
EventLog.WriteEntry()
, wie Sie tatsächlich in das Ereignisprotokoll schreiben. Ich füge den gesamten Code hier ein, weil er praktisch ist. Zusätzlich zur Protokollierung stellt diese Klasse auch sicher, dass die Nachricht nicht zu lang ist, um in das Ereignisprotokoll geschrieben zu werden (sie schneidet die Nachricht ab). Wenn die Nachricht zu lang war, erhalten Sie eine Ausnahme. Der Anrufer kann auch die Quelle angeben. Wenn der Aufrufer dies nicht tut, erhält diese Klasse die Quelle. Ich hoffe es hilft.Übrigens können Sie einen ObjectDumper aus dem Web erhalten. Ich wollte das alles nicht hier posten. Ich habe meine von hier:
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; } } }
quelle
Versuchen
System.Diagnostics.EventLog appLog = new System.Diagnostics.EventLog(); appLog.Source = "This Application's Name"; appLog.WriteEntry("An entry to the Application event log.");
quelle