【问题标题】:Write to Windows Application Event Log写入 Windows 应用程序事件日志
【发布时间】:2014-11-01 17:24:38
【问题描述】:

有没有办法写入这个事件日志:

或者至少是其他一些 Windows 默认日志,我不必在其中注册事件源

【问题讨论】:

  • “您必须先创建和配置事件源,然后才能使用源写入第一个条目。”
  • 看来我做不到。那么,是否有一种好的后备方法来警告应用程序无法写入 Windows 日志?平面文件似乎不错,但是,在哪里?应用程序文件夹仍需要一些权限。我的应用程序是 Windows 服务。
  • 如果您的应用程序是 Windows 服务,则会自动为您创建一个事件源。您可以通过ServiceBase.EventLog 访问它。 Source 的默认名称是 ServiceName。

标签: c# .net windows logging event-log


【解决方案1】:

是的,有一种方法可以写入您要查找的事件日志。您不需要创建新的源,只需使用现有的源,它通常与 EventLog 的名称相同,并且在某些情况下,例如事件日志应用程序,无需管理权限即可访问*。

*其他无法直接访问的情况是安全事件日志,例如只能由操作系统访问。

我使用此代码直接写入事件日志应用程序:

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

如您所见,EventLog 源与 EventLog 的名称相同。原因可以在Event Sources @ Windows Dev Center中找到(引用源名称的部分我加粗了):

Eventlog 键中的每个日志都包含称为事件源的子键。事件源是记录事件的软件的名称。 通常是应用程序的名称,如果应用程序很大,则为应用程序子组件的名称。您最多可以向注册表添加 16,384 个事件源。

【讨论】:

  • 但是你引用的文字说你必须在事件日志键下注册事件源。
  • 我的意思是事件日志的名称通常与应用程序的名称相同,这就是为什么您可以将事件日志条目直接注册到事件日志而无需创建新源的原因。我将引用的文本加粗以供进一步阅读。
  • 从技术上讲,创建注册表项的行为注册事件源。在应用程序名称之后命名键是避免冲突的约定。你的回答和this answer基本一样。
  • 感谢您抽出宝贵的时间 Raymond Chen,我们在这里尝试解决或提出一些可能对其他人有帮助的建议。在这种情况下,我相信我回答了主题问题:“有没有办法写入此事件日志:或者至少是其他一些 Windows 默认日志,我不必在其中注册事件源?”。 -> 我回答:是的,我和你分享了。尽管它可能会引起你所说的冲突,但它存在一种方式。
  • 您正在回答问题“有没有办法在不注册事件源的情况下做到这一点?”并且您的回答是“创建此注册表项以注册事件源”。它也与现有答案相同。
【解决方案2】:

如 MSDN 中所述(例如 https://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog(v=vs.110).aspx ),检查不存在的源并创建源需要管理员权限。

但也可以不使用source "Application"。 然而,在我在 Windows 2012 Server r2 下的测试中,我使用“应用程序”源获得了以下日志条目:

找不到来自源应用程序的事件 ID 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。

【讨论】:

    【解决方案3】:

    您可以使用 EventLog 类,如 How to: Write to the Application Event Log (Visual C#) 中所述:

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

    但是,您需要使用管理权限配置此来源“MySource”:

    使用 WriteEvent 和 WriteEntry 将事件写入事件日志。必须指定事件源才能写入事件;在使用源编写第一个条目之前,您必须创建和配置事件源。

    【讨论】:

    • 这是我遇到的问题:我无法创建源代码,因为我没有这些权限,但我仍然需要在某处记录该问题
    • 然后使用安装程序 (stackoverflow.com/questions/1484605/…) 或登录到文件。
    • 谢谢。这让我想到了另一个 SO 问题:stackoverflow.com/questions/3930529/…
    • @CodeCaster - 我们可以从哪里访问这些日志?我的意思是它存储的位置?
    • @Arvind 这个问题与我的回答无关,完全是一个全新的问题。
    【解决方案4】:

    这是我使用的记录器类。私有 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;
            }
        }
    }
    

    【讨论】:

    • 这段代码显示了这一点。和他分享这些有什么害处?难道对OP和其他人没有帮助吗?
    • 你不能写入事件日志不创建事件源,所以这段代码没有显示。
    • 我仍然需要创建事件源,但您在更新问题标题之前发布了您的 anwser。不过,我不知道长度限制。
    【解决方案5】:

    试试

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

    【讨论】:

    • 这需要注册一个事件源,因此无法回答问题。对不起。
    • 本题的主要思路是使用“Application”事件源。
    猜你喜欢
    • 2011-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-29
    • 2021-05-24
    • 2011-08-13
    • 1970-01-01
    相关资源
    最近更新 更多