@@ -79,11 +79,15 @@ public virtual DisplayEventModel ResolveEvent(EventRecord eventRecord)
7979
8080 var keywords = GetKeywordsFromBitmask ( eventRecord ) ;
8181
82+ // Resolve the modern event once and reuse for both description and task name
83+ ProviderDetails . TryGetValue ( eventRecord . ProviderName , out var details ) ;
84+ var modernEvent = details is not null ? GetModernEvent ( eventRecord , details ) : null ;
85+
8286 return new DisplayEventModel ( eventRecord . PathName , eventRecord . PathType )
8387 {
8488 ActivityId = eventRecord . ActivityId ,
8589 ComputerName = _cache ? . GetOrAddValue ( eventRecord . ComputerName ) ?? eventRecord . ComputerName ,
86- Description = ResolveDescription ( eventRecord ) ,
90+ Description = ResolveDescription ( eventRecord , details , modernEvent ) ,
8791 Id = eventRecord . Id ,
8892 Keywords = keywords ,
8993 KeywordsDisplayName = string . Join ( ", " , keywords ) ,
@@ -92,7 +96,7 @@ public virtual DisplayEventModel ResolveEvent(EventRecord eventRecord)
9296 ProcessId = eventRecord . ProcessId ,
9397 RecordId = eventRecord . RecordId ,
9498 Source = _cache ? . GetOrAddValue ( eventRecord . ProviderName ) ?? eventRecord . ProviderName ,
95- TaskCategory = ResolveTaskName ( eventRecord ) ,
99+ TaskCategory = ResolveTaskName ( eventRecord , details , modernEvent ) ,
96100 ThreadId = eventRecord . ThreadId ,
97101 TimeCreated = eventRecord . TimeCreated ,
98102 UserId = eventRecord . UserId ,
@@ -322,6 +326,7 @@ private string FormatDescription(
322326 IEnumerable < MessageModel > parameters )
323327 {
324328 string returnDescription ;
329+
325330 if ( string . IsNullOrWhiteSpace ( descriptionTemplate ) )
326331 {
327332 // If there is only one property then this is what certain EventRecords look like
@@ -470,11 +475,10 @@ private List<string> GetKeywordsFromBitmask(EventRecord eventRecord)
470475
471476 foreach ( var k in s_standardKeywords . Keys )
472477 {
473- if ( ( eventRecord . Keywords . Value & k ) == k )
474- {
475- var keyword = s_standardKeywords [ k ] . TrimEnd ( '\0 ' ) ;
476- returnValue . Add ( _cache ? . GetOrAddValue ( keyword ) ?? keyword ) ;
477- }
478+ if ( ( eventRecord . Keywords . Value & k ) != k ) { continue ; }
479+
480+ var keyword = s_standardKeywords [ k ] . TrimEnd ( '\0 ' ) ;
481+ returnValue . Add ( _cache ? . GetOrAddValue ( keyword ) ?? keyword ) ;
478482 }
479483
480484 if ( ! ProviderDetails . TryGetValue ( eventRecord . ProviderName , out var details ) || details is null )
@@ -486,18 +490,16 @@ private List<string> GetKeywordsFromBitmask(EventRecord eventRecord)
486490 // so let's skip those.
487491 var lower32 = eventRecord . Keywords . Value & 0xFFFFFFFF ;
488492
489- if ( lower32 != 0 )
493+ if ( lower32 == 0 ) { return returnValue ; }
494+
495+ foreach ( var k in details . Keywords . Keys )
490496 {
491- foreach ( var k in details . Keywords . Keys )
492- {
493- if ( ( lower32 & k ) == k )
494- {
495- var keyword = details . Keywords [ k ] . TrimEnd ( '\0 ' ) ;
496- returnValue . Add ( _cache ? . GetOrAddValue ( keyword ) ?? keyword ) ;
497- }
498- }
499- }
497+ if ( ( lower32 & k ) != k ) { continue ; }
500498
499+ var keyword = details . Keywords [ k ] . TrimEnd ( '\0 ' ) ;
500+ returnValue . Add ( _cache ? . GetOrAddValue ( keyword ) ?? keyword ) ;
501+ }
502+
501503 return returnValue ;
502504 }
503505
@@ -513,10 +515,22 @@ private List<string> GetKeywordsFromBitmask(EventRecord eventRecord)
513515
514516 int eventPropertyCount = eventRecord . Properties . Count ;
515517
516- EventModel ? modernEvent = details . Events
517- . FirstOrDefault ( e => e . Id == eventRecord . Id &&
518- e . Version == eventRecord . Version &&
519- e . LogName == eventRecord . LogName ) ;
518+ // Use indexed lookup instead of linear scan
519+ var candidateEvents = details . GetEventsById ( eventRecord . Id ) ;
520+
521+ EventModel ? modernEvent = null ;
522+
523+ foreach ( var e in candidateEvents )
524+ {
525+ if ( e . Id != eventRecord . Id || e . Version != eventRecord . Version || e . LogName != eventRecord . LogName )
526+ {
527+ continue ;
528+ }
529+
530+ modernEvent = e ;
531+
532+ break ;
533+ }
520534
521535 if ( modernEvent is not null && DoesTemplateMatchPropertyCount ( modernEvent . Template , eventPropertyCount ) )
522536 {
@@ -532,8 +546,10 @@ private List<string> GetKeywordsFromBitmask(EventRecord eventRecord)
532546 LogLevel . Debug ) ;
533547 }
534548
535- foreach ( var @event in details . Events . Where ( e => e . Id == eventRecord . Id && e . LogName == eventRecord . LogName ) )
549+ foreach ( var @event in candidateEvents )
536550 {
551+ if ( @event . LogName != eventRecord . LogName ) { continue ; }
552+
537553 if ( ! DoesTemplateMatchPropertyCount ( @event . Template , eventPropertyCount ) ) { continue ; }
538554
539555 Logger ? . Trace ( $ "{ nameof ( GetModernEvent ) } : Match by Id/LogName with template - EventId={ eventRecord . Id } , LogName={ eventRecord . LogName } , MatchedVersion={ @event . Version } ",
@@ -544,8 +560,10 @@ private List<string> GetKeywordsFromBitmask(EventRecord eventRecord)
544560
545561 // Try again forcing the long to a short and with no log name.
546562 // This is needed for providers such as Microsoft-Windows-Complus
547- foreach ( var @event in details . Events . Where ( e => ( short ) e . Id == eventRecord . Id && e . Version == eventRecord . Version ) )
563+ foreach ( var @event in details . Events )
548564 {
565+ if ( ( short ) @event . Id != eventRecord . Id || @event . Version != eventRecord . Version ) { continue ; }
566+
549567 if ( ! DoesTemplateMatchPropertyCount ( @event . Template , eventPropertyCount ) ) { continue ; }
550568
551569 Logger ? . Trace ( $ "{ nameof ( GetModernEvent ) } : Match by short Id/Version fallback - EventId={ eventRecord . Id } , Version={ eventRecord . Version } ",
@@ -554,50 +572,47 @@ private List<string> GetKeywordsFromBitmask(EventRecord eventRecord)
554572 return @event ;
555573 }
556574
557- var candidateCount = details . Events . Count ( e => e . Id == eventRecord . Id ) ;
558-
559- Logger ? . Trace ( $ "{ nameof ( GetModernEvent ) } : No matching event found - EventId={ eventRecord . Id } , Version={ eventRecord . Version } , LogName={ eventRecord . LogName } , PropertyCount={ eventPropertyCount } , CandidateEventsWithSameId={ candidateCount } ",
575+ Logger ? . Trace ( $ "{ nameof ( GetModernEvent ) } : No matching event found - EventId={ eventRecord . Id } , Version={ eventRecord . Version } , LogName={ eventRecord . LogName } , PropertyCount={ eventPropertyCount } , CandidateEventsWithSameId={ candidateEvents . Count } ",
560576 LogLevel . Debug ) ;
561577
562578 return null ;
563579 }
564580
565581 /// <summary>Resolve event descriptions from an event record.</summary>
566- private string ResolveDescription ( EventRecord eventRecord )
582+ private string ResolveDescription ( EventRecord eventRecord , ProviderDetails ? details , EventModel ? modernEvent )
567583 {
568- if ( ! ProviderDetails . TryGetValue ( eventRecord . ProviderName , out var details ) || details is null )
584+ if ( details is null )
569585 {
570586 Logger ? . Trace ( $ "{ nameof ( ResolveDescription ) } : No provider details available - Provider={ eventRecord . ProviderName } , EventId={ eventRecord . Id } , RecordId={ eventRecord . RecordId } ",
571587 LogLevel . Debug ) ;
572588
573589 return DefaultNoProviderDescription ;
574590 }
575591
576- var @event = GetModernEvent ( eventRecord , details ) ;
577- var properties = GetFormattedProperties ( @event ? . Template , eventRecord . Properties ) ;
592+ var properties = GetFormattedProperties ( modernEvent ? . Template , eventRecord . Properties ) ;
578593
579- if ( ! string . IsNullOrEmpty ( @event ? . Description ) )
594+ if ( ! string . IsNullOrEmpty ( modernEvent ? . Description ) )
580595 {
581596 Logger ? . Trace ( $ "{ nameof ( ResolveDescription ) } : Using modern event description - Provider={ eventRecord . ProviderName } , EventId={ eventRecord . Id } , PropertyCount={ properties . Count } ",
582597 LogLevel . Debug ) ;
583598
584- return FormatDescription ( properties , @event ? . Description , details . Parameters ) ;
599+ return FormatDescription ( properties , modernEvent ? . Description , details . Parameters ) ;
585600 }
586601
587- // Legacy provider message lookup, if there is multiple messages then move on so we don't show wrong description
588- var legacyMessage = details . Messages . Where ( m => m . ShortId == eventRecord . Id ) . ToList ( ) ;
602+ // Legacy provider message lookup using indexed dictionary
603+ var legacyMessages = details . GetMessagesByShortId ( ( short ) eventRecord . Id ) ;
589604
590- if ( legacyMessage . Count == 1 )
605+ if ( legacyMessages . Count == 1 )
591606 {
592607 Logger ? . Trace ( $ "{ nameof ( ResolveDescription ) } : Using legacy message - Provider={ eventRecord . ProviderName } , EventId={ eventRecord . Id } , PropertyCount={ properties . Count } ",
593608 LogLevel . Debug ) ;
594609
595- return FormatDescription ( properties , legacyMessage . First ( ) . Text , details . Parameters ) ;
610+ return FormatDescription ( properties , legacyMessages [ 0 ] . Text , details . Parameters ) ;
596611 }
597612
598- if ( legacyMessage . Count > 1 )
613+ if ( legacyMessages . Count > 1 )
599614 {
600- Logger ? . Trace ( $ "{ nameof ( ResolveDescription ) } : Multiple legacy messages found, skipping - Provider={ eventRecord . ProviderName } , EventId={ eventRecord . Id } , MessageCount={ legacyMessage . Count } ",
615+ Logger ? . Trace ( $ "{ nameof ( ResolveDescription ) } : Multiple legacy messages found, skipping - Provider={ eventRecord . ProviderName } , EventId={ eventRecord . Id } , MessageCount={ legacyMessages . Count } ",
601616 LogLevel . Debug ) ;
602617 }
603618
@@ -617,16 +632,14 @@ private string ResolveDescription(EventRecord eventRecord)
617632 }
618633
619634 /// <summary>Resolve event task names from an event record.</summary>
620- private string ResolveTaskName ( EventRecord eventRecord )
635+ private string ResolveTaskName ( EventRecord eventRecord , ProviderDetails ? details , EventModel ? modernEvent )
621636 {
622- if ( ! ProviderDetails . TryGetValue ( eventRecord . ProviderName , out var details ) || details is null )
637+ if ( details is null )
623638 {
624639 return string . Empty ;
625640 }
626641
627- var @event = GetModernEvent ( eventRecord , details ) ;
628-
629- if ( @event ? . Task is not null && details . Tasks . TryGetValue ( @event . Task , out var taskName ) )
642+ if ( modernEvent ? . Task is not null && details . Tasks . TryGetValue ( modernEvent . Task , out var taskName ) )
630643 {
631644 taskName = taskName . TrimEnd ( '\0 ' ) ;
632645 return _cache ? . GetOrAddValue ( taskName ) ?? taskName ;
@@ -645,9 +658,18 @@ private string ResolveTaskName(EventRecord eventRecord)
645658 return _cache ? . GetOrAddValue ( taskName ) ?? taskName ;
646659 }
647660
648- var potentialTaskNames = details . Messages
649- . Where ( m => m . ShortId == eventRecord . Task && m . LogLink != null && m . LogLink == eventRecord . LogName )
650- . ToList ( ) ;
661+ // Use indexed lookup instead of linear scan with .ToList()
662+ var messagesByShortId = details . GetMessagesByShortId ( ( short ) eventRecord . Task ) ;
663+
664+ List < MessageModel > ? potentialTaskNames = null ;
665+
666+ foreach ( var m in messagesByShortId )
667+ {
668+ if ( m . LogLink is null || m . LogLink != eventRecord . LogName ) { continue ; }
669+
670+ potentialTaskNames ??= [ ] ;
671+ potentialTaskNames . Add ( m ) ;
672+ }
651673
652674 if ( potentialTaskNames is { Count : > 0 } )
653675 {
0 commit comments