Suggerimenti e risoluzione dei problemi relativi alla scrittura dei parser

Questo documento descrive i problemi che potresti riscontrare quando scrivi il codice del parser.

Durante la scrittura del codice del parser, potresti riscontrare errori quando le istruzioni di analisi non funzionano come previsto. Le situazioni che potrebbero generare errori sono le seguenti:

  • Una sequenza Grok non va a buon fine
  • Un'operazione rename o replace non va a buon fine
  • Errori di sintassi nel codice del parser

Pratiche comuni nel codice del parser

Le seguenti sezioni descrivono le best practice, i suggerimenti e le soluzioni per la risoluzione dei problemi.

Evita di utilizzare punti o trattini nei nomi delle variabili.

L'utilizzo di trattini e punti nei nomi delle variabili può causare comportamenti imprevisti, spesso durante l'esecuzione di operazioni merge per archiviare i valori nei campi UDM. Potresti anche riscontrare problemi di analisi intermittente.

Ad esempio, non utilizzare i seguenti nomi di variabili:

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

Utilizza invece il seguente nome di variabile: my_variable_result.

Non utilizzare termini con un significato speciale nei nomi delle variabili

Determinate parole, come event e timestamp, possono avere un significato speciale nel codice del parser.

La stringa event viene spesso utilizzata per rappresentare un singolo record UDM e nell'istruzione @output. Se un messaggio di log include un campo denominato event o se definisci una variabile intermedia denominata event e il codice del parser utilizza la parola event nell'istruzione @output, riceverai un messaggio di errore relativo a un conflitto di nomi.

Rinomina la variabile intermedia in un altro nome o utilizza il termine event1 come prefisso nei nomi dei campi UDM e nell'istruzione @output.

La parola timestamp rappresenta il timestamp di creazione del log non elaborato originale. Un valore impostato in questa variabile intermedia viene salvato nel campo UDM metadata.event_timestamp. Il termine @timestamp rappresenta la data e l'ora in cui il log non elaborato è stato analizzato per creare un record UDM.

L'esempio seguente imposta il campo UDM metadata.event_timestamp sulla data e sull'ora in cui il log non elaborato è stato analizzato.

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

L'esempio seguente imposta il campo UDM metadata.event_timestamp sulla data e sull'ora estratti dal log non elaborato originale e archiviati nella variabile intermedia when.

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

Non utilizzare i seguenti termini come variabili:

  • timestamp raccolta
  • createtimestamp
  • event
  • nomefile
  • messaggio
  • spazio dei nomi
  • output
  • numero di errori
  • timestamp
  • fuso orario

Archivia ogni valore dei dati in un campo UDM separato

Non archiviare più campi in un singolo campo UDM concatenandoli con un delimitatore. Di seguito è riportato un esempio:

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

Archivia invece ogni valore in un campo UDM separato.

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

Utilizza gli spazi anziché le tabulazioni nel codice

Non utilizzare le schede nel codice del parser. Utilizza solo spazi e rientra due spazi alla volta.

Non eseguire più azioni di unione in una singola operazione

Se unisci più campi in una singola operazione, i risultati potrebbero essere incoerenti. Puoi invece inserire le istruzioni merge in operazioni separate.

Sostituisci ad esempio il seguente:

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

Con questo:

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

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

Scelta delle espressioni condizionali if e if else

Se il valore condizionale che stai verificando può avere una sola corrispondenza, utilizza l'istruzione condizionale if else. Questo approccio è leggermente più efficiente. Tuttavia, in uno scenario in cui il valore testato potrebbe corrispondere più di una volta, utilizza più istruzioni if distinte e ordina le istruzioni dal caso più generico a quello più specifico.

Scegli un set rappresentativo di file di log per testare le modifiche del parser

Una best practice consiste nel testare il codice del parser utilizzando campioni di log non elaborati con un'ampia varietà di formati. Ciò consente di trovare log univoci o casi limite che il parser potrebbe dover gestire.

Aggiungere commenti descrittivi al codice del parser

Aggiungi commenti al codice del parser che spieghino perché l'affermazione è importante, anziché la funzione dell'affermazione. Il commento aiuta chiunque mantenga l'analizzatore sintattico a seguire la procedura. Di seguito è riportato un esempio:

# 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}"
    }
  }
}

Inizializzare anticipatamente le variabili intermedie

Prima di estrarre i valori dal log originale non elaborato, inizializza le variabili intermedie che verranno utilizzate per archiviare i valori di test.

