El análisis de registros con expresiones regulares es una técnica valiosa para extraer información esencial de grandes volúmenes de datos de registro. Al emplear este método, se pueden identificar efectivamente patrones, errores y otros conocimientos clave, lo que en última instancia simplifica el análisis de registros y mejora el rendimiento del sistema.
BindPlane OP y un agente de BindPlane (recopilador de OpenTelemetry personalizado)
Un archivo de registro complejo que necesita un análisis personalizado
Conocimiento de expresiones regulares
Una selección de muestras de troncos que coinciden con todas las variaciones posibles.
(Opcional) Una buena herramienta de prueba de expresiones regulares como regex101.com
En esta publicación, examinaremos las entradas de registro que se asemejan a los ejemplos proporcionados a continuación. Al utilizar un script para escribir estas entradas en un archivo con las marcas de tiempo actuales, podemos trabajar efectivamente con datos en tiempo real.
May 01 14:31:11 loggen-app10 test-system[712]: Mon May 01 14:31:11 UTC 2023|TEST_PROC|[result=Service Access Granted,service=https://portal.fakeuni.edu/uPortal/Login?...,requiredAttributes={}]|SERVICE_ACCESS_ENFORCEMENT_TRIGGERED|camillec1997|172.13.49.165|172.16.156.55 May 01 14:31:11 loggen-app10 test-system[712]: Mon May 01 14:31:11 UTC 2023|TEST_PROC|[result=Service Access Granted,service=https://portal.fakeuni.edu/uPortal/Login?...,requiredAttributes={}]|SERVICE_ACCESS_ENFORCEMENT_TRIGGERED|camillec1997|172.13.49.165|172.16.156.55 May 01 14:31:11 loggen-app10 test-system[712]: Mon May 01 14:31:11 UTC 2023|TEST_PROC|[event=success,timestamp=May 01 14:31:11 UTC 2023,source=RankedMultifactorAuthenticationProviderWebflowEventResolver]|AUTHENTICATION_EVENT_TRIGGERED|audit:unknown|172.25.178.6|172.16.156.55 May 01 14:31:11 loggen-app08 test-system[780]: Mon May 01 14:31:11 UTC 2023|TEST_PROC|Supplied credentials: [UsernamePasswordCredential(username=dawsonb, source=null, customFields={})]|AUTHENTICATION_SUCCESS|dawsonb|172.12.154.117|172.16.156.53 May 01 14:31:11 loggen-app10 test-system[712]: Mon May 01 14:31:11 UTC 2023|TEST_PROC|[event=success,timestamp=May 01 14:31:11 UTC 2023,source=RankedMultifactorAuthenticationProviderWebflowEventResolver]|AUTHENTICATION_EVENT_TRIGGERED|audit:unknown|172.25.178.6|172.16.156.55
Ahora podemos tomar la primera entrada de registro anterior y comenzar a diseccionarla en las secciones que deseamos analizar.
Primero, notaremos que tenemos dos marcas de tiempo:
**May 01 14:31:11** loggen-app10 test-system[712]: **Mon May 01 14:31:11 UTC 2023**|TEST_PROC|[result=Service Access Granted,service=https://portal.fakeuni.edu/uPortal/Login?...,requiredAttributes={}]|SERVICE_ACCESS_ENFORCEMENT_TRIGGERED|camillec1997|172.13.49.165|172.16.156.55
La segunda marca de tiempo es la que conservaremos para que se convierta en nuestra marca de tiempo oficial porque contiene más información (la zona horaria y el año son útiles, mientras que el día de la semana no lo es realmente) que podemos usar para lograr la mayor precisión.
Desglosando esto, escribiremos un patrón de no captura para que coincida con la primera marca de tiempo.
^\w{3}\s\d{2}\s\d{2}:\d{2}:\d{2}\s+The caret “^”
al inicio de la línea. A esto le sigue “ \w{3}
”, que captura la abreviatura de mes de 3 letras. Después del mes, está “ \s\d{2}\s
”, que es para capturar un espacio; los 2 dígitos, 0 días rellenos del mes; y otro espacio. Finalmente, tenemos “ \d{2}:\d{2}:\d{2}\s+
” – para hora de 2 dígitos, minuto de 2 dígitos, segundo de 2 dígitos y 1 o más espacios.
Para la segunda marca de tiempo, queremos un grupo de captura con nombre. Esto se convertirá en un campo con nombre en el blob JSON de campos analizados.
(?P<timestamp>\w{3}\s\w{3}\s\d{2}\s\d{2}:\d{2}:\d{2}\s\w{3}\s\d{4})
Hemos llamado a este grupo de captura "marca de tiempo". Contiene la misma expresión regular básica que la otra marca de tiempo, con la adición de " \w{3}\s
" al comienzo para capturar el día de la semana abreviado, y " \s\w{3}\s\d{4}
” reemplazando el “ \s+
” al final para capturar la zona horaria de 3 caracteres y el año de 4 dígitos.
Profundizando en el mensaje de registro, querremos analizar el nombre de host y el sistema:
May 01 14:31:11 **loggen-app10 test-system[712]**: Mon May 01 14:31:11 UTC 2023|TEST_PROC|[result=Service Access Granted,service=https://portal.fakeuni.edu/uPortal/Login?...,requiredAttributes={}]|SERVICE_ACCESS_ENFORCEMENT_TRIGGERED|camillec1997|172.13.49.165|172.16.156.55
En este mensaje, nuestro nombre de host es **loggen-app10**
y nuestro sistema es **test-system[712]**
. No me dijeron qué era el [712] cuando recibí estos registros. Asumí que es el PID (ID de proceso), pero opté por no analizarlo por separado por ahora. Analizar estos campos es bastante simple y terminamos con: “ (?P<hostname>[^\s]+)\s+(?P<system>.*?):\s+
”. Tenemos un par de grupos de captura con nombre, nombre de host y sistema.
El patrón para el nombre de host es " [^\s]+
", que dice capturar cualquier carácter que no sea espacio y capturar tantos como puedas (codicioso). A esto le sigue " \s+
", que captura al menos uno, pero tantos como sea posible (codiciosos de nuevo) espacio(s).
El grupo de captura para el sistema es aún más fácil porque después de los espacios capturamos todo hasta un carácter de dos puntos. Para hacer esto, usamos “ .*?
”. Lo que dice ese patrón es capturar cualquier personaje 0 o más veces, pero no seas codicioso.
Después de eso, tenemos el carácter de dos puntos y otro 1 o más espacios codiciosos. Estos no se capturan, pero se necesitan para completar entre esta sección y la sección de marca de tiempo que escribimos anteriormente.
Esto da como resultado el siguiente patrón inicial:
^\w{3}\s*\d{2}\s*\d{2}:\d{2}:\d{2}\s+(?P<hostname>[^\s]+)\s+(?P<system>.*?):\s+(?P<timestamp>\w{3}\s\w{3}\s\d{2}\s\d{2}:\d{2}:\d{2}\s\w{3}\s\d{4})
No pasaré por todo el proceso de creación del patrón, pero sigo fragmentándolo como lo hice anteriormente.
El patrón final resultante es:
^\w{3}\s*\d{2}\s*\d{2}:\d{2}:\d{2}\s+(?P<hostname>[^\s]+)\s+(?P<system>.*?):\s+(?P<timestamp>\w{3}\s\w{3}\s\d{2}\s\d{2}:\d{2}:\d{2}\s\w{3}\s\d{4})\|(?P<app_name>\w*)\|((?P<message_type>.*?):\s+)?\[?(?P<message>.*)\]?\|(?P<event_message>.*?)\|(?P<username>.*?)\|(?P<external_ip>[\d\.:]*)\|(?P<internal_ip>[\d\.:]*)
Este patrón final incluye los siguientes grupos de captura con nombre, que se convierten en campos en nuestro blob JSON de datos analizados:
En BindPlane, creo una fuente de archivo. Esto mira mi archivo de registro generado en /root/complex.log. He seleccionado expresiones regulares en Formato de análisis. Debajo del patrón Regex, puse el patrón final de arriba. Marqué la casilla de Parse Timestamp, elegí Manual para el formato e ingresé los códigos de análisis de tiempo para el patrón de mi marca de tiempo.
Una vez hecho esto, se ve así:
Para completar la prueba, necesito crear un destino y verificar los datos allí. Para mi caso de uso, elegí un destino de Google Cloud Logging.
Una vez que se completa la configuración de mi tubería, la adjunto a un agente. Después de que se haya ejecutado durante unos momentos, hago clic en el botón "Ver telemetría reciente" en la página del agente.
La vista de telemetría me muestra el siguiente registro analizado:
Finalmente, también lo verifico en la consola de Google Cloud Logging:
Esto muestra la misma entrada de registro y tiene una carga JSON del objeto de mapa JSON de nuestro cuerpo de la vista de telemetría reciente.
Para los próximos pasos, me gustaría analizar el valor de ese mensaje. Con frecuencia es un conjunto clave/valor; como está en las capturas de pantalla y ejemplos anteriores. Podría pasar los datos a un procesador que analiza estas entradas de clave/valor en otra capa de JSON. En el ejemplo anterior, body.message
se volvería a analizar en sí mismo y podría tener campos como:
body.message.result=Service Access Granted body.message.service=https://innosoftfusiongo.com/sso/logi… body.message.principal=SimplePrincipal(id=dawsonb, attributes={mail=[[email protected]], eduPersonAffiliation=[Staff], ou=[Recreation/Student Rec Center], givenName=[Dawson], cn=[Dawson Branden], title=[Asst. Director], employeeNumber=[5000000], o=[Vice ChancellorStudent Affairs], fakeuniOrg=[Vice ChancellorStudent Affairs], casMFARequired=[YES], uid=[dawsonb], eduPersonPrimaryAffiliation=[Staff], fakeuniCid=[5000000], fakeuniSeparationDate=[99991231000000Z], UDC_IDENTIFIER=[dawsonb], sn=[Branden], organizationalStatus=[Staff]}) body.message.requiredAttributes=””
Incluso esto podría analizarse aún más, poniendo body.message.principle a través de un analizador de clave/valor también.
Ahora, alguien seguramente se preguntará: "¿Por qué no usaste el análisis de expresiones regulares de los subcampos body.message también?" La respuesta: es demasiado inconsistente. La expresión regular sería increíblemente e irrazonablemente compleja cuando ya tenemos la capacidad de analizar pares clave/valor.
Se pueden encontrar muchas formas de datos en los archivos de registro. Estos datos a menudo deben analizarse para que sean más fáciles de leer para los humanos y más fáciles para que la automatización y las herramientas actúen más adelante en la cadena.
Si bien el ejemplo con el que trabajé se realizó en un registro de archivo simple, las técnicas de este documento se pueden usar en cualquier flujo de registro. Además del análisis de expresiones regulares, BindPlane también admite JSON ,XML , pares clave/valor y valores separados por caracteres. Con el uso de procesadores, estos analizadores se pueden encadenar para analizar datos incrustados y manipularlos en un formato utilizable.
También publicado aquí.