Logging strutturato

Questo documento illustra il concetto di logging strutturato e i metodi per aggiungere struttura ai campi payload voce di log. Se il payload di log è formattato come oggetto JSON e tale oggetto è archiviato nel campo jsonPayload, la voce di log si chiama log strutturato. Per questi log, puoi creare query che cercano percorsi JSON specifici e puoi indicizzare campi specifici nel payload dei log. Al contrario, quando il payload di log è formattato come stringa e archiviato nel campo textPayload, la voce di log è non strutturata. Puoi eseguire ricerche nel campo di testo, ma non puoi indicizzarne i contenuti.

Per creare voci di log strutturate, esegui una delle seguenti operazioni:

  • Chiama il metodo API entries.write e fornisci un LogEntry completamente formattato.
  • Usare una libreria client di Cloud Logging che scrive log strutturati.
  • Usa il comando gcloud logging write.
  • Utilizzare il servizio BindPlane.
  • Utilizza un agente per scrivere i log:

    • Alcuni servizi Google Cloud contengono un agente di logging integrato che invia i dati scritti a stdout o stderr sotto forma di log a Cloud Logging. Puoi utilizzare questo approccio per i servizi Google Cloud come Google Kubernetes Engine, l'ambiente flessibile di App Engine e Cloud Functions.

    • Per le macchine virtuali (VM) Compute Engine, puoi installare e configurare Ops Agent o l'agente Logging legacy, quindi utilizzare l'agente installato per inviare i log a Cloud Logging.

Per ulteriori informazioni su questi approcci, consulta le sezioni seguenti.

Scrivi i log utilizzando le librerie client o l'API

Puoi scrivere dati di log utilizzando le librerie client di Cloud Logging, che chiamano l'API Cloud Logging, o chiamando direttamente l'API Cloud Logging. Le librerie client possono semplificare la compilazione dei campi JSON speciali acquisendo automaticamente alcune informazioni e fornendo interfacce per compilare i campi in modo appropriato. Tuttavia, per avere il controllo completo sulla struttura dei tuoi payload, chiama direttamente l'API Cloud Logging e passa l'intera struttura di LogEntry all'API Cloud Logging.

Per saperne di più, consulta la documentazione di entries.write.

Per esempi di codice, consulta Scrittura di log strutturati.

Scrivi i log utilizzando gcloud CLI

Puoi scrivere dati di log utilizzando gcloud CLI. L'interfaccia supporta log non strutturati e log strutturati. Se vuoi scrivere un log strutturato, fornisci al comando un oggetto JSON serializzato.

Per una guida rapida, consulta Scrivere ed eseguire query sulle voci di log con Google Cloud CLI.

Per esempi di codice, consulta il riferimento a gcloud logging write.

Scrivi i log utilizzando BindPlane

Puoi utilizzare il servizio BindPlane per inviare log a Logging. Per questi log, i payload sono in formato JSON e sono strutturati in base al sistema di origine. Per informazioni su come trovare e visualizzare i log importati utilizzando BindPlane, consulta la guida rapida di BindPlane.

Scrivi i log utilizzando un agente

Per ottenere i log dalle istanze di Compute Engine, puoi utilizzare l'Ops Agent o l'agente Cloud Logging legacy. Entrambi gli agenti possono raccogliere metriche da applicazioni di terze parti e fornire assistenza per il logging strutturato:

  • Ops Agent è l'agente consigliato per la raccolta dei dati di telemetria dalle istanze Compute Engine. Questo agente combina logging e metriche in un unico agente, fornisce una configurazione basata su YAML e offre logging a velocità effettiva elevata.

    Per informazioni su come configurare Ops Agent per il supporto del logging strutturato o per personalizzare la forma di un log strutturato, consulta Configurare Ops Agent.

  • L'agente legacy Cloud Logging raccoglie i log. Questo agente non raccoglie altre forme di telemetria.

Il resto di questa sezione è specifico per l'agente Logging legacy.

Agente Logging: campi JSON speciali

