生成唯一的事件ID

4
我正在使用Windows事件日志记录一些事件。 Windows事件日志中的事件可以分配几个属性之一,其中之一是EventID。
现在,我想使用EventId来尝试组合相关错误。我可以为每次调用日志记录方法选择一个数字,但这似乎有点繁琐。
我希望系统自动执行此操作。它将选择一个eventId,该eventId对于发生日志事件的代码位置是“唯一”的。现在,只有65536个唯一的事件ID,因此可能会发生冲突,但它们应该足够稀少,以使EventId成为一种有用的分组错误方式。
一种策略是获取堆栈跟踪的哈希码,但这意味着以下代码中的第一个和第二个调用将生成相同的事件ID。
public void TestLog()
{
   LogSomething("Moo");
   // Do some stuff and then a 100 lines later..
   LogSomething("Moo");
}

我考虑使用StackFrame类来遍历调用栈,它有一个GetFileLineNumber方法。但是这个策略的问题在于只有在开启了调试符号时才能使用,而我需要在生产代码中使用它。
有没有人有什么好的想法?

只是为了明确,您希望您示例中的这两个调用具有不同的事件ID吗? - Michael Haren
@Michael 是的,它们应该有不同的 EventIds,因为日志调用发生在不同的行上。我的问题是这是否可能? - Simon Johnson
6个回答

6

这里有一些代码,你可以使用它来生成一个包含我在问题描述中提到的属性的EventID:

 public static int GenerateEventId()
 {
     StackTrace trace = new StackTrace();

     StringBuilder builder = new StringBuilder();
     builder.Append(Environment.StackTrace);

     foreach (StackFrame frame in trace.GetFrames())
     {
           builder.Append(frame.GetILOffset());
           builder.Append(",");
     }

     return builder.ToString().GetHashCode() & 0xFFFF;
 }

frame.GetILOffset() 方法调用返回执行时该特定帧内的位置。

我将这些偏移量与整个堆栈跟踪连接起来,以便为程序中当前位置提供唯一字符串。

最后,由于只有65536个唯一的事件ID,我使用逻辑AND运算符将哈希码与0xFFFF相与,以提取最低有效的16位。此值然后成为EventId。


3

我不知道为什么错过了这个,但感谢。这正是我想要的。 - Simon Johnson

1

*重要提示:本帖旨在解决您问题的根本原因,而不是提供您特别要求的解决方案。我意识到这篇文章已经过时,但仍然觉得有必要做出贡献。*

我的团队曾遇到类似问题,我们改变了日志管理方式,显著减少了生产支持和错误修补时间。从实用角度来看,这适用于我们团队大多数企业应用程序:

  1. 使用“类名”。“函数名”前缀记录日志消息。
  2. 对于真正的错误,将捕获的异常输出到事件记录器中。
  3. 注重清晰的消息作为同行代码审查的一部分,而不是事件ID。
  4. 为每个函数使用唯一的事件ID,只需从上到下进行键入。
  5. 当编写每个函数的不同事件ID变得不切实际时,每个类应该只有一个唯一的事件ID(无论是否发生冲突)。
  6. 在过滤日志时,利用事件类别减少事件ID的依赖性。

当然,这取决于您的应用程序有多大以及数据的敏感程度。我们大多数应用程序都有10k到500k行代码,包含最少量的敏感信息。这可能看起来过于简单化,但从KISS的角度来看,它实用。

话虽如此,使用抽象的事件日志类来简化流程会使它容易使用,尽管清理可能不太愉快。例如:

MyClass.cs(使用包装器)

class MyClass
{
    // hardcoded, but should be from configuration vars
    private string AppName = "MyApp";
    private string AppVersion = "1.0.0.0";
    private string ClassName = "MyClass";
    private string LogName = "MyApp Log";

    EventLogAdapter oEventLogAdapter;
    EventLogEntryType oEventLogEntryType;

    public MyClass(){
        this.oEventLogAdapter = new EventLogAdapter(
              this.AppName
            , this.LogName
            , this.AppName
            , this.AppVersion
            , this.ClassName
        );
    }

