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

Compatible avec:

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. Voici les 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 de l'analyseur

Pratiques courantes d'utilisation du code d'analyse

Les sections suivantes décrivent les bonnes pratiques, les conseils et les solutions pour vous aider à 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 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 l'analyseur du code source.

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 de l'analyseur utilise le mot event dans l'instruction @output, un message d'erreur concernant un conflit de nom s'affiche.

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 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 à laquelle 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
  • event
  • 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 concatenant avec un séparateur. 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 de l'analyseur. N'utilisez que des espaces et mettez en retrait deux espaces à la fois.

Ne pas effectuer plusieurs actions de fusion dans une même opération

Si vous fusionnez plusieurs champs dans une même opération, vous risquez d'obtenir 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 ou if else

Si la valeur conditionnelle que vous testez ne peut avoir qu'une seule correspondance, Utilisez ensuite l'instruction conditionnelle if else. Cette approche est un peu plus efficace. Toutefois, si la valeur testée peut correspondre à davantage utilisez plusieurs instructions if distinctes et organisez-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 requête large dans différents formats. Cela vous permet de trouver des journaux uniques ou des cas particuliers que l'analyseur devra peut-être 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 responsable de la maintenance de l'analyseur de 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 les valeurs du journal brut d'origine, initialisez les requêtes intermédiaires qui serviront à stocker les valeurs de test.

Cela évite qu'une erreur soit renvoyée 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 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 du 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 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. De plus, l'instruction on_error gère les erreurs pouvant 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 ou contiennent des données mal formatées.

Vous pouvez créer l'analyseur pour gérer ces erreurs. Une bonne pratique consiste à ajouter on_error au filtre d'extraction, puis à Testez 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 que L'entrée de journal n'a pas pu être 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 le format d'un champ est correct. 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 dans 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 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 la création d'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 Security Operations.

La valeur transmise à la variable tag est stockée dans la drop_reason_code dans Tableau des métriques d'ingestion. Vous pouvez exécuter une requête ad hoc sur la table, semblable à la suivante :

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 se présenter comme 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'analyse s'exécute correctement, mais pas l'enregistrement UDM généré. incluez tous les champs UDM obligatoires tels que définis par la valeur metadata.event_type. La Voici d'autres exemples susceptibles de provoquer cette erreur:

  • Si metadata.event_type est USER_LOGIN et que le champ UDM target.user value n'est pas défini.
  • Si metadata.event_type est NETWORK_CONNECTION et que target.hostnameLe champ "UDM" 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 la gestion des identités et des droits d'accès.

Pour résoudre ce type d'erreur, vous pouvez commencer par définir des valeurs statiques sur UDM . Après avoir défini tous les champs UDM nécessaires, examinez le journal brut d'origine pour identifier les champs à analyser et à enregistrer dans l'enregistrement UDM. Si le journal brut d'origine ne contiennent 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 bien cette approche.

Notez les points suivants:

  • Le modèle initialise les variables intermédiaires et définit chacune d'elles sur une chaîne statique.
  • Le code sous 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 d'entrée, ajoutez du code qui extrait des champs du journal brut d'origine et définit les valeurs sur les variables intermédiaires.

  • Dans la section Date Extract, ajoutez le 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 prédéfinis et des instructions d'expression régulière. Motifs Grok facilitent la lecture du code. Si l'expression régulière n'inclut pas de raccourci caractères (\w ou \s, par exemple), vous pouvez copier et coller l'instruction directement dans le code de l'analyseur.

É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 lorsque vous rencontrez une erreur. Voici un exemple : 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 effectuer la mise en correspondance. Il s'agit d'un point important à prendre en compte pour une source de journaux potentiellement volumineuse.

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

Les analyseurs Google Security Operations 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 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 la chaîne littérale. Par exemple, \t est interprété comme plutôt qu'un caractère de tabulation.

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

Voici le journal brut d'origine :

field=\entry\

Voici le journal converti au format encodé 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 souhaitez n'extraire que 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 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 d'expression régulière 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 la partie d'extraction. Par exemple, \\ reste inchangé en tant que \\.

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 d'être incluse 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é.
\\\\
\\\\
Les caractères spéciaux en dehors d'un groupe de classe 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. Toutefois, dans le code de l'analyseur, 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. Si vous utilisez des d'expression, 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 intercepte 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'intégralité du message. Il construit ensuite 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 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 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.

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

N'oubliez pas d'échapper les caractères abrégés d'expression régulière lorsque vous utilisez l'expression dans le code de l'analyseur. Voici un exemple de chaîne de texte et le 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, la lettre s est manquante dans le résultat.

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

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

Expression régulière révisé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 abrégés, tels que \s, \r et \t. Les autres caractères, tels que ", ne doivent pas être échappés.

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 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>[^\\\\]+)\\\"\\.$