Alcuni campi dell'oggetto JSON vengono riconosciuti come speciali dall'agente Logging legacy ed estratti nella struttura LogEntry. Questi campi JSON speciali possono essere utilizzati per impostare i seguenti campi in LogEntry:

  • severity
  • spanId
  • labels definito dall'utente
  • httpRequest

Poiché JSON è più preciso e versatile delle righe di testo, puoi utilizzare oggetti JSON per scrivere messaggi su più righe e aggiungere metadati.

Per creare voci di log strutturate per le tue applicazioni utilizzando il formato semplificato, consulta la seguente tabella, che elenca i campi e i relativi valori in JSON:

Campo log JSON Campo LogEntry Funzione agente Cloud Logging Valore di esempio
severity severity L'agente Logging tenta di trovare una corrispondenza con una serie di stringhe di gravità comuni, tra cui l'elenco di stringhe LogSeverity riconosciute dall'API Logging. "severity":"ERROR"
message textPayload (o parte di jsonPayload) Il messaggio visualizzato sulla riga della voce di log in Esplora log. "message":"There was an error in the application."

Nota: message viene salvato come textPayload se è l'unico campo rimanente dopo che l'agente Logging ha spostato gli altri campi per scopi speciali e detect_json non è stato abilitato; in caso contrario, message rimane in jsonPayload. detect_json non è applicabile ad ambienti di logging gestiti come Google Kubernetes Engine. Se la voce di log contiene un'analisi dello stack di eccezioni, è necessario impostare l'analisi dello stack di eccezioni in questo campo di log JSON message, in modo che l'analisi dello stack di eccezioni possa essere analizzata e salvata in Error Reporting.
log (solo versione precedente di Google Kubernetes Engine) textPayload Si applica solo alla versione precedente di Google Kubernetes Engine: se, dopo lo spostamento di campi per scopi speciali, rimane solo un campo log, questo campo viene salvato come textPayload.
httpRequest httpRequest Un record strutturato nel formato del campo LogEntry HttpRequest. "httpRequest":{"requestMethod":"GET"}
di campi temporali timestamp Per ulteriori informazioni, consulta la sezione Campi correlati al tempo. "time":"2020-10-12T07:20:50.52Z"
logging.googleapis.com/insertId insertId Per ulteriori informazioni, consulta insertId nella pagina LogEntry. "logging.googleapis.com/insertId":"42"
logging.googleapis.com/labels labels Il valore di questo campo deve essere un record strutturato. Per maggiori informazioni, consulta labels nella pagina LogEntry. "logging.googleapis.com/labels": {"user_label_1":"value_1","user_label_2":"value_2"}
logging.googleapis.com/operation operation Il valore di questo campo viene utilizzato anche da Esplora log per raggruppare le voci di log correlate. Per ulteriori informazioni, consulta operation nella pagina LogEntry. "logging.googleapis.com/operation": {"id":"get_data","producer":"github.com/MyProject/MyApplication", "first":"true"}
logging.googleapis.com/sourceLocation sourceLocation Informazioni sulla posizione del codice sorgente associate all'eventuale voce di log. Per ulteriori informazioni, consulta LogEntrySourceLocation nella pagina LogEntry. "logging.googleapis.com/sourceLocation": {"file":"get_data.py","line":"142","function":"getData"}
logging.googleapis.com/spanId spanId L'ID intervallo all'interno della traccia associata alla voce di log. Per ulteriori informazioni, consulta spanId nella pagina LogEntry. "logging.googleapis.com/spanId":"000000000000004a"
logging.googleapis.com/trace trace Nome di risorsa della traccia associata all'eventuale voce di log. Per ulteriori informazioni, consulta trace nella pagina LogEntry. "logging.googleapis.com/trace":"projects/my-projectid/traces/0679686673a"

