写入Windows应用程序事件日志


164

有没有办法写入此事件日志:

在此处输入图片说明

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




1
“必须先创建并配置事件源,然后才能使用事件源写入第一个条目。”
Jerther 2014年

看来我做不到。因此,是否有一个好的后备方法来警告应用程序无法写入Windows日志?平面文件看起来不错,但是在哪里?应用程序文件夹仍将需要一些权限。我的应用程序是Windows服务。
Jerther 2014年

3
如果您的应用程序是Windows服务,则会自动为您创建一个事件源。您可以通过访问它ServiceBase.EventLog。Source的默认名称是ServiceName。
Mike Zboray 2014年

Answers:


236

是的,有一种方法可以写入您要查找的事件日志。您不需要创建新的源,只需简单地使用现有的源,该源通常与EventLog的名称相同,并且在某些情况下,例如事件日志Application,可以在没有管理特权的情况下进行访问*。

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

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

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
但是您引用的文本说您必须在事件日志项下注册事件源。
Raymond Chen

1
我的意思是事件日志的名称通常与应用程序的名称相同,因此这就是为什么您可以在不创建新源的情况下直接将事件日志条目注册到EventLog的原因。我将加粗的文字加粗,以供进一步阅读。
cloud120 2014年

3
从技术上讲,创建注册表项的行为注册事件源。为避免冲突,在应用程序名称后命名密钥是一种约定。您的答案与该答案基本相同。
Raymond Chen

7
感谢您的宝贵时间,Raymond Chen,我们在这里尝试解决或提出可能对他人有所帮助的建议。在这种情况下,我相信我已经回答了以下主题问题:“是否可以写入该事件日志:或者至少是其他一些Windows默认日志,而我不必注册事件源?”。->我回答:是的,我和你分享了。尽管它可能会引起您所说的冲突,但它是存在的一种方法。
cloud120 2014年

7
您正在回答“没有注册事件源的方法吗?”的问题。并且您的回答是“创建此注册表项以注册事件源”。它也与现有答案相同。
雷蒙·陈

14

您可以使用EventLog类,如如何:写入应用程序事件日志(Visual C#)中所述

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

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

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


2
这是我遇到的问题:我无法创建源代码,因为我没有那些特权,但是我仍然需要在某个地方记录该问题
Jerther

2
然后使用安装程序(stackoverflow.com/questions/1484605/…)或登录到文件。
CodeCaster

1
谢谢。这使我这个其他SO问题:stackoverflow.com/questions/3930529/...
Jerther

@CodeCaster-从哪里可以访问这些日志?我的意思是它存储的位置?
Arvind Chourasiya

1
@Arvind这个问题与我的答案无关,完全是一个全新的问题。
CodeCaster

11

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

但是,可以不使用源“应用程序”。但是,在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。


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
这段代码说明了这一点。与他分享这有什么害处?对OP和其他人有用吗?
Bob Horn 2014年

5
如果不创建事件源,则无法写入事件日志,因此此代码不会显示该事件。
CodeCaster

2
我仍然需要创建事件源,但是您在更新问题标题之前发布了答案。仍然,我不知道长度限制,谢谢。
Jerther 2014年

-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
这需要注册一个事件源,因此无法回答问题。抱歉。
Jerther 2014年

这个问题的主要思想是使用“应用程序”事件源。
rcarrillopadron
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.