代码之家  ›  专栏  ›  技术社区  ›  Simon Johnson Andomar

唯一事件ID生成

  •  4
  • Simon Johnson Andomar  · 技术社区  · 17 年前

    我正在使用Windows事件日志记录一些事件。可以为Windows事件日志中的事件分配一些属性。其中之一是eventid。

    现在,我想使用eventid尝试对相关错误进行分组。我可以为我所做的每个日志记录方法调用选择一个数字,但这似乎有点乏味。

    我希望系统能自动完成这项工作。它将选择一个对发生日志事件的代码位置“唯一”的事件ID。现在,只有65536个唯一的事件ID,所以可能会发生冲突,但它们应该足够少,使事件ID成为分组错误的有用方法。

    一种策略是获取stacktrace的hashcode,但这意味着下面代码中的第一个和第二个调用将生成相同的事件ID。

    public void TestLog()
    {
       LogSomething("Moo");
       // Do some stuff and then a 100 lines later..
       LogSomething("Moo");
    }
    

    我考虑使用具有GetFileLineNumber方法的StackFrame类向上遍历调用堆栈。这个策略的问题在于,它只有在使用调试符号构建时才能工作。我也需要它在生产代码中工作。

    有人有什么想法吗?

    6 回复  |  直到 12 年前
        1
  •  3
  •   richardtallent    17 年前

    IL偏移量编号不带调试符号。结合栈信息和散列,我认为这可以做到。

    这里有一篇文章,部分内容涉及检索IL偏移量(为了将其记录为与PDB文件的脱机匹配——不同的问题,但我认为它将向您展示您需要的东西):

    http://timstall.dotnetdevelopersjournal.com/getting_file_and_line_numbers_without_deploying_the_pdb_file.htm

        2
  •  5
  •   Simon Johnson Andomar    17 年前

    下面是一些代码,您可以使用我在问题中描述的属性生成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;
     }
    

    getiloffset()方法调用在执行时给出特定帧中的位置。

    我将这些偏移量与整个stacktrace连接起来,为程序中的当前位置提供一个唯一的字符串。

    最后,因为只有65536个唯一的事件ID,所以逻辑和针对0xffff的散列码可以提取最低有效的16位。然后,该值变为eventid。

        3
  •  1
  •   Alan Moore    17 年前

    使用最后但只有一个堆栈帧的iloffset而不是行号(即上面testlog方法的堆栈帧)创建哈希。

        4
  •  1
  •   Dave    12 年前

    * 重要提示:这篇文章的重点是解决问题出现的根本原因,而不是提供您特别要求的解决方案。我意识到这篇文章已经过时了,但我觉得贡献自己很重要。*

    我的团队也遇到了类似的问题,我们改变了管理日志的方式,这大大减少了生产支持和错误修补时间。实际上,这适用于我的团队工作的大多数企业应用程序:

    1. 在日志消息前面加上“class name”.“function name”。
    2. 对于真正的错误,将捕获的异常输出到事件记录器。
    3. 重点是将清晰的消息作为对等代码评审的一部分,而不是事件ID。
    4. 为每个函数使用一个唯一的事件ID,只需从上到下对其进行键控。
    5. 当将每个函数编码为不同的事件ID变得不切实际时,每个类应该只有一个唯一的ID(冲突是可恶的)。
    6. 在筛选日志时,使用事件类别减少事件ID依赖性

    当然,你的应用程序有多大,数据有多敏感也很重要。我们中的大多数人大约有10到50万行的代码,并且信息的敏感度最低。它可能觉得过于简单化,但从亲吻的角度来看,它实际上是有效的。

    也就是说,使用一个抽象事件日志类来简化这个过程可以很容易地使用,尽管清除我的代码会让人不快。例如:

    MyCase.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;
        }
    }
    

    事件日志适配器.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; }
    
    }
    
        5
  •  0
  •   MatthewMartin muthu    17 年前

    多亏了对调用堆栈进行哈希处理的想法,我还想问同样的问题,如何选择一个eventid。

    我建议在日志中放入一个静态变量,每次调用该变量时都会递增。

        6
  •  -1
  •   abmv    17 年前

    现在我想用eventid来尝试 以及与组相关的错误。

    您在事件查看器中有过滤器,所以为什么(转到“查找”?您也有65536个唯一的事件ID。

    还是用log4net之类的??

    只是我的想法……