Nota: se non scrivi in stdout o stderr, il valore di questo campo deve essere formattato come projects/[PROJECT-ID]/traces/[TRACE-ID], in modo che possa essere utilizzato da Esplora log e dal visualizzatore di tracce per raggruppare le voci di log e visualizzarle in linea con le tracce. Se autoformat_stackdriver_trace è true e [V] corrisponde al formato di ResourceTrace traceId, il campo LogEntry trace avrà il valore projects/[PROJECT-ID]/traces/[V].
logging.googleapis.com/trace_sampled traceSampled Il valore di questo campo deve essere true o false. Per ulteriori informazioni, consulta traceSampled nella pagina LogEntry. "logging.googleapis.com/trace_sampled": false

Per creare voci di log nel formato semplificato, utilizza i campi per creare una rappresentazione JSON della voce. Tutti i campi sono facoltativi.

Di seguito è riportato un esempio di voce di log JSON semplificata:

{
  "severity":"ERROR",
  "message":"There was an error in the application.",
  "httpRequest":{
    "requestMethod":"GET"
  },
  "times":"2020-10-12T07:20:50.52Z",
  "logging.googleapis.com/insertId":"42",
  "logging.googleapis.com/labels":{
    "user_label_1":"value_1",
    "user_label_2":"value_2"
  },
  "logging.googleapis.com/operation":{
    "id":"get_data",
    "producer":"github.com/MyProject/MyApplication",
     "first":"true"
  },
  "logging.googleapis.com/sourceLocation":{
    "file":"get_data.py",
    "line":"142",
    "function":"getData"
  },
  "logging.googleapis.com/spanId":"000000000000004a",
  "logging.googleapis.com/trace":"projects/my-projectid/traces/06796866738c859f2f19b7cfb3214824",
  "logging.googleapis.com/trace_sampled":false
}

Di seguito è riportato un esempio della voce di log risultante:

{
  "insertId": "42",
  "jsonPayload": {
    "message": "There was an error in the application",
    "times": "2020-10-12T07:20:50.52Z"
  },
  "httpRequest": {
    "requestMethod": "GET"
  },
  "resource": {
    "type": "k8s_container",
    "labels": {
      "container_name": "hello-app",
      "pod_name": "helloworld-gke-6cfd6f4599-9wff8",
      "project_id": "stackdriver-sandbox-92334288",
      "namespace_name": "default",
      "location": "us-west4",
      "cluster_name": "helloworld-gke"
    }
  },
  "timestamp": "2020-11-07T15:57:35.945508391Z",
  "severity": "ERROR",
  "labels": {
    "user_label_2": "value_2",
    "user_label_1": "value_1"
  },
  "logName": "projects/stackdriver-sandbox-92334288/logs/stdout",
  "operation": {
    "id": "get_data",
    "producer": "github.com/MyProject/MyApplication",
    "first": true
  },
  "trace": "projects/my-projectid/traces/06796866738c859f2f19b7cfb3214824",
  "sourceLocation": {
    "file": "get_data.py",
    "line": "142",
    "function": "getData"
  },
  "receiveTimestamp": "2020-11-07T15:57:42.411414059Z",
  "spanId": "000000000000004a"
}

Agente Logging: configurazione

L'agente Logging legacy, google-fluentd, è una pacchettizzazione specifica di Cloud Logging del raccoglitore dati di log Fluentd. L'agente Logging include la configurazione Fluentd predefinita e utilizza i plug-in di input Fluentd per estrarre log eventi da origini esterne, come file su disco, o per analizzare i record di log in entrata.

Fluentd dispone di un elenco di analizzatori sintattico supportati che estraggono i log e li convertono in payload strutturati (JSON).

Configurando un'origine log con format [PARSER_NAME], puoi utilizzare i parser integrati forniti da Fluentd. Per informazioni sulla configurazione dell'agente Logging legacy, consulta Configurare l'agente Logging.

I seguenti esempi di codice mostrano la configurazione Fluentd, il record del log di input e il payload strutturato di output, che fa parte di una voce di log di Cloud Logging:

  • Configurazione fluida:

      <source>
        @type tail
    
        format syslog # This uses a predefined log format regex named
                      # `syslog`. See details at https://docs.fluentd.org/parser/syslog.
    
        path /var/log/syslog
        pos_file /var/lib/google-fluentd/pos/syslog.pos
        read_from_head true
        tag syslog
      </source>
    
  • Record di log (input):

      <6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test
    
  • Payload strutturato (output):

      jsonPayload: {
          "pri": "6",
          "host": "192.168.0.1",
          "ident": "fluentd",
          "pid": "11111",
          "message": "[error] Syslog test"
      }
    