Questo impedisce che venga restituito un errore che indica che la variabile intermedia non esiste.

La seguente istruzione assegna il valore della variabile product al campo UDM metadata.product_name.

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

Se la variabile product non esiste, viene visualizzato il seguente errore:

"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"

Puoi aggiungere un'istruzione on_error per rilevare l'errore. Di seguito è riportato un esempio:

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

L'istruzione di esempio precedente rileva correttamente l'errore di analisi in una variabile intermedia booleana chiamata _error_does_not_exist. Non consente di utilizzare la variabile product in un'istruzione condizionale, ad esempio if. Di seguito è riportato un esempio:

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

L'esempio precedente restituisce il seguente errore perché la clausola condizionale if non supporta le istruzioni on_error:

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

Per risolvere il problema, aggiungi un blocco di istruzioni separato che inizializza le variabili intermedie prima di eseguire le istruzioni di filtro di estrazione (json, csv, xml, kv o grok). Di seguito è riportato un esempio.

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"
  }
}

Lo snippet aggiornato del codice del parser gestisce più scenari utilizzando un'istruzione condizionale per verificare se il campo esiste. Inoltre, l'istruzione on_error gestisce gli errori che potrebbero verificarsi.

Converti SHA-256 in base64

L'esempio seguente estrae il valore SHA-256, lo codifica in base64, converte i dati codificati in una stringa esadecimale, quindi sostituisce campi specifici con i valori estratti ed elaborati.

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}"
  }
  }
}

Gestire errori nelle istruzioni del parser

Non è raro che i log in entrata abbiano un formato di log imprevisto o che i dati siano formattati in modo errato.

Puoi creare il parser per gestire questi errori. Una best practice è aggiungere gestori on_error al filtro di estrazione, quindi testare la variabile intermedia prima di passare al segmento successivo della logica del parser.

L'esempio seguente utilizza il filtro di estrazione json con un'istruzione on_error per impostare la variabile booleana _not_json. Se il criterio _not_json è impostato su true, significa che la voce di log in entrata non era in un formato JSON valido e che la voce di log non è stata analizzata correttamente. Se la variabile _not_json è false, la voce di log in entrata era in un formato JSON valido.

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

Puoi anche verificare se un campo è nel formato corretto. L'esempio seguente verifica se _not_json è impostato su true, per indicare che il log non era nel formato previsto.

 # 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" }
    }
  }

Ciò garantisce che l'analisi non vada a buon fine se i log vengono importati con un formato errato per il tipo di log specificato.

Utilizza il filtro drop con la variabile tag in modo che la condizione venga acquisita nella tabella delle metriche di importazione in BigQuery.

  • TAG_UNSUPPORTED
  • TAG_MALFORMED_ENCODING
  • TAG_MALFORMED_MESSAGE
  • TAG_NO_SECURITY_VALUE

Il filtro drop impedisce al parser di elaborare il log non elaborato, normalizzare i campi e creare un record UDM. Il log non elaborato originale viene ancora importato in Google Security Operations e può essere cercato utilizzando la ricerca nei log non elaborata in Google Security Operations.

Il valore passato alla variabile tag viene archiviato nel campo drop_reason_code nella tabella delle metriche di importazione. Puoi eseguire una query ad hoc sulla tabella, come in questo esempio:

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

Risolvere gli errori di convalida

Quando crei un parser, potresti riscontrare errori relativi alla convalida. Ad esempio, nel record UDM non è impostato un campo obbligatorio. L'errore potrebbe essere simile al seguente:

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

Il codice del parser viene eseguito correttamente, ma il record UDM generato non include tutti i campi UDM obbligatori come definito dal valore impostato su metadata.event_type. Di seguito sono riportati alcuni esempi aggiuntivi che potrebbero causare questo errore:

  • Se metadata.event_type è USER_LOGIN e il campo UDM target.user value non è impostato.
  • Se metadata.event_type è NETWORK_CONNECTION e il target.hostnamecampo UDM non è impostato.

Per ulteriori informazioni sul campo UDM metadata.event_type e su quelli obbligatori, consulta la guida all'uso dell'UDM.

Un'opzione per risolvere questo tipo di errore è iniziare impostando valori statici per i campi UDM. Dopo aver definito tutti i campi UDM necessari, esamina il log non elaborato originale per vedere quali valori analizzare e salvare nel record UDM. Se il log originale non elaborato non contiene determinati campi, potrebbe essere necessario impostare valori predefiniti.