    private bool MyFunction() {
        bool result = false;
        this.oEventLogAdapter.SetMethodInformation("MyFunction", 100);
        try {
            // do stuff
            this.oEventLogAdapter.WriteEntry("Something important found out...", EventLogEntryType.Information);

        } catch (Exception oException) {
            this.oEventLogAdapter.WriteEntry("Error: " + oException.ToString(), EventLogEntryType.Error);
        }
        return result;
    }
}

EventLogAdapter.cs

class EventLogAdapter
{
    //vars
    private string _EventProgram = "";
    private string _EventSource = "";
    private string _ProgramName = "";
    private string _ProgramVersion = "";
    private string _EventClass = "";
    private string _EventMethod = "";
    private int _EventCode = 1;
    private bool _Initialized = false;
    private System.Diagnostics.EventLog oEventLog = new EventLog();
    // methods
    public EventLogAdapter() {  }
    public EventLogAdapter(
          string EventProgram
        , string EventSource
        , string ProgramName
        , string ProgramVersion
        , string EventClass
    ) {
        this.SetEventProgram(EventProgram);
        this.SetEventSource(EventSource);
        this.SetProgramName(ProgramName);
        this.SetProgramVersion(ProgramVersion);
        this.SetEventClass(EventClass);
        this.InitializeEventLog();
    }
    public void InitializeEventLog() {
        try {
            if(
                !String.IsNullOrEmpty(this._EventSource)
                && !String.IsNullOrEmpty(this._EventProgram)
            ){
                if (!System.Diagnostics.EventLog.SourceExists(this._EventSource)) {
                    System.Diagnostics.EventLog.CreateEventSource(
                        this._EventSource
                        , this._EventProgram
                    );
                }
                this.oEventLog.Source = this._EventSource;
                this.oEventLog.Log = this._EventProgram;
                this._Initialized = true;
            }
        } catch { }
    }
    public void WriteEntry(string Message, System.Diagnostics.EventLogEntryType EventEntryType) {
        try {
            string _message = 
                "[" + this._ProgramName + " " + this._ProgramVersion + "]"
                + "." + this._EventClass + "." + this._EventMethod + "():\n"
                + Message;
            this.oEventLog.WriteEntry(
                  Message
                , EventEntryType
                , this._EventCode
            );
        } catch { }
    }
    public void SetMethodInformation(
        string EventMethod
        ,int EventCode
    ) {
        this.SetEventMethod(EventMethod);
        this.SetEventCode(EventCode);
    }
    public string GetEventProgram() { return this._EventProgram; }
    public string GetEventSource() { return this._EventSource; }
    public string GetProgramName() { return this._ProgramName; }
    public string GetProgramVersion() { return this._ProgramVersion; }
    public string GetEventClass() { return this._EventClass; }
    public string GetEventMethod() { return this._EventMethod; }
    public int GetEventCode() { return this._EventCode; }
    public void SetEventProgram(string EventProgram) { this._EventProgram = EventProgram; }
    public void SetEventSource(string EventSource) { this._EventSource = EventSource; }
    public void SetProgramName(string ProgramName) { this._ProgramName = ProgramName; }
    public void SetProgramVersion(string ProgramVersion) { this._ProgramVersion = ProgramVersion; }
    public void SetEventClass(string EventClass) { this._EventClass = EventClass; }
    public void SetEventMethod(string EventMethod) { this._EventMethod = EventMethod; }
    public void SetEventCode(int EventCode) { this._EventCode = EventCode; }

}

1

使用倒数第二个堆栈帧的ILOffset而非行号(即上面的TestLog方法的堆栈帧)创建哈希。


0
谢谢你提出使用哈希调用堆栈的想法,我正要问如何选择eventId。
我建议在LogSomething中放置一个静态变量,每次调用时递增。

-2
现在我想使用EventId来尝试并分组相关错误。
您的事件查看器中有过滤器,为什么要(去寻找?您也有65536个独特的事件ID。
或者使用log4net或其他东西?
这只是我的想法……

这有两个问题。首先,即使我使用了log4net,也无法解决我的分组问题。其次,我希望使用EventId是因为我想使用过滤器。这是一个非常差的答案,这就是为什么我投票反对它的原因。 - Simon Johnson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接