Per ulteriori informazioni sul funzionamento dell'analizzatore sintattico syslog, consulta la documentazione dettagliata di Fluentd.

Agente Logging: parser standard abilitati per impostazione predefinita

La seguente tabella include i parser standard inclusi nell'agente se abiliti il logging strutturato:

Nome parser File di configurazione
syslog /etc/google-fluentd/config.d/syslog.conf
nginx /etc/google-fluentd/config.d/nginx.conf
apache2 /etc/google-fluentd/config.d/apache.conf
apache_error /etc/google-fluentd/config.d/apache.conf

Per istruzioni su come abilitare il logging strutturato durante l'installazione dell'agente Logging legacy, consulta la sezione Installazione.

Agente Logging: installazione

Per abilitare il logging strutturato, devi modificare la configurazione predefinita dell'agente Logging legacy quando lo installi o lo reinstalli. L'abilitazione del logging strutturato sostituisce i file di configurazione elencati in precedenza, ma non modifica il funzionamento dell'agente stesso.

Quando attivi il logging strutturato, i log elencati vengono convertiti in voci di log con formati diversi rispetto a prima dell'abilitazione dei log strutturati. Se i log vengono instradati a destinazioni esterne a Logging, la modifica potrebbe influire sulle applicazioni di post-elaborazione. Ad esempio, se si esegue il routing dei log a BigQuery, BigQuery rifiuta le nuove voci di log per il resto della giornata in quanto presenta uno schema non corretto.

Per istruzioni sull'installazione dell'agente Logging legacy e sull'abilitazione del logging strutturato, consulta Installazione dell'agente Logging.

Puoi trovare i file di configurazione legacy dell'agente Logging in /etc/google-fluentd/config.d/, che ora dovrebbero includere i parser standard abilitati per impostazione predefinita.

Agente Logging: configura il formato dei log degli accessi Apache

Per impostazione predefinita, l'agente Logging legacy archivia i dati del log di accesso Apache nel campo jsonPayload. Ad esempio:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": ...,
  "jsonPayload": {
    "user"   : "some-user",
    "method" : "GET",
    "code"   : 200,
    "size"   : 777,
    "host"   : "192.168.0.1",
    "path"   : "/some-path",
    "referer": "some-referer",
    "agent"  : "Opera/12.0"
  },
  ...
}

In alternativa, puoi configurare l'agente Logging legacy in modo che estragga determinati campi nel campo httpRequest. Ad esempio:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": {
    "requestMethod": "GET",
    "requestUrl": "/some-path",
    "requestSize": "777",
    "status": "200",
    "userAgent": "Opera/12.0",
    "serverIp": "192.168.0.1",
    "referrer":"some-referrer",
  },
  "jsonPayload": {
    "user":"some-user"
  },
  ...
}

La configurazione del campo httpRequest, come mostrato nell'esempio precedente, facilita il tracciamento: la console Google Cloud presenta tutti i log per una determinata richiesta HTTP in una gerarchia padre-figlio.

Per configurare questa estrazione, aggiungi quanto segue alla fine di /etc/google-fluentd/config.d/apache.conf:

<filter apache-access>
  @type record_transformer
  enable_ruby true
  <record>
    httpRequest ${ {"requestMethod" => record['method'], "requestUrl" => record['path'], "requestSize" => record['size'], "status" => record['code'], "userAgent" => record['agent'], "serverIp" => record['host'],
    "referer" => record['referer']} }
  </record>
  remove_keys method, path, size, code, agent, host, referer
</filter>

Per ulteriori dettagli sulla configurazione delle voci di log, consulta Modifica dei record di log.

Agente Logging: configurazione del formato del log degli accessi nginx

