Unique Generation EventId

I use the Windows event log to record some events. Events in the Windows Event Log can be assigned multiple properties. One of them is EventID.

Now I want to use EventId to try to group related errors. I could just pick a number for each call to the logging method I make, but that seems a little tedious.

I want the system to do this automatically. He will select eventId, which is "unique" for the position in the code in which the registration event occurred. Now there are only 65,536 unique event identifiers, so the likelihood of their collision can be rare enough to make EventId a useful way to group errors.

One strategy would be to take a stacktrace hash code, but that would mean that the first and second calls in the following code would generate the same event id.

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

I thought about getting to the call stack using the StackFrame class, which has the GetFileLineNumber method. The problem with this strategy is that it will only work when building using debug symbols. I also need to work with production code.

Does anyone have any ideas?

+4
source share
6 answers

IL offset number is available without debug symbols. Combined with stack and hash info, I think that would do the trick.

Here's an article that, in particular, covers getting the IL offset (in order to register it for offline correspondence to PDB files, there are different problems, but I think it will show you what you need):

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

+3
source

Here is what code you can use to create an EventID with the properties that I describe in my question:

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

A call to the frame.GetILOffset () method gives a position inside this particular frame at runtime.

I combine these offsets with the entire stack to give a unique string for the current position within the program.

Finally, since there are only 65,536 unique event identifiers that I logically AND hash against 0xFFFF to extract the least significant 16-bits. This value becomes EventId.

+5
source

Create a hash using the ILOffset of the last but one stack frame instead of the line number (i.e. the stack frame of your TestLog method above).

+1
source

* Important: this article focuses on solving the root cause of what seems to be your problem, rather than offering the solution that you specifically requested. I understand that this post is outdated, but it was important to contribute. *

My team had a similar problem, and we changed the log management method, which significantly reduced production support and bug fixes. This works pragmatically in most enterprise applications that my team runs on:

  • Prefix log messages named "class name". "function name".
  • For true errors, capture the caught exception in the event log.
  • Focus on having clear messages as part of a peer-to-peer code overview rather than an event identifier.
  • Use a unique event identifier for each function, just scroll from top to bottom and click on them.
  • when it becomes impractical to encode each function with a different event identifier, each class should simply have a unique one (collisions are damned).
  • Use event categories to reduce the likelihood of an event when filtering logs

Of course, it is important how big your applications are and how sensitive the data is. Most of us are about 10 thousand. Up to 500 thousand. Lines of code with minimally sensitive information. It may feel simplistic, but from the point of view of KISS it works pragmatically.

Speaking of which, using the abstract event log class to simplify the process makes it easier to use, although cleaning up is unpleasant. For instance:

MyClass.cs (using a wrapper)

 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
source

Thanks for the idea of ​​hashing the call stack, I was about to ask the same question on how to select eventId.

I recommend putting a static variable in LogSomething, which increments every time it is called.

0
source

Now I want to use EventId to try and group related errors.

You have filters in the event viewer, so why (Go to find?) Do you have 65,536 unique event identifiers.

Or rather use log4net or something ??

only my ideas ....

-1
source

Source: https://habr.com/ru/post/1285762/


All Articles