我正在尝试弄清楚如何管理我的事件 ID。到目前为止,我一直在手动将每个事件 id 放入每个方法中,方法中的每个步骤都按顺序编号。这不允许我有效地过滤事件日志中的事件。为了在事件日志中使用过滤器,似乎每个记录的事件都必须有自己的唯一 ID。
我可以将它们全部存储在一个带有链接到它们的描述的表中,但是当我的代码执行时,我正在记录“魔法”无意义的事件代码。
我进行了谷歌搜索,但我似乎不知道使用正确的关键字来查明这个问题的根源。
提前致谢
就像 Ben 的建议一样,使用间接级别可能值得 - 但不是使用 int 作为代码,而是使用实际的枚举,因此对于 Ben 的示例:
public enum EventId
{
[Format("Building command object from {0}.")]
BuildingCommandObject = 1,
[Format("Command object build successfully.")]
CommandObjectBuilt = 2,
[Format("Connecting to {0}.")]
ConnectingToDatabase = 3,
[Format("Executing command against database {0}.")]
ExecutingCommand = 4,
[Format("Command executed successfully.")]
CommandExecuted = 5,
[Format("Disconnecting from {0}.")]
DisconnectingFromDatabase = 6,
[Format("Connection terminated")]
Disconnected = 7
}
或者(并且以更面向对象的方式)使用“智能枚举”模式):
public class LogEvent
{
public static readonly LogEvent BuildingCommandObject = new LogEvent(1,
"Building command object from {0}");
// etc
private readonly int id;
private readonly string format;
// Add the description if you want
private LogEvent(int id, string format)
{
this.id = id;
this.format = format;
}
public void Log(params object[] data)
{
string message = string.Format(format, data);
// Do the logging here
}
}
然后您可以致电:
LogEvent.BuildingCommandObject.Log("stuff");
通过一些工作,您可以通过具有不同接口的不同日志事件以安全的方式公开这一点,以使其在每个参数有多少参数方面安全(在编译时)。事实上,我确信你可以使用接口和私有嵌套类来做到这一点,但现在是凌晨 4 点,我太累了,无法在 atm 中写出来 :)
首先想到 - 我还没有完全考虑过这一点,但这似乎是一个合理的可能性:
public class LogEvent
{
/* This is the event code you reference from your code
* so you're not working with magic numbers. It will work
* much like an enum */
public string Code;
/* This is the event id that's published to the event log
* to allow simple filtering for specific events */
public int Id;
/* This is a predefined string format that allows insertion
* of variables so you can have a descriptive text template. */
public string DisplayFormat;
/* A constructor to allow you to add items to a collection in
* a single line of code */
public LogEvent(int id, string code, string displayFormat)
{
Code = code;
Id = id;
DisplayFormat = displayFormat;
}
public LogEvent(int id, string code)
: this(id, code, null)
{
}
public LogEvent()
{
}
}
然后,您可以拥有一个事件管理器类,该类包装您的事件列表,提供一个根据您传递的参数查询列表的方法 - 例如:
public class EventManager
{
private List<LogEvent> _eventList;
public LogEvent this[string eventCode]
{
get
{
return _eventList.Where(i => i.Code.Equals(eventCode)).SingleOrDefault();
}
}
public LogEvent this[int id]
{
get
{
return _eventList.Where(i => i.Id.Equals(id)).SingleOrDefault();
}
}
public void AddRange(params LogEvent[] logEvents)
{
Array.ForEach(logEvents, AddEvent);
}
public void Add(int id, string code)
{
AddEvent(new LogEvent(id, code));
}
public void Add(int id, string code, string displayFormat)
{
AddEvent(new LogEvent(id, code, displayFormat));
}
public void Add(LogEvent logEvent)
{
_events.Add(logEvent);
}
public void Remove(int id)
{
_eventList.Remove(_eventList.Where(i => i.id.Equals(id)).SingleOrDefault());
}
public void Remove(string code)
{
_eventList.Remove(_eventList.Where(i => i.Code.Equals(code)).SingleOrDefault());
}
public void Remove(LogEvent logEvent)
{
_eventList.Remove(logEvent);
}
}
这允许简化事件定义的管理,可以为每个 TraceSource 独立管理。
var Events = new EventManager();
Events.AddRange(
new LogEvent(1, "BuildingCommandObject", "Building command object from {0}."),
new LogEvent(2, "CommandObjectBuilt", "Command object built successfully."),
new LogEvent(3, "ConnectingToDatabase", "Connecting to {0}."),
new LogEvent(4, "ExecutingCommand", "Executing command against database {0}".),
new LogEvent(5, "CommandExecuted", "Command executed succesfully."),
new LogEvent(6, "DisconnectingFromDatabase", "Disconnecting from {0}."),
new LogEvent(7, "Disconnected", "Connection terminated.")
)
您可以使用您分配的有意义的标识符访问事件:
var evt = Events["ConnectingToDatabase"];
TraceSource.TraceEvent(TraceEventType.Information, evt.Id, evt.DisplayFormat, otherParams);
或者
var evt = Events[1024];
Console.WriteLine("Id: {1}{0}Code: {2}{0}DisplayFormat{3}",
Environment.NewLine, evt.Id, evt.Code, evt.DisplayFormat);
这可能会简化您的事件管理,您不再通过幻数调用事件,在一个地方管理所有事件很简单 - 您的 EventManager 类,您仍然可以通过它需要您的幻数过滤事件日志过滤。
我知道这是一个老问题,但也许您正在寻找一种方法来做这样的事情,您将自定义事件 ID 用于不同目的,并在代码中的适当位置调用它们:
public class ErrorLog
{
//Notifications
public const int NOTIFY_ALPHA = 2000;
public const int NOTIFY_BETA = 2001;
public const int NOTIFY_GAMMA = 2002;
public static string[] errMessage =
{"Critical Error.", //2000
"File not found.", //2001
"Unknown Event Action Encountered - " //2002
};
public static string GetErrMsg(int errNum)
{
return (errMessage[errNum-2000]);
}
private static bool ErrorRoutine(int eventLogId)
{
try
{
string eventAppName = "My Application";
string eventLogName = "My Apps Events";
string msgStr = GetErrMsg(eventLogId); // gets the message associated with the ID from the constant you passed in
if (!EventLog.SourceExists(eventAppName))
EventLog.CreateEventSource(eventAppName, eventLogName);
EventLog.WriteEntry(eventAppName, msgStr, EventLogEntryType.Error, eventLogId);
return true;
}
catch (Exception)
{
return false;
}
}
}
然后你会这样调用这个类,当你抛出你的异常时:
ErrorLog.ErrorRoutine(ErrorLog.NOTIFY_ALPHA);
就最佳实践而言,我会说最好将所有错误处理都放在自己的类中,如果它是自定义的(或者更多,例如在绑定警告和信息 EventLogEntryTypes 或附加信息时而不是罐头消息给出) 比这个。拥有单独的 ID,您可以像这样查看他们的消息,这将使您在尝试整理要呼叫的消息、时间和地点时更轻松。