C#写Windows系统日志(EventLog)
.NET framework 2.0开始,可以使用 EventLog构造函数在Windows系统日志里添加内容,一切看上去是如此的简单。只是……
会涉及到一点点的权限问题,必须要注意一下。
使用 EventLog时,涉及到一个叫做“来源”(source)的参数(一般用来记录消息的出处)。比如将该参数设为“MyEXE”,那么写系统日志的时候,EventLog.WriteEntry就会去注册表里找这个叫做“MyEXE”的节点,如果找不到,那它会创建一个 “MyEXE”节点作为来源,然后开始写入系统日志。的确是很人性化的处理办法。只是,要在注册表里建立一个节点,没有相当的权限是不行的,比如说系统管理员的权限。偏偏写系统日志的通常是一些服务账户(例如:NET SERVICE),这些账户往往不会有这么大的权限。
解决方案的话。
1. 事先在注册表里创建要使用的事件源。
1. 点击“开始”,再点击“运行”。
2. 在“打开”框中输入“regedit”。
3. 找到下列子键:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLogs\Application
4. 右击“Application”点击“新建”再点“项”(各个参数可以参照同层的其他节点。)
5. 关闭注册表编辑器
2. 代码中模拟管理员权限(个人不是很喜欢,因为要输入密码……)
下面是使用 EventLog的一个例子。
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Security;
using System.Text;
namespace ConsoleApplication2
{
/// <summary>
/// 写系统日志
/// </summary>
public class AppLog
{
/// <summary>
/// 系统日志所在的主机名。当前设定:本地电脑
/// </summary>
public const string MACHINE_NAME = ".";
/// <summary>
/// 系统日志名。当前设定:应用程序
/// </summary>
public const string LOG_NAME = "Application";
/// <summary>
/// 消息事件类型。当前设定:无
/// 该值为0的时候分类为“无”。
/// </summary>
public const short CATEGORY = 0;
/// <summary>
/// 消息事件的种类:信息(Information),警告(Warning),重大错误(Error)
/// </summary>
public enum LogType
{
/// <summary>
/// 重大错误
/// </summary>
Error = 1,
/// <summary>
/// 警告
/// </summary>
Warning = 2,
/// <summary>
/// 情報
/// </summary>
Information = 4
}
/// <summary>
///
/// </summary>
static AppLog()
{
}
/// <summary>
/// 向日志管理器写入系统日志
/// </summary>
/// <param name="source">消息事件来源</param>
/// <param name="message">要在消息事件中写入的信息</param>
/// <param name="type">消息事件类型</param>
/// <param name="eventID">消息事件的事件ID。(0~65535)</param>
/// <exception cref="System.ComponentModel.Win32Exception">系统日志存储空间不足时</exception>
/// <exception cref="System.Security.SecurityException">操作系统日志权限不足时</exception>
static public void WriteEntry(string source, string message, LogType type, int eventID)
{
try
{
if (!EventLog.SourceExists(source))
{
EventLog.CreateEventSource(source, LOG_NAME);
}
EventLog.WriteEntry(source, message, GetLogEntryType(type), eventID, CATEGORY);
}
catch (SecurityException)
{
//碰到权限不够无法操作系统日志的情况时,模拟本地管理员权限进行操作。
//提示:当注册表中没有消息事件来源时,会发生该例外
//ImpersonateAccount类的代码参考鄙人另一篇文章C#模拟AD用户
//不要指望可以使用System.Diagnostics.EventLogInstaller类,使用它也需要本地系统管理员的权限
using (ImpersonateAccount sa = new ImpersonateAccount("<用户名>", "<域>", "<密码>"))
{
if (!EventLog.SourceExists(source))
{
EventLog.CreateEventSource(source, LOG_NAME);
}
EventLog.WriteEntry(source, message, GetLogEntryType(type), eventID, CATEGORY);
}
}
}
/// <summary>
/// 向日志管理器写入系统日志
/// </summary>
/// <param name="source">消息事件来源</param>
/// <param name="ex">要在消息事件中写入的信息</param>
/// <param name="eventID">消息事件的事件ID。(0~65535)</param>
static public void WriteEntry(string source, Exception ex, int eventID)
{
StringBuilder sb = new StringBuilder();
if (ex != null)
{
sb.AppendFormat("[Message]"n{0}"n", ex.Message);
sb.AppendFormat("[Source]"n{0}"n", ex.Source);
sb.AppendFormat("[TargetSite]"n{0}"n", ex.TargetSite);
sb.AppendFormat("[ToString]"n{0}"n", ex.ToString());
if (ex.Data.Count > 0)
{
sb.Append("[Data]"n");
foreach (System.Collections.DictionaryEntry var in ex.Data)
{
sb.AppendFormat(""t[{0}]:{1}"n", var.Key, var.Value);
}
}
}
WriteEntry(source, sb.ToString(), LogType.Error, eventID);
}
/// <summary>
/// 将LogType类型转换成EventLogEntryType类型
/// </summary>
/// <param name="type">LogType</param>
/// <returns>EventLogEntryType</returns>
/// <exception cref="System.ApplicationException">转换错误时抛出的例外类型</exception>
static private EventLogEntryType GetLogEntryType(LogType type)
{
Type t = typeof(EventLogEntryType);
if (Enum.IsDefined(t, (int)type))
{
return (EventLogEntryType)Enum.Parse(t, ((int)type).ToString());
}
else
{
StringBuilder sb = new StringBuilder();
sb.Append("LogType类型无法转换为EventLogEntryType类型");
sb.AppendFormat("(LogType:{0})", type);
throw new ApplicationException(sb.ToString());
}
}
}
}