Conseils et dépannage pour écrire des analyseurs

Compatible avec :

Ce document décrit les problèmes que vous pouvez rencontrer lorsque vous écrivez du code d'analyseur.

Lorsque vous écrivez du code d'analyseur, vous pouvez rencontrer des erreurs lorsque les instructions d'analyse ne fonctionnent pas comme prévu. Voici quelques situations qui peuvent générer des erreurs :

  • Échec d'un modèle Grok
  • Échec d'une opération rename ou replace
  • Erreurs de syntaxe dans le code du parseur

Pratiques courantes dans le code du parseur

Les sections suivantes décrivent les bonnes pratiques, les conseils et les solutions pour vous aider à résoudre les problèmes.

Éviter d'utiliser des points ou des tirets dans les noms de variables

L'utilisation de traits d'union et de points dans les noms de variables peut entraîner un comportement inattendu, souvent lors de l'exécution d'opérations merge pour stocker des valeurs dans des champs UDM. Vous pouvez également rencontrer des problèmes d'analyse intermittents.

Par exemple, n'utilisez pas les noms de variables suivants :

  • my.variable.result
  • my-variable-result

Utilisez plutôt le nom de variable suivant : my_variable_result.

N'utilisez pas de termes ayant une signification particulière comme nom de variable.

Certains mots, comme event et timestamp, peuvent avoir une signification particulière dans le code de l'analyseur.

La chaîne event est souvent utilisée pour représenter un seul enregistrement UDM et est utilisée dans l'instruction @output. Si un message de journal inclut un champ appelé event, ou si vous définissez une variable intermédiaire appelée event, et que le code du parser utilise le mot event dans l'instruction @output, un message d'erreur s'affiche concernant un conflit de noms.

Renommez la variable intermédiaire ou utilisez le terme event1 comme préfixe dans les noms de champs UDM et dans l'instruction @output.

Le mot timestamp représente le code temporel de création du journal brut d'origine. Une valeur définie dans cette variable intermédiaire est enregistrée dans le champ UDM metadata.event_timestamp. Le terme @timestamp représente la date et l'heure auxquelles le journal brut a été analysé pour créer un enregistrement UDM.

L'exemple suivant définit le champ UDM metadata.event_timestamp sur la date et l'heure auxquelles le journal brut a été analysé.

 # Save the log parse date and time to the timestamp variable
  mutate {
     rename => {
       "@timestamp" => "timestamp"
     }
   }

L'exemple suivant définit le champ UDM metadata.event_timestamp sur la date et l'heure extraites du journal brut d'origine et stockées dans la variable intermédiaire when.

   # Save the event timestamp to timestamp variable
   mutate {
     rename => {
       "when" => "timestamp"
     }
   }

N'utilisez pas les termes suivants comme variables :

  • collectiontimestamp
  • createtimestamp
  • événement
  • filename
  • message
  • espace de noms
  • output
  • onerrorcount
  • timestamp
  • timezone

Stocker chaque valeur de données dans un champ UDM distinct

Ne stockez pas plusieurs champs dans un seul champ UDM en les concaténant avec un délimiteur. En voici un exemple :

"principal.user.first_name" => "first:%{first_name},last:%{last_name}"

Stockez plutôt chaque valeur dans un champ UDM distinct.

"principal.user.first_name" => "%{first_name}"
"principal.user.last_name" => "%{last_name}"

Utiliser des espaces plutôt que des tabulations dans le code

N'utilisez pas d'onglets dans le code du parseur. N'utilisez que des espaces et mettez en retrait de deux espaces à la fois.

N'effectuez pas plusieurs actions de fusion en une seule opération.

Si vous fusionnez plusieurs champs en une seule opération, cela peut entraîner des résultats incohérents. Placez plutôt les instructions merge dans des opérations distinctes.

Par exemple, remplacez l'exemple suivant :

mutate {
  merge => {
      "security_result.category_details" => "category_details"
      "security_result.category_details" => "super_category_details"
  }
}

Par les lignes de code suivantes :

mutate {
  merge => {
    "security_result.category_details" => "category_details"
  }
}

mutate {
  merge => {
    "security_result.category_details" => "super_category_details"
  }
}

Choisir entre les expressions conditionnelles if et if else

Si la valeur conditionnelle que vous testez ne peut avoir qu'une seule correspondance, utilisez l'instruction conditionnelle if else. Cette approche est légèrement plus efficace. Toutefois, si la valeur testée peut correspondre plusieurs fois, utilisez plusieurs instructions if distinctes et ordonnez-les de la plus générique à la plus spécifique.