Di seguito è riportato un modello di esempio, specifico per un tipo di evento USER_LOGIN, che illustra questo approccio.

Tieni presente quanto segue:

  • Il modello inizializza le variabili intermedie e imposta ciascuna su una stringa statica.
  • Il codice nella sezione Assegnazione campi imposta i valori nelle variabili intermedie nei campi UDM.

Puoi espandere questo codice aggiungendo ulteriori variabili intermedie e campi UDM. Dopo aver identificato tutti i campi UDM che devono essere compilati, segui questi passaggi:

  • Nella sezione Configurazione di input, aggiungi il codice che estrae i campi dal log non elaborato originale e imposta i valori sulle variabili intermedie.

  • Nella sezione Date Extract, aggiungi il codice che estrae il timestamp dell'evento dal log non elaborato originale, lo trasforma e lo imposta come variabile intermedia.

  • Se necessario, sostituisci il valore inizializzato impostato in ogni variabile intermedia con una stringa vuota.

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"
    }
  }

}

Analizzare il testo non strutturato con una funzione Grok

Quando utilizzi una funzione Grok per estrarre valori da testo non strutturato, puoi utilizzare pattern Grok predefiniti ed istruzioni di espressioni regolari. I pattern Grok semplificano la lettura del codice. Se l'espressione regolare non include caratteri abbreviati (come \w, \s), puoi copiare e incollare l'istruzione direttamente nel codice del parser.

Poiché i pattern Grok sono un livello di astrazione aggiuntivo nell'istruzione, potrebbero rendere la risoluzione dei problemi più complessa quando si verifica un errore. Di seguito è riportato un esempio di funzione Grok che contiene sia pattern Grok predefiniti che espressioni regolari.

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+).*"
    ]
  }
}

Un'istruzione di estrazione senza pattern Grok potrebbe offrire prestazioni migliori. Nel seguente esempio, la corrispondenza richiede meno della metà dei passaggi di elaborazione. Questa è una considerazione importante per un'origine log con volumi potenzialmente elevati.

Comprendere le differenze tra le espressioni regolari RE2 e PCRE

I parser di Google Security Operations utilizzano RE2 come motore delle espressioni regolari. Se hai familiarità con la sintassi PCRE, potresti notare differenze. Ecco un esempio:

Di seguito è riportata un'istruzione PCRE: (?<_custom_field>\w+)\s

Di seguito è riportata un'istruzione RE2 per il codice del parser: (?P<_custom_field>\\w+)\\s

Assicurati di utilizzare l'interpretazione letterale per i caratteri di escape

Google Security Operations archivia i dati di log non elaborati in entrata in formato con codifica JSON. Ciò garantisce che le stringhe di caratteri che sembrano essere espressioni regolari come forma abbreviata vengano interpretate come stringa letterale. Ad esempio, \t viene interpretato come stringa letterale, anziché come carattere di tabulazione.

L'esempio seguente è un log originale non elaborato e il log in formato con codifica JSON. Nota il carattere di escape aggiunto prima di ogni barra rovesciata che circonda il termine entry.

Di seguito è riportato il log originale non elaborato:

field=\entry\

Di seguito è riportato il log convertito nel formato con codifica JSON:

field=\\entry\\

Quando utilizzi un'espressione regolare nel codice del parser, devi aggiungere altri caratteri di escape se desideri estrarre solo il valore. Per trovare una barra rovesciata nel log non elaborato originale, utilizza quattro barre rovesciate nell'istruzione di estrazione.

Di seguito è riportata un'espressione regolare per il codice del parser:

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

Di seguito è riportato il risultato generato. Il gruppo denominato _value memorizza il termine entry:

"_value": "entry"

Quando sposti un'istruzione di espressione regolare standard nel codice dell'analizzatore sintattico, esegui l'escape dei caratteri abbreviati delle espressioni regolari nell'istruzione di estrazione. Ad esempio, modifica \s in \\s.

Lascia invariati i caratteri speciali dell'espressione regolare se viene eseguito il doppio escape nell'istruzione di estrazione. Ad esempio, \\ rimane invariato come \\.

Di seguito è riportata un'espressione regolare standard:

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

La seguente espressione regolare è stata modificata per funzionare nel codice del parser.

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

La tabella seguente riassume i casi in cui un'espressione regolare standard deve includere caratteri di escape aggiuntivi prima di includerla nel codice dell'analizzatore sintattico.

