Suggerimenti e risoluzione dei problemi relativi alla scrittura dei parser

Supportato in:

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

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

  • Un pattern Grok non va a buon fine
  • Un'operazione rename o replace non riesce
  • Errori di sintassi nel codice dell'analizzatore

Pratiche comuni nel codice del parser

Le sezioni seguenti descrivono le best practice, i suggerimenti e le soluzioni per aiutarti a risolvere i problemi.

Evita di utilizzare punti o trattini nei nomi delle variabili.

L'utilizzo di trattini e punti nei nomi delle variabili può causare un comportamento imprevisto, spesso quando vengono eseguite operazioni merge per memorizzare i valori nei campi UDM. Potresti anche riscontrare problemi di analisi intermittenti.

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

Alcune 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 viene utilizzata 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, viene visualizzato un messaggio di errore relativo a un conflitto di nomi.

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

La parola timestamp rappresenta il timestamp del log non elaborato originale creato. R valore impostato in questa variabile intermedia viene salvato nella metadata.event_timestamp campo UDM. 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 di analisi del log non elaborato.

 # 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 estratte dal log non elaborato originale e archiviate nella variabile intermedia when.

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

Non utilizzare i seguenti termini come variabili:

  • collectiontimestamp
  • createtimestamp
  • event
  • filename
  • messaggio
  • spazio dei nomi
  • output
  • numero di errori
  • timestamp
  • fuso orario

Memorizza ogni valore di dati in un campo UDM separato

Non memorizzare 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 tabulazioni nel codice dell'analizzatore. Utilizza solo spazi e rientra due spazi alla volta.

Non eseguire più azioni di unione in una singola operazione

Se unisci più campi in un'unica operazione, potresti ottenere risultati incoerenti. Inserisci invece le istruzioni merge in operazioni separate.

Ad esempio, sostituisci il seguente esempio:

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 rispetto a if else

Se il valore condizionale che stai testando può avere una sola corrispondenza, utilizza l'istruzione condizionale if else. Questo approccio è leggermente più efficiente. Tuttavia, se hai 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 diversi formati. In questo modo puoi trovare log o casi limite unici che il parser potrebbe dover gestire.

Aggiungere commenti descrittivi al codice dell'analizzatore

Aggiungi commenti al codice del parser che spieghino perché l'affermazione è importante, anziché rispetto all'affermazione. Il commento aiuta chiunque gestisca il parser a seguire il flusso. 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}"
    }
  }
}

Inizializza le variabili intermedie in anticipo

Prima di estrarre i valori dal log non elaborato originale, inizializza il valore intermedio e variabili che verranno utilizzate per archiviare i valori di test.

Ciò impedisce la restituzione di un errore che indica che la variabile intermedia inesistente.

La seguente istruzione assegna il valore nella variabile product a nel 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 questo problema, aggiungi un blocco di istruzioni separato che inizializza l'intermedio prima di eseguire le istruzioni del 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 l'istruzione condizionale per verificare se il campo esiste. Inoltre, l'istruzione on_error gestisce gli errori che potrebbero verificarsi.

Converti SHA-256 in base64

Il seguente esempio estrae il valore SHA-256, lo codifica in base64, converte i dati codificati in una stringa esadecimale e poi 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 gli errori negli statement del parser

Non è raro che i log in entrata siano in un formato di log imprevisto o i dati non sono formattati correttamente.

Puoi creare il parser per gestire questi errori. Una best practice è aggiungere gestori on_error al filtro di estrazione e poi 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 on_error per impostare la variabile booleana _not_json. Se _not_json è impostato su true, significa che la voce di log in arrivo non era in formato JSON valido e la voce di log non è stata analizzata correttamente. Se la variabile _not_json è false, la voce di log in arrivo era in 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. Nell'esempio che segue verifica se _not_json è impostato su true, indicando 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 all'analizzatore di elaborare il log non elaborato, normalizzare i campi e creare un record UDM. Il log non elaborato originale viene comunque importato in Google Security Operations e può essere cercato utilizzando la ricerca dei log non elaborati in Google Security Operations.

Il valore passato alla variabile tag viene memorizzato nel campo drop_reason_code della tabella Metriche di importazione. Puoi eseguire una query ad hoc sulla tabella simile alla seguente:

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

