Conseils et dépannage pour l'écriture d'analyseurs

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

Lorsque vous écrivez le code de l'analyseur, vous pouvez rencontrer des erreurs si les instructions d'analyse ne fonctionnent pas comme prévu. Les situations suivantes peuvent générer des erreurs:

  • Un schéma Grok échoue
  • Échec d'une opération rename ou replace
  • Erreurs de syntaxe dans le code de l'analyseur

Pratiques courantes d'utilisation du code d'analyse

Les sections suivantes décrivent les bonnes pratiques, les conseils et les solutions permettant de résoudre les problèmes.

Évitez d'utiliser des points ou des traits d'union 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 lorsque vous effectuez des opérations merge pour stocker des valeurs dans des champs UDM. Vous pouvez également rencontrer des problèmes d'analyse intermittente.

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, tels que 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 nommé event ou si vous définissez une variable intermédiaire appelée event et que le code d'analyse utilise le mot event dans l'instruction @output, vous recevez un message d'erreur concernant un conflit de noms.

Renommez la variable intermédiaire différemment 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 créé 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 à laquelle 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:

  • code temporel de la collection
  • code temporel de création
  • event
  • filename
  • message
  • espace de noms
  • sortie
  • 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 de tabulations dans le code de l'analyseur. N'utilisez que des espaces et mettez en retrait deux espaces à la fois.

Ne pas effectuer 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 des 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 vous disposez d'un scénario dans lequel la valeur testée peut correspondre plusieurs fois, utilisez plusieurs instructions if distinctes et classez-les du cas le plus générique au cas le plus spécifique.

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

Une bonne pratique consiste à tester le code de l'analyseur à l'aide d'échantillons de journaux bruts avec une grande variété de formats. Cela vous permet de rechercher les journaux uniques ou les cas particuliers que l'analyseur peut avoir besoin de gérer.

Ajouter des commentaires descriptifs au code de l'analyseur

Ajoutez des commentaires au code de l'analyseur qui expliquent pourquoi l'instruction est importante, plutôt que ce qu'elle fait. Le commentaire aide toute personne gérant 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 de manière précoce

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

Cela empêche le renvoi d'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'exemple d'instruction précédent détecte bien l'erreur d'analyse dans une variable intermédiaire booléenne, appelée _error_does_not_exist. Elle 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 (instructions 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 de l'analyseur mis à jour gère les différents scénarios à l'aide d'une instruction conditionnelle pour vérifier si le champ existe. En outre, l'instruction on_error gère les erreurs qui peuvent se produire.

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 d'analyse

Il n'est pas rare que les journaux entrants présentent un format inattendu ou que leurs données soient mal formatées.

Vous pouvez créer l'analyseur pour gérer ces erreurs. Une bonne pratique consiste à ajouter des gestionnaires on_error au filtre d'extraction, puis à 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 dans un format JSON valide et qu'elle n'a pas été correctement analysée. Si la variable _not_json est false, l'entrée de journal entrante était dans un 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 garantit que l'analyse n'échoue pas si les journaux sont ingérés sous un format incorrect pour le type de journal spécifié.

Utilisez le filtre drop avec la variable tag pour que la condition soit capturée dans le tableau 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 dans les journaux bruts de Google Security Operations.

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

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 à ce qui suit:

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

Le code d'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 la valeur définie sur metadata.event_type. Voici d'autres exemples susceptibles de provoquer 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 de l'UDM target.hostname 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 dans les champs UDM. Après avoir défini tous les champs UDM nécessaires, examinez le journal brut d'origine pour voir les 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 les définit sur une chaîne statique.
  • Le code de la section Affectation des champs 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. Après avoir identifié tous les champs UDM à renseigner, procédez comme suit:

  • Dans la section Configuration des entrées, ajoutez du 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, ajoutez du code qui extrait l'horodatage de l'événement du journal brut d'origine, le transforme et le définit sur la variable intermédiaire.

  • Si nécessaire, remplacez l'ensemble de valeurs initialisées 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 d'un texte non structuré, vous pouvez utiliser des modèles Grok et des instructions d'expression régulière prédéfinis. Les modèles Grok facilitent la lecture du code. Si l'expression régulière n'inclut pas de caractères raccourcis (tels que \w ou \s), vous pouvez copier et coller l'instruction directement dans le code de l'analyseur.

Étant donné que les modèles de 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, la mise en correspondance de l'exemple suivant nécessite moins de la moitié des étapes de traitement. Il s'agit d'un point important à prendre en compte si la source de journal peut générer un volume potentiellement élevé.

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

Les analyseurs Google Security Operations utilisent RE2 comme moteur d'expression régulière. Si vous connaissez bien 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 d'analyseur: (?P<_custom_field>\\w+)\\s

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

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

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

Voici le journal brut d'origine:

field=\entry\

Voici le journal converti au format encodé au format JSON:

field=\\entry\\

Lorsque vous utilisez une expression régulière dans le code de l'analyseur, vous devez ajouter des caractères d'échappement supplémentaires si vous ne souhaitez extraire que la valeur. Pour trouver une correspondance avec 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 de l'analyseur:

^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 des expressions régulières dans l'instruction d'extraction. Par exemple, remplacez \s par \\s.

Ne modifiez pas les caractères spéciaux des expressions régulières en cas d'échappement double dans l'instruction d'extraction. Par exemple, \\ reste inchangé (\\).

Voici une expression régulière standard:

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

L'expression régulière suivante a été 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 de l'inclure dans le code de l'analyseur.

Expression régulière Expression régulière modifiée pour le code de l'analyseur 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 classes de caractères doivent être échappés.
\\\\
\\\\
Les caractères spéciaux en dehors d'un groupe de classes de caractères ou les caractères abrégés 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. Cependant, dans le code de l'analyseur, elle é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, ceux-ci incluent par défaut un groupe de capture nommé. Lorsque vous utilisez des remplacements d'expression régulière, veillez à inclure un groupe nommé.

Voici un exemple d'expression régulière dans le code de l'analyseur:

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

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

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

Utilisez un groupe nommé "catchall" pour commencer à créer l'expression

Lorsque vous créez une instruction d'extraction, commencez par une expression qui en détecte plus 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'ensemble du message. Ensuite, il construit l'expression par étapes en faisant correspondre des parties supplémentaires du texte. À chaque étape, le groupe nommé _catchall contient moins de texte d'origine. Continuez et itérez étape par étape 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 de l'analyseur 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 à toute la chaîne de texte.

Échappement des caractères courts dans l'expression régulière

N'oubliez pas d'échapper les caractères courts des expressions régulières lorsque vous utilisez l'expression dans le code de l'analyseur. 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 de l'analyseur, il manque la lettre s dans le résultat.

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 du code de l'analyseur nécessitent l'ajout d'un caractère d'échappement supplémentaire aux caractères abrégés. Dans l'exemple précédent, \s doit être remplacé par \\s.

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

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

Exemple complet

Cette section décrit les règles précédentes sous la forme 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 analyser la chaîne. 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>[^\\\\]+)\\\"\\.$