LogParser accessing the Windows event log

 package require tcl 8.6
 package require tcom
 
 namespace eval ::eventLog {
     variable types;
     variable columns;
     variable hexMap
 
     unset -nocomplain types columns hexMap;
 
     proc RecordsToList {records} {
         variable types;
         variable columns;
 
         set list [list];
 
         try {
             if {![info exists types]} {
                 # create the record-tcl type mapping
                 #
                 set types [list \
                     [$records REAL_TYPE] {
                         set type    double;
                         set command getValue;
                         set eval    "";
                     } \
                     [$records INTEGER_TYPE] {
                         set type    integer;
                         set command getValue;
                         set eval    "";
                     } \
                     [$records TIMESTAMP_TYPE] {
                         set type    timestamp;
                         set command toNativeString;
                         set eval    [list apply [list {value} {
                             return [clock scan $value -format {%Y-%m-%d %H:%M:%S}];
                         } [namespace current]]];
                     } \
                     [$records NULL_TYPE] {
                         set type    NULL;
                         set command getValue;
                         set eval    "";
                     } \
                     [$records STRING_TYPE] - \
                     default                {
                         set type    string;
                         set command getValue;
                         set eval    "";
                     } \
                 ];
             }
 
             if {   ![info exists columns]
                 || [$records getColumnCount] != [dict size $columns]} {
                 # create the columns data set
                 #
                 set columns [dict create];
 
                 for {set i 0; set end [$records getColumnCount]} {$i < $end} {incr i} {
                     set column [dict create name "" type "" command "" eval ""];
 
                     dict with column {
                         set name [$records getColumnName $i];
 
                         switch [$records getColumnType $i] $types;
 
                         switch $name {
                             EvntType {
                                 set name EventTypeName;
                                 set eval [list apply [list {value} {return [string tolower $value];} [namespace current]]];
                             }
                             EventCategoryName {
                                 set eval [list apply [list {value} {
                                     return [expr {[string first {The name for category } $value] == -1 ? $value : ""}];
                                 } [namespace current]]];
                             }
                             Message {
                                 set eval [list apply [list {value} {return [string trim $value];} [namespace current]]];
                             }
                             Data {
                                 set eval [list apply [list {value} {
                                     variable hexMap;
 
                                     if {![info exists hexMap]} {
                                         for {set i 0} {$i < 256} {incr i} {
                                             lappend hexMap [format {%02X} $i] [format {%c} $i];
                                         }
                                     }
 
                                     set value [string map $hexMap $value];
 
                                     if {[string first "\x0" $value] >= 0} {
                                         if {[string first "\x0\x0" $value] == -1} {
                                             set temp [string trim [encoding convertfrom unicode $value]];
 
                                             if {[string is print -strict $temp]} {
                                                 set value $temp;
                                             }
                                         }
                                     } else {
                                         set value [string trim $value];
                                     }
 
                                     return $value;
                                 } [namespace current]]];
                             }
                         }
                     };
 
                     dict set columns $i $column;
                 }
             }
 
             # convert the records set into a tcl list of dictionaries
             #
             set list [list];
 
             while {![$records atEnd]} {
                 set row     [dict create];
                 set record  [$records getRecord];
 
                 for {set i 0; set end [dict size $columns]} {$i < $end} {incr i} {
                     set column [dict get $columns $i];
 
                     dict with column {
                         set value "";
 
                         if {![$record isNull $i]} {
                             set value [$record $command $i];
 
                             if {$eval ne ""} {
                                 set value [{*}$eval $value];
                             }
                         }
 
                         dict set row $name $value;
                     }
                 }
 
                 lappend list $row;
 
                 unset record;
 
                 $records moveNext;
             }
         } on error {result options} {
             error $result [dict get $options -errorinfo] [dict get $options -errorcode];
         } finally {
             $records close;
         }
 
         return $list;
     }
 
     proc get {source types {undefined 0}} {
         try {
             set lgp [tcom::ref createobject MSUtil.LogQuery];
             set evt [tcom::ref createobject MSUtil.LogQuery.EventLogInputFormat];
 
             $evt resolveSIDs 1;
             $evt binaryFormat HEX;
 
             set records [$lgp Execute \
                 [format \
     "SELECT
         SID,
         TO_LOWERCASE(REPLACE_STR(EventTypeName,' event','')) As EvntType,
         TimeGenerated,
         SourceName,
         EventCategoryName,
         EventCategory,
         EventID,
         Message,
         Data
     FROM %s
     WHERE
         EvntType IN ('%s')
         %s
     ORDER BY
         EvntType, TimeGenerated DESC" \
                 $source \
                     [join [string tolower $types] {';'}] \
                     [expr {$undefined ? "" : "AND NOT Message LIKE 'The description for Event ID %'"}] \
                 ] \
                 $evt \
             ];
 
             set list [RecordsToList $records];
         } on error {result options} {
             error $result [dict get $options -errorinfo] [dict get $options -errorcode];
         } finally {
             unset -nocomplain lgp evt records;
         }
 
         return $list;
     }
 
     namespace export -clear {[a-z]*};
     namespace ensemble create;
 }
 
 set apps    [eventLog get Application {warning error}]; puts "";
 set system  [eventLog get System {warning error}]; puts "";