- Usa FilterHashtable para filtrar en origen por Level, Id, ProviderName y rangos de tiempo.
- Admite XPath/XML para filtros avanzados y exclusiones (Suppress/SuppressHashFilter).
- Combina múltiples logs y archivos .evtx/.etl; -Oldest y -MaxEvents optimizan el flujo.
Filtrar eventos de Windows por nivel (crítico, error o advertencia) con PowerShell es una de esas tareas que te ahorran horas cuando investigas incidencias. El cmdlet Get-WinEvent, disponible únicamente en Windows, permite hacerlo de forma muy eficiente, especialmente si se usa el parámetro FilterHashtable en lugar de encadenar Where-Object.
Además de ser más rápido y escalable, Get-WinEvent ofrece tres modelos de consultas: una tabla hash (FilterHashtable), XPath y XML estructurado. Con ellos puedes consultar registros locales o remotos, combinar varias fuentes, leer archivos .evtx y .etl, y hasta excluir niveles o patrones concretos, todo sin arrastrar miles de objetos por la canalización.
Get-WinEvent vs Get-EventLog: cuándo usar cada uno
Aunque ambos cmdlets devuelven eventos, Get-EventLog es el cmdlet clásico orientado a los registros tradicionales (Application, System, Security) del Administrador de eventos de Windows y su uso es intuitivo para exploración rápida. Sin embargo, Get-WinEvent es el reemplazo moderno: accede a registros basados en la tecnología de Windows Eventing, entiende XML, XPath, ETW (.etl) y ofrece un rendimiento superior con filtros a nivel de origen.
La recomendación práctica es usar Get-EventLog para exploraciones simples de los logs clásicos y apoyarte en Get-WinEvent cuando necesites filtrar con precisión, acceder a registros modernos, leer archivos guardados, combinar fuentes o exprimir el rendimiento con FilterHashtable.
Disponibilidad, entradas y salidas
Get-WinEvent solo está disponible en Windows y no se admite en Windows PE. Por defecto devuelve objetos EventLogRecord; con -ListLog devuelve EventLogConfiguration y con -ListProvider, ProviderMetadata. Puedes canalizar un LogName (string), una consulta FilterHashtable (Hashtable) o un XmlDocument para FilterXml.
Si no ejecutas PowerShell como administrador, puedes ver errores de acceso a ciertos registros. Tenlo en cuenta al consultar Security, depuración o analíticos; -Force ayuda a exponer depuración/análisis cuando usas comodines en nombres de log.
Sintaxis práctica por conjuntos de parámetros
Get-WinEvent admite varios conjuntos: GetLogSet (por -LogName y filtros como -FilterXPath), ListLogSet (-ListLog), ListProviderSet (-ListProvider), GetProviderSet (-ProviderName), FileSet (-Path a .evtx/.etl/.evt), HashQuerySet (-FilterHashtable) y XmlQuerySet (-FilterXml). El orden por defecto es de más reciente a más antiguo, salvo cuando usas -Oldest o lees .etl/.evt, que requieren orden inverso.
Ejemplos de sintaxis habituales: listar logs o proveedores, consultar por nombre de log, leer archivos guardados, o limitar por número de eventos.
# Listar logs y proveedores
Get-WinEvent -ListLog *
Get-WinEvent -ListProvider *
# Consultar un log concreto (más recientes primero)
Get-WinEvent -LogName 'Application' -MaxEvents 100
# Leer un archivo de registro guardado (.evtx)
Get-WinEvent -Path 'C:\\Test\\Windows PowerShell.evtx' -MaxEvents 100
# Leer un ETW (.etl) en orden cronológico y quedarte con los últimos 100
Get-WinEvent -Path 'C:\\Tracing\\TraceLog.etl' -Oldest | Sort-Object TimeCreated -Descending | Select-Object -First 100
Por qué FilterHashtable es más rápido que Where-Object
Cuando trabajas con registros voluminosos, es ineficiente traer miles de eventos y filtrarlos con Where-Object. FilterHashtable aplica el filtro en origen y acelera la consulta. Evita patrones como:
# Evitar: traer todo y filtrar después
Get-WinEvent -LogName Application | Where-Object { $_.ProviderName -match 'defrag' }
En su lugar, filtra con FilterHashtable para usar comodines en origen:
# Mejor: filtrar en origen con ProviderName
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='*defrag' }
FilterHashtable de cabo a rabo
El parámetro acepta una o varias tablas hash con pares clave-valor. Reglas clave: no distingue mayúsculas/minúsculas, cada clave aparece una sola vez, los comodines solo se admiten en LogName y ProviderName, y Path acepta rutas a .etl, .evt y .evtx. Puedes combinar LogName, Path y ProviderName en la misma consulta.
Las claves válidas incluyen LogName, ProviderName, Path, Keywords, Id, Level, StartTime, EndTime, UserID, Data y campos de datos con nombre (<named-data>). Además, existe la clave especial SuppressHashFilter para excluir criterios de la consulta (por ejemplo, filtrar fuera información).
# Esqueleto típico, añadiendo claves poco a poco
Get-WinEvent -FilterHashtable @{ LogName='Application' }
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime' }
# Excluir nivel Informational (4) en los últimos 2 días
$desde = (Get-Date).AddDays(-2)
$filter = @{ LogName='Application'; StartTime=$desde; SuppressHashFilter=@{ Level=4 } }
Get-WinEvent -FilterHashtable $filter
Para datos con nombre, puedes filtrar por elementos de EventData. Por ejemplo, un evento con <Data Name=»Service»>BITS</Data> se consulta así:
Get-WinEvent -FilterHashtable @{ LogName='Application'; 'Service'='BITS' }
Filtrar por nivel: críticos, errores y advertencias
La clave Level usa valores enumerados (no cadenas). Los niveles son: LogAlways=0, Crítico=1, Error=2, Advertencia=3, Informativo=4, Detallado/Verbose=5. Puedes ver los nombres con la clase .NET System.Diagnostics.Eventing.Reader.StandardEventLevel.
# Obtener niveles de evento como propiedades estáticas
| Get-Member -Static -MemberType Property
Para filtrar solo errores en Application:
Get-WinEvent -FilterHashtable @{ LogName='Application'; Level=2 }
Si prefieres nombres, usa la propiedad Value__ de la enumeración:
$lvl = ::Error
Get-WinEvent -FilterHashtable @{ LogName='Application'; Level=$lvl.Value__ }
Para advertencias (3) y críticos (1):
Get-WinEvent -FilterHashtable @{ LogName='System'; Level=3 }
Get-WinEvent -FilterHashtable @{ LogName='System'; Level=1 }
Filtrar por Id de evento y proveedor
La clave Id (o ID) admite matrices de enteros. Es muy útil cuando conoces el identificador exacto (por ejemplo, 1023 para ciertos eventos de .NET Runtime). Puedes combinar ProviderName para acotar todavía más:
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime'; Id=1023; Level=2 }
Filtrar por Keywords (palabras clave)
Keywords es un valor de tipo long (o matriz de long) y no acepta cadenas. Hay palabras clave estándar definidas en .NET: AuditFailure=4503599627370496, AuditSuccess=9007199254740992, CorrelationHint2=18014398509481984, EventLogClassic=36028797018963968, Sqm=2251799813685248, WdiDiagnostic=1125899906842624, WdiContext=562949953421312, ResponseTime=281474976710656, None=0.
Para consultar por EventLogClassic, usa el valor numérico o la propiedad estática con Value__:
# Con valor numérico directo
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime'; Keywords=36028797018963968 }
# Con enumeración
$kw = ::EventLogClassic
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime'; Keywords=$kw.Value__ }
Rangos de tiempo, usuario y datos
Para acotar por fechas, usa StartTime y EndTime. También puedes filtrar por UserID con un SID válido o un nombre de cuenta que resuelva a NTAccount. Data permite filtrar campos sin nombre (típicos de registros clásicos).
$start = (Get-Date).AddDays(-7)
Get-WinEvent -FilterHashtable @{ LogName='Application'; StartTime=$start; UserID='S-1-5-18' }
XPath y XML estructurado (consultas avanzadas)
Para escenarios complejos, puedes usar XPath o una consulta XML completa. Por ejemplo, eventos de nivel Advertencia (3) ocurridos en las últimas 24 horas en un log:
# XPath directo
$xp = '*]]'
Get-WinEvent -LogName 'Windows PowerShell' -FilterXPath $xp
# XML estructurado (útil para el editor de filtros del Visor de eventos)
$xml = @'
<QueryList>
<Query Path='Windows PowerShell'>
<Select Path='System'>*]]</Select>
</Query>
</QueryList>
'@
Get-WinEvent -FilterXml $xml
La ventaja del XML es que admite un elemento Suppress para excluir eventos; con FilterHashtable tienes un equivalente práctico: SuppressHashFilter para descartar niveles como Informational.
Combinar varios logs y agrupar resultados
Get-WinEvent permite consultar múltiples logs en una sola pasada y agrupar por propiedades para análisis. Por ejemplo, agrupar por LevelDisplayName y LogName:
Get-WinEvent -LogName '*PowerShell*', 'Microsoft-Windows-Kernel-WHEA*' |
Group-Object -Property LevelDisplayName, LogName -NoElement |
Format-Table -AutoSize
Para obtener estadísticas de un log concreto y entender qué IDs y niveles predominan:
$ev = Get-WinEvent -LogName 'Windows PowerShell'
$ev.Count
$ev | Group-Object Id -NoElement | Sort-Object Count -Descending
$ev | Group-Object LevelDisplayName -NoElement
Listar logs y proveedores; descubrir qué escribir
Antes de filtrar, conviene saber qué hay disponible. Puedes listar todos los registros y proveedores, así como ver los proveedores que escriben en un log concreto; apóyate en -ListLog y -ListProvider para localizar las carpetas de log:
# Todos los logs
Get-WinEvent -ListLog *
# Todos los proveedores
Get-WinEvent -ListProvider *
# Proveedores que escriben en 'Application'
(Get-WinEvent -ListLog 'Application').ProviderNames
# IDs emitidos por un proveedor
(Get-WinEvent -ListProvider 'Microsoft-Windows-GroupPolicy').Events | Format-Table Id, Description
Lectura de archivos .evtx y trazas ETW (.etl)
Además de los registros activos, puedes abrir archivos guardados, incluidas trazas ETW (.etl). Con .evtx, el orden por defecto es de más nuevo a más antiguo, y con .etl necesitas -Oldest para mantener el orden cronológico. Es posible combinar ambos tipos en un único comando si filtras por propiedades comunes como Id.
# .evtx más recientes
Get-WinEvent -Path 'C:\\Test\\PowerShellCore Operational.evtx' -MaxEvents 100
# Combinar .etl y .evtx y filtrar por Id=403
Get-WinEvent -Path 'C:\\Tracing\\TraceLog.etl', 'C:\\Test\\Windows PowerShell.evtx' -Oldest |
Where-Object { $_.Id -eq 403 }
Consultas remotas, credenciales y rendimiento
Con -ComputerName apuntas a otro equipo por NetBIOS, IP o FQDN (acepta un valor cada vez). Si necesitas autenticación específica, usa -Credential (PSCredential). Recuerda abrir los puertos del servicio de registro de eventos en el firewall para acceso remoto; no depende de PowerShell Remoting.
# Logs con datos en localhost
Get-WinEvent -ListLog * -ComputerName 'localhost' | Where-Object { $_.RecordCount }
Para datos masivos, limita con -MaxEvents y filtra en origen con FilterHashtable o XPath/XML. Evita Where-Object salvo cuando no haya alternativa.
Escenarios útiles de filtrado por nivel
Errores de aplicación (nivel 2) del último día en Application, con filtro en origen:
$hace24h = (Get-Date) - (New-TimeSpan -Day 1)
Get-WinEvent -FilterHashtable @{ LogName='Application'; Level=2; StartTime=$hace24h }
Advertencias (3) y errores (2) del proveedor ‘Application Error’ que mencionen ‘iexplore.exe’ en los últimos 7 días, usando la clave Data para campos sin nombre:
$desde = (Get-Date).AddDays(-7)
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='Application Error'; Data='iexplore.exe'; StartTime=$desde }
Errores .NET Runtime (Id 1023) marcados como EventLogClassic:
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime'; Keywords=36028797018963968; Id=1023; Level=2 }
Filtrar por Id de evento y proveedor
La clave Id (o ID) admite matrices de enteros. Es muy útil cuando conoces el identificador exacto (por ejemplo, 1023 para ciertos eventos de .NET Runtime). Puedes combinar ProviderName para acotar todavía más:
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime'; Id=1023; Level=2 }
Exploración rápida con Get-EventLog (y por qué migrar)
Get-EventLog sigue siendo práctico para consultas rápidas en registros clásicos y equipos remotos. Por ejemplo, últimos 5 errores del sistema que contengan la palabra ‘failed’:
Get-EventLog -LogName System -EntryType Error -Newest 5 -Message *failed* | Format-List *
Para intervalos temporales y conteo básico también resulta cómodo, pero recuerda que no llega a algunos registros modernos, y que Microsoft posiciona Get-WinEvent como sustituto en Windows Vista y posteriores, incluyendo Windows 11.
Estrategias de consulta incrementales
Una forma robusta de construir filtros es sumar claves paso a paso, comprobando el resultado en cada iteración. Empieza por LogName, añade ProviderName, después Keywords, Id y Level, y termina acotando por StartTime/EndTime.
# 1) Solo el log
Get-WinEvent -FilterHashtable @{ LogName='Application' }
# 2) Añade proveedor
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime' }
# 3) Añade palabra clave
$kw = ::EventLogClassic
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime'; Keywords=$kw.Value__ }
# 4) Añade Id y Level
Get-WinEvent -FilterHashtable @{ LogName='Application'; ProviderName='.NET Runtime'; Keywords=$kw.Value__; Id=1023; Level=2 }
Listar y comprender Keywords y Levels
Puedes enumerar nombres y valores de StandardEventKeywords y StandardEventLevel directamente con Get-Member:
| Get-Member -Static -MemberType Property
| Get-Member -Static -MemberType Property
Valores comunes de Keywords (nombre=valor): AuditFailure=4503599627370496, AuditSuccess=9007199254740992, CorrelationHint2=18014398509481984, EventLogClassic=36028797018963968, Sqm=2251799813685248, WdiDiagnostic=1125899906842624, WdiContext=562949953421312, ResponseTime=281474976710656, None=0.
Valores de Level: LogAlways=0, Crítico=1, Error=2, Advertencia=3, Informativo=4, Verbose=5. Usar números evita errores de conversión, pero la técnica Value__ te deja mantener el código legible.
Forzar logs de depuración/análisis y orden de salida
Los registros de depuración y análisis se excluyen por defecto. -Force es necesario cuando usas comodines en -ListLog para incluirlos. Recuerda que .evt y .etl requieren -Oldest porque almacenan eventos en orden cronológico y solo se pueden leer así.
Integración con Datadog: filtros y seguridad
Si reenvías eventos a Datadog, tienes dos modos: API de logs de eventos (recomendado) y modo legacy (WMI, obsoleto desde el Agent 7.20). Configura instancias en ‘win32_event_log.d/conf.yaml’. Con legacy_mode: false, usa ‘path’ para el canal; admite filtros ‘path’ (Application, System, Setup, Security), ‘type’ (Critical, Error, Warning, Information, Success Audit, Failure Audit), ‘source’ e ‘id’.
También puedes usar query con XPath/XML (se ignoran los filtros cuando hay query). Es recomendable construir la consulta en el Visor de eventos y pegar el XML. Para el log Security, añade la cuenta del Agent al grupo Event Log Readers y activa ‘dd_security_events’ en low o high a partir del Agent v7.54 para enviar eventos de seguridad predeterminados listos para Cloud SIEM.
La validación se hace desde el Agent Manager o con el subcomando ‘status’ verificando la sección ‘win32_event_log’. Si usas reglas de procesamiento de logs, comprueba que tus regex casan con el formato real (puedes inspeccionarlo con ‘stream-logs’) y, ante dudas, desactiva temporalmente ‘log_processing_rules’ para aislar el problema.
Ejemplos adicionales y utilidades
Filtrar las últimas 24 horas con diferentes métodos, destacando que los filtros en origen son más eficientes que Where-Object:
# Where-Object (menos eficiente)
$ayer = (Get-Date) - (New-TimeSpan -Day 1)
Get-WinEvent -LogName 'Windows PowerShell' | Where-Object { $_.TimeCreated -ge $ayer }
# FilterHashtable (recomendado)
Get-WinEvent -FilterHashtable @{ LogName='Windows PowerShell'; Level=3; StartTime=$ayer }
# XML estructurado
$xml = @'
<QueryList>
<Query Path='Windows PowerShell'>
<Select Path='System'>*]]</Select>
</Query>
</QueryList>
'@
Get-WinEvent -FilterXml $xml
# XPath directo
$xp = '*]]'
Get-WinEvent -LogName 'Windows PowerShell' -FilterXPath $xp
Un patrón práctico para detectar incidentes frecuentes es agrupar por Id y mensaje, mostrando los top 5 por recuento en System para niveles 2 o 3:
Get-WinEvent -LogName 'System' -FilterXPath "*]" |
ForEach-Object {
@{ EventID=$_.Id; Message=$_.Message; Count=1 }
} |
Group-Object EventID |
Sort-Object Count -Descending |
Select-Object @{Name='Count';Expression={$_.Count}}, @{Name='Event ID';Expression={$_.Group.EventID}}, @{Name='Message';Expression={$_.Group.Message}} -First 5
Si necesitas exportar solo Avisos y Errores de Application y System, filtra por Level y luego exporta al formato deseado:
# Aviso (3) y Error (2) de Application y System, últimos 2 días, a CSV
$desde = (Get-Date).AddDays(-2)
Get-WinEvent -FilterHashtable @{ LogName=@('Application','System'); Level=@(2,3); StartTime=$desde } |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName, LogName, Message |
Export-Csv 'C:\\Temp\\avisos_errores.csv' -NoTypeInformation -Encoding UTF8
Buenas prácticas y notas finales
Recuerda que los comodines solo aplican a LogName y ProviderName en FilterHashtable; cada clave debe figurar una sola vez; y si una clave no se reconoce, Get-WinEvent la interpreta como nombre de dato del evento con distinción de mayúsculas/minúsculas.
Get-WinEvent admite -MaxEvents para recortar la salida y -Oldest para invertir el orden cuando sea necesario. -Path admite patrones con comodines y varias rutas separadas por comas. -Credential usa PSCredential; si pasas un nombre, se solicitará la contraseña interactivamente.
Para descubrir qué proveedores y logs te interesan, apóyate en -ListLog y -ListProvider. Y cuando vayas a crear una consulta XML compleja, construye el filtro primero en el Visor de eventos (Crear vista personalizada/Filtrar registro actual) y copia la pestaña XML para pegarla directamente en FilterXml.
Dominar FilterHashtable, los niveles y palabras clave, y conocer las diferencias entre Get-EventLog y Get-WinEvent, te permite responder con precisión: aislar críticos, errores y avisos, buscar por proveedor o Id, excluir ruido informativo y leer registros guardados o de tracking ETW sin despeinarte.
Redactor apasionado del mundo de los bytes y la tecnología en general. Me encanta compartir mis conocimientos a través de la escritura, y eso es lo que haré en este blog, mostrarte todo lo más interesante sobre gadgets, software, hardware, tendencias tecnológicas, y más. Mi objetivo es ayudarte a navegar por el mundo digital de forma sencilla y entretenida.