Espressione regolare Espressione regolare modificata per il codice del parser Descrizione della modifica
\s
\\s
I caratteri abbreviati devono essere preceduti dal carattere di escape.
\.
\\.
I caratteri riservati devono essere preceduti dal carattere di escape.
\\"
\\\"
I caratteri riservati devono essere preceduti dal carattere di escape.
\]
\\]
I caratteri riservati devono essere preceduti dal carattere di escape.
\|
\\|
I caratteri riservati devono essere preceduti dal carattere di escape.
[^\\]+
[^\\\\]+
I caratteri speciali di un gruppo di classi di caratteri devono essere preceduti dal carattere di escape.
\\\\
\\\\
I caratteri speciali esterni a un gruppo di classi di caratteri o i caratteri abbreviati non richiedono caratteri di escape aggiuntivi.

Le espressioni regolari devono includere un gruppo di acquisizione denominato

Un'espressione regolare, come "^.*$", è una sintassi RE2 valida. Tuttavia, il codice del parser non riesce e restituisce il seguente errore:

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

Devi aggiungere un gruppo di acquisizione valido all'espressione. Se utilizzi i pattern Grok, questi includono per impostazione predefinita un gruppo di acquisizione denominato. Quando utilizzi gli override delle espressioni regolari, assicurati di includere un gruppo denominato.

Di seguito è riportato un esempio di espressione regolare nel codice del parser:

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

Di seguito è riportato il risultato, che mostra il testo assegnato al gruppo denominato _catchall.

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

Utilizza un gruppo con nome catchall per iniziare mentre crei l'espressione

Quando crei un'istruzione di estrazione, inizia con un'espressione che rileva più di quanto vuoi. Quindi, espandi l'espressione un campo alla volta.

L'esempio seguente inizia utilizzando un gruppo denominato (_catchall) che corrisponde all'intero messaggio. Poi crea l'espressione gradualmente, abbinando altre parti del testo. A ogni passaggio, il gruppo denominato _catchall contiene meno del testo originale. Continua e ripeti un passaggio alla volta per corrispondere al messaggio finché non avrai più bisogno del gruppo denominato _catchall.

Passaggio Espressione regolare nel codice del parser Output del gruppo di acquisizione denominato _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\".
Continua fino a quando l'espressione corrisponde all'intera stringa di testo.

Esegui l'escape dei caratteri brevi nell'espressione regolare

Ricorda di eseguire l'escape dei caratteri abbreviati delle espressioni regolari quando utilizzi l'espressione nel codice dell'analizzatore sintattico. Di seguito è riportato un esempio di stringa di testo e l'espressione regolare standard che estrae la prima parola, This.

  This is a sample log.

La seguente espressione regolare standard estrae la prima parola, This. Tuttavia, quando esegui questa espressione nel codice dell'analizzatore sintattico, nel risultato manca la lettera s.

Espressione regolare standard Output del gruppo di acquisizione denominato _firstWord
"^(?P<_firstWord>[^\s]+)\s.*$" "_firstWord": "Thi",

Questo perché le espressioni regolari nel codice del parser richiedono un carattere di escape aggiuntivo aggiunto ai caratteri brevi. Nell'esempio precedente, \s deve essere modificato in \\s.

Espressione regolare modificata per il codice del parser Output del gruppo di acquisizione denominato _firstWord
"^(?P<_firstWord>[^\\s]+)\\s.*$" "_firstWord": "This",

Questo vale solo per i caratteri brevi, come \s, \r e \t. Altri caratteri, come "`, non devono essere preceduti dal carattere di escape.

Un esempio completo

Questa sezione descrive le regole precedenti come esempio end-to-end. Di seguito è riportata una stringa di testo non strutturata e un'espressione regolare standard scritta per analizzare la stringa. Infine, include l'espressione regolare modificata che funziona nel codice del parser.

Di seguito è riportata la stringa di testo originale.

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

Di seguito è riportata un'espressione regolare RE2 standard che analizza la stringa di testo.

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

Questa espressione estrae i seguenti campi.

Gruppo di corrispondenze Posizione carattere Stringa di testo
Corrispondenza completa 0-53
User \"BOB\" logged on to workstation \"DESKTOP-01\".
Gruppo "_user" 7-10
BOB
Gruppo 2. 13-22
logged on
Raggruppa "_device" 40-50
DESKTOP-01

Questa è l'espressione modificata. L'espressione regolare RE2 standard è stata modificata per funzionare nel codice del parser.

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