Choisissez un ensemble représentatif de fichiers journaux pour tester les modifications apportées à l'analyseur.

Il est recommandé de tester le code du parseur à l'aide d'exemples de journaux bruts avec une grande variété de formats. Cela vous permet de trouver des journaux uniques ou des cas extrêmes que l'analyseur doit peut-être gérer.

Ajouter des commentaires descriptifs au code du parseur

Ajoutez des commentaires au code du parseur pour expliquer pourquoi l'instruction est importante, plutôt que ce qu'elle fait. Le commentaire aide toute personne chargée de la maintenance de l'analyseur à suivre le flux. En voici un exemple :

# only assign a Namespace if the source address is RFC 1918 or Loopback IP address
if [jsonPayload][id][orig_h] =~ /^(127(?:\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\{3\}$)|(10(?:\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\{3\}$)|(192\.168(?:\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\{2\}$)|(172\.(?:1[6-9]|2\d|3[0-1])(?:\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\{2\}$)/ {
  mutate {
    replace => {
      "event1.idm.read_only_udm.principal.namespace" => "%{resource.labels.project_id}"
    }
  }
}

Initialiser les variables intermédiaires tôt

Avant d'extraire les valeurs du journal brut d'origine, initialisez les variables intermédiaires qui seront utilisées pour stocker les valeurs de test.

Cela évite de renvoyer une erreur indiquant que la variable intermédiaire n'existe pas.

L'instruction suivante attribue la valeur de la variable product au champ UDM metadata.product_name.

mutate{
  replace => {
    "event1.idm.read_only_udm.metadata.product_name" => "%{product}"
  }
}

Si la variable product n'existe pas, vous obtenez l'erreur suivante :

"generic::invalid_argument: pipeline failed: filter mutate (4) failed: replace failure: field \"event1.idm.read_only_udm.metadata.product_name\": source field \"product\": field not set"

Vous pouvez ajouter une instruction on_error pour détecter l'erreur. En voici un exemple :

mutate{
  replace => {
    "event1.idm.read_only_udm.metadata.product_name" => "%{product}"
    }
  on_error => "_error_does_not_exist"
  }

L'instruction de l'exemple précédent capture correctement l'erreur d'analyse dans une variable intermédiaire booléenne, appelée _error_does_not_exist. Il ne vous permet pas d'utiliser la variable product dans une instruction conditionnelle, par exemple if. En voici un exemple :

if [product] != "" {
  mutate{
    replace => {
      "event1.idm.read_only_udm.metadata.product_name" => "%{product}"
    }
  }
  on_error => "_error_does_not_exist"
}

L'exemple précédent renvoie l'erreur suivante, car la clause conditionnelle if n'est pas compatible avec les instructions on_error :

"generic::invalid_argument: pipeline failed: filter conditional (4) failed: failed to evaluate expression: generic::invalid_argument: "product" not found in state data"

Pour résoudre ce problème, ajoutez un bloc d'instructions distinct qui initialise les variables intermédiaires avant d'exécuter les instructions de filtre d'extraction (json, csv, xml, kv ou grok). Consultez l'exemple suivant.

filter {
  # Initialize intermediate variables for any field you will use for a conditional check
  mutate {
    replace => {
      "timestamp" => ""
      "does_not_exist" => ""
    }
  }

  # load the logs fields from the message field
  json {
    source         => "message"
    array_function => "split_columns"
    on_error       => "_not_json"
  }
}

L'extrait de code du parseur mis à jour gère les différents scénarios à l'aide d'une instruction conditionnelle permettant de vérifier si le champ existe. De plus, l'instruction on_error gère les erreurs qui peuvent être rencontrées.

Convertir SHA-256 en base64

L'exemple suivant extrait la valeur SHA-256, l'encode en base64, convertit les données encodées en chaîne hexadécimale, puis remplace des champs spécifiques par les valeurs extraites et traitées.

if [Sha256] != "" 
{
  base64
  {
  encoding => "RawStandard"
  source => "Sha256"
  target => "base64_sha256"
  on_error => "base64_message_error"
  }
  mutate
  {
    convert =>
    {
      "base64_sha256" => "bytestohex"
    }
    on_error => "already_a_string"
  }
  mutate
  {
    replace => 
  {
     "event.idm.read_only_udm.network.tls.client.certificate.sha256" => "%{base64_sha256}"
     "event.idm.read_only_udm.target.resource.name" => "%{Sha256}"
  }
  }
}

Gérer les erreurs dans les instructions du parseur

Il n'est pas rare que les journaux entrants soient dans un format inattendu ou que leurs données soient mal mises en forme.

Vous pouvez créer l'analyseur pour gérer ces erreurs. Nous vous recommandons d'ajouter des gestionnaires on_error au filtre d'extraction, puis de tester la variable intermédiaire avant de passer au segment suivant de la logique de l'analyseur.

L'exemple suivant utilise le filtre d'extraction json avec une instruction on_error pour définir la variable booléenne _not_json. Si _not_json est défini sur true, cela signifie que l'entrée de journal entrante n'était pas au format JSON valide et qu'elle n'a pas été analysée correctement. Si la variable _not_json est false, l'entrée de journal entrante était au format JSON valide.

 # load the incoming log from the default message field
  json {
    source         => "message"
    array_function => "split_columns"
    on_error       => "_not_json"
  }

Vous pouvez également vérifier si un champ est au bon format. L'exemple suivant vérifie si _not_json est défini sur true, ce qui indique que le journal n'était pas au format attendu.

 # Test that the received log matches the expected format
  if [_not_json] {
    drop { tag => "TAG_MALFORMED_MESSAGE" }
  } else {
    # timestamp is always expected
    if [timestamp] != "" {

      # ...additional parser logic goes here …

    } else {

      # if the timestamp field does not exist, it's not a log source
      drop { tag => "TAG_UNSUPPORTED" }
    }
  }

Cela permet de s'assurer que l'analyse n'échoue pas si les journaux sont ingérés avec un format incorrect pour le type de journal spécifié.

Utilisez le filtre drop avec la variable tag pour que la condition soit enregistrée dans la table des métriques d'ingestion dans BigQuery.

  • TAG_UNSUPPORTED
  • TAG_MALFORMED_ENCODING
  • TAG_MALFORMED_MESSAGE
  • TAG_NO_SECURITY_VALUE

Le filtre drop empêche l'analyseur de traiter le journal brut, de normaliser les champs et de créer un enregistrement UDM. Le journal brut d'origine est toujours ingéré dans Google Security Operations et peut être recherché à l'aide de la recherche de journaux bruts dans Google SecOps.

La valeur transmise à la variable tag est stockée dans le champ drop_reason_code du tableau "Métriques d'ingestion". Vous pouvez exécuter une requête ad hoc sur la table, comme celle-ci :

SELECT
  log_type,
  drop_reason_code,
  COUNT(drop_reason_code) AS count
FROM `datalake.ingestion_metrics`
GROUP BY 1,2
ORDER BY 1 ASC

Résoudre les erreurs de validation

Lorsque vous créez un analyseur, vous pouvez rencontrer des erreurs liées à la validation. Par exemple, un champ obligatoire n'est pas défini dans l'enregistrement UDM. L'erreur peut ressembler à l'exemple suivant :

Error: generic::unknown: invalid event 0: LOG_PARSING_GENERATED_INVALID_EVENT: "generic::invalid_argument: udm validation failed: target field is not set"

Le code de l'analyseur s'exécute correctement, mais l'enregistrement UDM généré n'inclut pas tous les champs UDM requis, tels que définis par l'ensemble de valeurs défini sur metadata.event_type. Voici d'autres exemples pouvant entraîner cette erreur :

  • Si metadata.event_type est défini sur USER_LOGIN et que le champ UDM target.user value n'est pas défini.
  • Si metadata.event_type est défini sur NETWORK_CONNECTION et que le champ target.hostnameUDM n'est pas défini.

Pour en savoir plus sur le champ UDM metadata.event_type et les champs obligatoires, consultez le guide d'utilisation de l'UDM.

Pour résoudre ce type d'erreur, vous pouvez commencer par définir des valeurs statiques pour les champs UDM. Une fois que vous avez défini tous les champs UDM nécessaires, examinez le journal brut d'origine pour voir quelles valeurs analyser et enregistrer dans l'enregistrement UDM. Si le journal brut d'origine ne contient pas certains champs, vous devrez peut-être définir des valeurs par défaut.

Voici un exemple de modèle, spécifique à un type d'événement USER_LOGIN, qui illustre cette approche.

Notez les points suivants :

  • Le modèle initialise les variables intermédiaires et définit chacune sur une chaîne statique.
  • Le code de la section Field Assignment (Attribution de champ) définit les valeurs des variables intermédiaires sur les champs UDM.

Vous pouvez développer ce code en ajoutant des variables intermédiaires et des champs UDM supplémentaires. Une fois que vous avez identifié tous les champs UDM à renseigner, procédez comme suit :

  • Dans la section Configuration des entrées, ajoutez le code qui extrait les champs du journal brut d'origine et définit les valeurs sur les variables intermédiaires.

  • Dans la section Date Extract (Extraction de la date), ajoutez le code qui extrait le code temporel de l'événement du journal brut d'origine, le transforme et le définit sur la variable intermédiaire.

  • Si nécessaire, remplacez la valeur initialisée définie dans chaque variable intermédiaire par une chaîne vide.

filter {
 mutate {
   replace => {
     # UDM > Metadata
     "metadata_event_timestamp"    => ""
     "metadata_vendor_name"        => "Example"
     "metadata_product_name"       => "Example SSO"
     "metadata_product_version"    => "1.0"
     "metadata_product_event_type" => "login"
     "metadata_product_log_id"     => "12345678"
     "metadata_description"        => "A user logged in."
     "metadata_event_type"         => "USER_LOGIN"

     # UDM > Principal
     "principal_ip"       => "192.168.2.10"

     # UDM > Target
     "target_application"            => "Example Connect"
     "target_user_user_display_name" => "Mary Smith"
     "target_user_userid"            => "mary@example.com"

     # UDM > Extensions
     "auth_type"          => "SSO"
     "auth_mechanism"     => "USERNAME_PASSWORD"

     # UDM > Security Results
     "securityResult_action"         => "ALLOW"
     "security_result.severity"       => "LOW"

   }
 }

 # ------------ Input Configuration  --------------
  # Extract values from the message using one of the extraction filters: json, kv, grok

 # ------------ Date Extract  --------------
 # If the  date {} function is not used, the default is the normalization process time

  # ------------ Field Assignment  --------------
  # UDM Metadata
  mutate {
    replace => {
      "event1.idm.read_only_udm.metadata.vendor_name"        =>  "%{metadata_vendor_name}"
      "event1.idm.read_only_udm.metadata.product_name"       =>  "%{metadata_product_name}"
      "event1.idm.read_only_udm.metadata.product_version"    =>  "%{metadata_product_version}"
      "event1.idm.read_only_udm.metadata.product_event_type" =>  "%{metadata_product_event_type}"
      "event1.idm.read_only_udm.metadata.product_log_id"     =>  "%{metadata_product_log_id}"
      "event1.idm.read_only_udm.metadata.description"        =>  "%{metadata_description}"
      "event1.idm.read_only_udm.metadata.event_type"         =>  "%{metadata_event_type}"
    }
  }

  # Set the UDM > auth fields
  mutate {
    replace => {
      "event1.idm.read_only_udm.extensions.auth.type"        => "%{auth_type}"
    }
    merge => {
      "event1.idm.read_only_udm.extensions.auth.mechanism"   => "auth_mechanism"
    }
  }

  # Set the UDM > principal fields
  mutate {
    merge => {
      "event1.idm.read_only_udm.principal.ip"                => "principal_ip"
    }
  }

  # Set the UDM > target fields
  mutate {
    replace => {
      "event1.idm.read_only_udm.target.user.userid"             =>  "%{target_user_userid}"
      "event1.idm.read_only_udm.target.user.user_display_name"  =>  "%{target_user_user_display_name}"
      "event1.idm.read_only_udm.target.application"             =>  "%{target_application}"
    }
  }

  # Set the UDM > security_results fields
  mutate {
    merge => {
      "security_result.action" => "securityResult_action"
    }
  }

  # Set the security result
  mutate {
    merge => {
      "event1.idm.read_only_udm.security_result" => "security_result"
    }
  }

 # ------------ Output the event  --------------
  mutate {
    merge => {
      "@output" => "event1"
    }
  }

}

Analyser du texte non structuré à l'aide d'une fonction Grok

Lorsque vous utilisez une fonction Grok pour extraire des valeurs à partir de texte non structuré, vous pouvez utiliser des modèles Grok prédéfinis et des instructions d'expression régulière. Les modèles Grok facilitent la lecture du code. Si l'expression régulière n'inclut pas de caractères abrégés (tels que \w, \s), vous pouvez copier et coller l'instruction directement dans le code du parser.

Étant donné que les modèles Grok constituent une couche d'abstraction supplémentaire dans l'instruction, ils peuvent rendre le dépannage plus complexe en cas d'erreur. Voici un exemple de fonction Grok contenant à la fois des modèles Grok prédéfinis et des expressions régulières.

grok {
  match => {
    "message" => [
      "%{NUMBER:when}\\s+\\d+\\s%{SYSLOGHOST:srcip} %{WORD:action}\\/%{NUMBER:returnCode} %{NUMBER:size} %{WORD:method} (?P<url>\\S+) (?P<username>.*?) %{WORD}\\/(?P<tgtip>\\S+).*"
    ]
  }
}

Une instruction d'extraction sans modèles Grok peut être plus performante. Par exemple, l'exemple suivant nécessite moins de la moitié des étapes de traitement pour correspondre. Il s'agit d'un point important à prendre en compte pour une source de journaux potentiellement à volume élevé.

Comprendre les différences entre les expressions régulières RE2 et PCRE

Les analyseurs Google SecOps utilisent RE2 comme moteur d'expressions régulières. Si vous connaissez la syntaxe PCRE, vous remarquerez peut-être des différences. Voici un exemple :

Voici une instruction PCRE : (?<_custom_field>\w+)\s

Voici une instruction RE2 pour le code du parser : (?P<_custom_field>\\w+)\\s

Veillez à échapper les caractères d'échappement

Google SecOps stocke les données de journaux brutes entrantes au format JSON. Cela permet de s'assurer que les chaînes de caractères qui semblent être des raccourcis d'expressions régulières sont interprétées comme des chaînes littérales. Par exemple, \t est interprété comme une chaîne littérale, et non comme un caractère de tabulation.

L'exemple suivant présente un journal brut d'origine et un journal au format JSON. Notez le caractère d'échappement ajouté devant chaque barre oblique inverse entourant le terme entry.

Voici le journal brut d'origine :

field=\entry\

Voici le journal converti au format JSON :

field=\\entry\\

Lorsque vous utilisez une expression régulière dans le code du parser, vous devez ajouter des caractères d'échappement supplémentaires si vous souhaitez extraire uniquement la valeur. Pour faire correspondre une barre oblique inverse dans le journal brut d'origine, utilisez quatre barres obliques inverses dans l'instruction d'extraction.

Voici une expression régulière pour le code du parseur :

^field=\\\\(?P<_value>.*)\\\\$

Voici le résultat généré. Le groupe nommé _value stocke le terme entry :

"_value": "entry"

Lorsque vous déplacez une instruction d'expression régulière standard dans le code de l'analyseur, échappez les caractères abrégés d'expression régulière dans l'instruction d'extraction. Par exemple, remplacez \s par \\s.

Laissez les caractères spéciaux des expressions régulières inchangés lorsqu'ils sont doublement échappés dans l'instruction d'extraction. Par exemple, \\ reste inchangé et devient \\.

Voici une expression régulière standard :

^.*?\\\"(?P<_user>[^\\]+)\\\"\s(?:(logged\son|logged\soff))\s.*?\\\"(?P<_device>[^\\]+)\\\"\.$

L'expression régulière suivante est modifiée pour fonctionner dans le code de l'analyseur.

^.*?\\\"(?P<_user>[^\\\\]+)\\\"\\s(?:(logged\\son|logged\\soff))\\s.*?\\\"(?P<_device>[^\\\\]+)\\\"\\.$

Le tableau suivant récapitule les cas où une expression régulière standard doit inclure des caractères d'échappement supplémentaires avant d'être incluse dans le code de l'analyseur.

Expression régulière Expression régulière modifiée pour le code du parseur Description de la modification
\s
\\s
Les caractères abrégés doivent être échappés.
\.
\\.
Les caractères réservés doivent être échappés.
\\"
\\\"
Les caractères réservés doivent être échappés.
\]
\\]
Les caractères réservés doivent être échappés.
\|
\\|
Les caractères réservés doivent être échappés.
[^\\]+
[^\\\\]+
Les caractères spéciaux d'un groupe de classe de caractères doivent être échappés.
\\\\
\\\\
Les caractères spéciaux en dehors d'un groupe de classe de caractères ou d'un caractère abrégé ne nécessitent pas d'échappement supplémentaire.

Les expressions régulières doivent inclure un groupe de capture nommé.

Une expression régulière, telle que "^.*$", est une syntaxe RE2 valide. Toutefois, dans le code du parseur, il échoue avec l'erreur suivante :

"ParseLogEntry failed: pipeline failed: filter grok (0) failed: failed to parse data with all match
patterns"

Vous devez ajouter un groupe de capture valide à l'expression. Si vous utilisez des modèles Grok, ils incluent un groupe de capture nommé par défaut. Lorsque vous utilisez des remplacements d'expressions régulières, veillez à inclure un groupe nommé.

Voici un exemple d'expression régulière dans le code du parseur :

"^(?P<_catchall>.*$)"

Voici le résultat, qui affiche le texte attribué au groupe nommé _catchall.

"_catchall": "User \"BOB\" logged on to workstation \"DESKTOP-01\"."

Utilisez un groupe nommé générique pour commencer à créer l'expression.

Lorsque vous créez une instruction d'extraction, commencez par une expression qui capture plus d'éléments que vous ne le souhaitez. Développez ensuite l'expression un champ à la fois.

L'exemple suivant commence par utiliser un groupe nommé (_catchall) qui correspond à l'intégralité du message. Il crée ensuite l'expression par étapes en faisant correspondre des portions supplémentaires du texte. À chaque étape, le groupe nommé _catchall contient moins de texte d'origine. Continuez et itérez une étape à la fois pour faire correspondre le message jusqu'à ce que vous n'ayez plus besoin du groupe nommé _catchall.

Étape Expression régulière dans le code du parseur Sortie du groupe de capture nommé _catchall
1
"^(?P<_catchall>.*$)"
User \"BOB\" logged on to workstation \"DESKTOP-01\".
2
^User\s\\\"(?P<_catchall>.*$)
BOB\" logged on to workstation \"DESKTOP-01\".
3
^User\s\\\"(?P<_user>.*?)\\\"\s(?P<_catchall>.*$)
logged on to workstation \"DESKTOP-01\".
Continuez jusqu'à ce que l'expression corresponde à l'intégralité de la chaîne de texte.

Échapper les caractères abrégés dans l'expression régulière

N'oubliez pas d'échapper les caractères abrégés des expressions régulières lorsque vous utilisez l'expression dans le code du parseur. Voici un exemple de chaîne de texte et l'expression régulière standard qui extrait le premier mot, This.

  This is a sample log.

L'expression régulière standard suivante extrait le premier mot, This. Toutefois, lorsque vous exécutez cette expression dans le code du parser, le résultat ne contient pas la lettre s.

Expression régulière standard Sortie du groupe de capture nommé _firstWord
"^(?P<_firstWord>[^\s]+)\s.*$" "_firstWord": "Thi",

En effet, les expressions régulières dans le code du parseur nécessitent un caractère d'échappement supplémentaire ajouté aux caractères abrégés. Dans l'exemple précédent, \s doit être remplacé par \\s.

Expression régulière révisée pour le code du parseur Sortie du groupe de capture nommé _firstWord
"^(?P<_firstWord>[^\\s]+)\\s.*$" "_firstWord": "This",

Cela ne s'applique qu'aux caractères abrégés, tels que \s, \r et \t. D'autres caractères, comme ``, n'ont pas besoin d'être échappés davantage.

Exemple complet

Cette section décrit les règles précédentes à l'aide d'un exemple de bout en bout. Voici une chaîne de texte non structurée et l'expression régulière standard écrite pour l'analyser. Enfin, il inclut l'expression régulière modifiée qui fonctionne dans le code de l'analyseur.

Voici la chaîne de texte d'origine.

User "BOB" logged on to workstation "DESKTOP-01".

Voici une expression régulière RE2 standard qui analyse la chaîne de texte.

^.*?\\\"(?P<_user>[^\\]+)\\\"\s(?:(logged\son|logged\soff))\s.*?\\\"(?P<_device>[^\\]+)\\\"\.$

Cette expression extrait les champs suivants.

Groupe de correspondance Position du caractère Chaîne de texte
Correspondance complète 0-53
User \"BOB\" logged on to workstation \"DESKTOP-01\".
Groupe `_user` 7-10
BOB
Groupe 2. 13-22
logged on
Groupe `_device` 40-50
DESKTOP-01

Il s'agit de l'expression modifiée. L'expression régulière RE2 standard a été modifiée pour fonctionner dans le code de l'analyseur.

^.*?\\\"(?P<_user>[^\\\\]+)\\\"\\s(?:(logged\\son|logged\\soff))\\s.*?\\\"(?P<_device>[^\\\\]+)\\\"\\.$

Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.