Durante la creazione di un parser, potresti riscontrare errori relativi alla convalida, ad esempio Ad esempio, non è impostato un campo obbligatorio nel record UDM. L'errore potrebbe avere il seguente aspetto:

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 non il record UDM generato Includi tutti i campi UDM obbligatori come definito dal valore impostato su metadata.event_type. Di seguito sono riportati altri esempi che possono 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 per i campi obbligatori consulta la guida all'uso dell'UDM.

Un'opzione per risolvere questo tipo di errore è iniziare impostando valori statici su UDM. campi. 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 non elaborato originale 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 dei campi imposta i valori nelle variabili intermedie ai campi UDM.

Puoi espandere questo codice aggiungendo altre variabili intermedie e campi UDM. Dopo aver identificato tutti i campi UDM che devono essere compilati, svolgi i seguenti passaggi:

  • Nella sezione Configurazione di input, aggiungi il codice che estrae i campi da il log originale non elaborato 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 sulla 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"
    }
  }

}

Analizza il testo non strutturato utilizzando 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. Motivi grok facilitano la lettura del codice. Se l'espressione regolare non include caratteri di abbreviazione (ad esempio \w, \s), puoi copiare e incollare l'istruzione direttamente nel codice del parser.

Poiché i pattern Grok sono un ulteriore livello di astrazione nell'istruzione, possono procedure di risoluzione dei problemi più complesse quando si verifica un errore. Di seguito è riportato un esempio di funzione Grok che contiene sia pattern Grok predefiniti sia 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 avere un rendimento migliore. Per quello che segue richiede la corrispondenza con meno della metà dei passaggi di elaborazione. Per un'origine log con un volume potenzialmente elevato, questa è una considerazione importante.

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, puoi 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 dei caratteri di escape

Google Security Operations archivia i dati di log non elaborati in entrata in formato con codifica JSON. Questo serve per assicurarti che le stringhe di caratteri che sembrano essere una scorciatoia per le espressioni regolari vengano interpretate come stringa letterale. Ad esempio, \t viene interpretato come la stringa letterale anziché come un carattere di tabulazione.

L'esempio seguente mostra un log originale non elaborato e il log in formato con codifica JSON. Nota il carattere di escape aggiunto prima di ogni carattere barra inversa 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 un carattere di escape aggiuntivo per estrarre solo il valore. Per trovare una barra rovesciata in del 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, utilizza il comando Esc 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 utilizzato il doppio carattere di escape nella una dichiarazione 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 viene modificata per funzionare all'interno del codice del parser.

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

La tabella seguente riassume quando un'espressione regolare standard deve includere caratteri di escape aggiuntivi prima di essere inclusa nel codice del parser.

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 all'interno di un gruppo di classi di caratteri devono essere preceduti dal carattere di escape.
\\\\
\\\\
Caratteri speciali esterni a un gruppo di classi di caratteri o caratteri brevi non richiedono un carattere di escape aggiuntivo.

Le espressioni regolari devono includere un gruppo di cattura denominato

Un'espressione regolare, ad esempio "^.*$", è una sintassi RE2 valida. Tuttavia, nel 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 cattura valido all'espressione. Se usi Grok tra cui, per impostazione predefinita, un gruppo di acquisizione denominato. Quando utilizzi le sostituzioni di 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 rilevi più di quanto si voglia. Quindi, espandi l'espressione un campo alla volta.

L'esempio seguente inizia utilizzando un gruppo denominato (_catchall) che corrisponde all'intero messaggio. Quindi, genera l'espressione in modo graduale abbinando altre parti del testo. A ogni passaggio, il gruppo denominato _catchall contiene meno testo originale. Continua e ripeti un passaggio alla volta per corrispondere al messaggio fino a quando non hai 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 finché l'espressione non corrisponde all'intera stringa di testo.

Caratteri di escape per i caratteri abbreviati nell'espressione regolare

Ricorda di eseguire l'escape dei caratteri brevi delle espressioni regolari quando utilizzi il metodo nel codice del parser. Di seguito è riportato un esempio di stringa di testo e un'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 del parser, nel risultato manca la letteras.

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 sfuggita aggiuntivo aggiunto ai caratteri di abbreviazione. Nell'esempio precedente, \s deve essere modificato in \\s.

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

Questo vale solo per i caratteri di abbreviazione, come \s, \r e \t. Altri caratteri, come ", non richiedono ulteriori interpretazioni letterali.

Un esempio completo

Questa sezione descrive le regole precedenti come esempio end-to-end. Ecco un stringa di testo non strutturata e l'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 del 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>[^\\\\]+)\\\"\\.$