Per impostazione predefinita, l'agente Logging legacy archivia i dati del log di accesso nginx nel campo jsonPayload. Ad esempio:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": ...,
  "jsonPayload": {
    "remote":"127.0.0.1",
    "host":"192.168.0.1",
    "user":"some-user",
    "method":"GET",
    "path":"/some-path",
    "code":"200",
    "size":"777",
    "referrer":"some-referrer",
    "agent":"Opera/12.0",
    "http_x_forwarded_for":"192.168.3.3"
  },
  ...
}

In alternativa, puoi configurare l'agente Logging legacy in modo che estragga determinati campi nel campo httpRequest. Ad esempio:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": {
    "requestMethod": "GET",
    "requestUrl": "/some-path",
    "requestSize": "777",
    "status": "200",
    "userAgent": "Opera/12.0",
    "remoteIp": "127.0.0.1",
    "serverIp": "192.168.0.1",
    "referrer":"some-referrer",
  },
  "jsonPayload": {
    "user":"some-user",
    "http_x_forwarded_for":"192.168.3.3"
  },
  ...
}

La configurazione del campo httpRequest, come mostrato nell'esempio precedente, facilita il tracciamento: la console Google Cloud presenta tutti i log per una determinata richiesta HTTP in una gerarchia padre-figlio.

Per configurare questa estrazione, aggiungi quanto segue alla fine di /etc/google-fluentd/config.d/nginx.conf:

<filter nginx-access>
  @type record_transformer
  enable_ruby true
  <record>
    httpRequest ${ {"requestMethod" => record['method'], "requestUrl" => record['path'], "requestSize" => record['size'], "status" => record['code'], "userAgent" => record['agent'], "remoteIp" => record['remote'], "serverIp" => record['host'], "referer" => record['referer']} }
  </record>
  remove_keys method, path, size, code, agent, remote, host, referer
</filter>

Per ulteriori dettagli sulla configurazione delle voci di log, consulta Modifica dei record di log.

Scrivi il tuo parser

Se i tuoi log non sono supportati dai parser standard, puoi scrivere autonomamente il tuo parser. I parser sono costituiti da un'espressione regolare utilizzata per associare i record dei log e applicare etichette alle parti.

I seguenti esempi di codice mostrano una riga di log nel record di log, una configurazione con un'espressione regolare che indica il formato della riga di log e la voce di log archiviata:

  • Una riga di log nel record di log:

    REPAIR CAR $500
    
  • Una configurazione con un'espressione regolare che indica il formato della riga di log:

    $ sudo vim /etc/google-fluentd/config.d/test-structured-log.conf
    $ cat /etc/google-fluentd/config.d/test-structured-log.conf
    <source>
      @type tail
    
      # Format indicates the log should be translated from text to
      # structured (JSON) with three fields, "action", "thing" and "cost",
      # using the following regex:
      format /(?<action>\w+) (?<thing>\w+) \$(?<cost>\d+)/
      # The path of the log file.
      path /tmp/test-structured-log.log
      # The path of the position file that records where in the log file
      # we have processed already. This is useful when the agent
      # restarts.
      pos_file /var/lib/google-fluentd/pos/test-structured-log.pos
      read_from_head true
      # The log tag for this log input.
      tag structured-log
    </source>
    
  • La voce di log risultante:

    {
    insertId:  "eps2n7g1hq99qp"
    jsonPayload: {
      "action": "REPAIR"
      "thing": "CAR"
      "cost": "500"
    }
    labels: {
      compute.googleapis.com/resource_name:  "add-structured-log-resource"
    }
    logName:  "projects/my-sample-project-12345/logs/structured-log"
    receiveTimestamp:  "2023-03-21T01:47:11.475065313Z"
    resource: {
      labels: {
        instance_id:  "3914079432219560274"
        project_id:  "my-sample-project-12345"
        zone:  "us-central1-c"
      }
      type:  "gce_instance"
    }
    timestamp:  "2023-03-21T01:47:05.051902169Z"
    }
    

Risoluzione dei problemi

Per risolvere i problemi comuni rilevati durante l'installazione o l'interazione con l'agente Logging legacy, consulta Risoluzione dei problemi dell'agente.

Passaggi successivi