Strukturiertes Logging

In diesem Dokument werden das Konzept des strukturierten Loggings und die Methoden zum Hinzufügen einer Struktur zu den Nutzlastfeldern von Logeinträgen erläutert.

Übersicht

In Cloud Logging bezieht sich der Begriff strukturierte Logs auf Logeinträge, deren Nutzlasten mit dem Feld jsonPayload strukturiert werden. Das strukturierte Logging gilt für vom Nutzer geschriebene Logs.

Sie haben mehrere Möglichkeiten, strukturierte Logs in Logging zu schreiben:

  • Mit der Cloud Logging API Logeinträge schreiben
  • Mit dem gcloud-Befehlszeilentool Logeinträge schreiben
  • BindPlane-Dienst zum Aufnehmen von Logs verwenden
  • Serialisierte JSON-Objekte für den Logging-Agent bereitstellen

Jede dieser Methoden wird in den folgenden Abschnitten erläutert.

Logging API verwenden

Wenn Sie mit der Cloud Logging API Logeinträge schreiben, können Sie die Struktur der Nutzlasten steuern. Senden Sie dazu die vollständige Struktur LogEntry mit einem jsonPayload an die Cloud Logging API.

Weitere Informationen finden Sie in der Referenz zu entries.write. Codebeispiele finden Sie unter Strukturierte Logs schreiben.

gcloud-Tool verwenden

Wenn Sie mit dem gcloud-Tool Logeinträge schreiben, können Sie die Struktur der Nutzlasten durch Senden der vollständigen LogEntry-Struktur mit einem jsonPayload an die Cloud Logging API steuern.

Codebeispiele finden Sie in der Referenz zu gcloud logging write.

BindPlane verwenden

Wenn Sie den BindPlane-Dienst zum Aufnehmen von Logs nutzen, haben die Nutzlasten das JSON-Format und sind gemäß dem Quellsystem strukturiert. Informationen zum Suchen und Aufrufen von Logs, die über BindPlane aufgenommen wurden, finden Sie in der BindPlane-Dokumentation unter Logdaten finden.

Logging-Agent verwenden

Wenn Sie Logeinträge mit dem Cloud Logging-Agent abrufen, können Sie festlegen, dass der Logging-Agent die Nutzlasten in das JSON-Format konvertiert.

Wenn Sie Google Kubernetes Engine oder die flexible App Engine-Umgebung verwenden, können Sie strukturierte Logs als JSON-Objekte schreiben, die in einer einzigen Zeile in stdout oder stderr serialisiert sind. Der Logging-Agent sendet dann die strukturierten Logs als jsonPayload der Struktur LogEntry an Cloud Logging.

Einige Felder im JSON-Objekt werden vom Logging-Agent als speziell erkannt und in die Struktur LogEntry extrahiert. Mit diesen speziellen JSON-Feldern können die folgenden Felder in LogEntry festgelegt werden:

  • severity
  • spanId
  • Vom Nutzer definiert: labels
  • httpRequest

Da JSON genauer und vielseitiger als Textzeilen ist, können Sie JSON-Objekte verwenden, um mehrzeilige Nachrichten zu schreiben und Metadaten hinzuzufügen.

In der folgenden Tabelle sind die Felder und ihre Werte im JSON-Format aufgeführt. Dort erfahren Sie, wie Sie strukturierte Logeinträge für Ihre Anwendungen mit dem vereinfachten Format erstellen:

JSON-Logfeld Feld LogEntry Cloud Logging-Agent-Funktion Beispielwert
severity severity Der Logging-Agent versucht, eine Übereinstimmung mit einer Vielzahl von allgemeinen Schweregrad-Strings zu finden. Dazu gehört die Liste der LogSeverity-Strings, die von der Logging API erkannt werden. "severity":"ERROR"
message textPayload (oder Teil von jsonPayload) Die Nachricht, die in der Logeintragszeile im Log-Explorer angezeigt wird. "message":"There was an error in the application."

Hinweis: message wird als textPayload gespeichert, wenn es das einzige verbleibende Feld ist, nachdem der Logging-Agent die anderen Spezialfelder entfernt hat und detect_json nicht aktiviert war. Andernfalls bleibt message in jsonPayload. Wenn der Logeintrag einen Ausnahme-Stacktrace enthält, sollte dieser im JSON-Logfeld message festgelegt werden, damit er geparst und in Error Reporting gespeichert werden kann.
log (nur Legacy-Google Kubernetes Engine) textPayload Gilt nur für Legacy-Google Kubernetes Engine: Wenn nach dem Entfernen von Spezialfeldern nur ein Logfeld übrig ist, wird dieses Log als textPayload gespeichert.
httpRequest httpRequest Ein strukturierter Datensatz im Format des Felds LogEntry HttpRequest. "httpRequest":{"requestMethod":"GET"}
zeitbezogene Felder time, timestamp usw. Weitere Informationen finden Sie unter Zeitbezogene Felder. "time":"2020-10-12T07:20:50.52Z"
logging.googleapis.com/insertId insertId Weitere Informationen finden Sie unter insertId auf der LogEntry-Seite. "logging.googleapis.com/insertId":"42"
logging.googleapis.com/labels labels Der Wert dieses Felds muss ein strukturierter Datensatz sein. Weitere Informationen finden Sie unter labels auf der LogEntry-Seite. "logging.googleapis.com/labels": {"user_label_1":"value_1","user_label_2":"value_2"}
logging.googleapis.com/operation operation Der Wert dieses Felds wird auch vom Log-Explorer verwendet, um zugehörige Logeinträge zu gruppieren. Weitere Informationen finden Sie auf der LogEntry-Seite unter operation. "logging.googleapis.com/operation": {"id":"get_data","producer":"github.com/MyProject/MyApplication", "first":"true"}
logging.googleapis.com/sourceLocation sourceLocation Speicherort des Quellcodes-Informationen zum Logeintrag, falls vorhanden. Weitere Informationen finden Sie unter LogEntrySourceLocation auf der LogEntry-Seite. "logging.googleapis.com/sourceLocation": {"file":"get_data.py","line":"142","function":"getData"}
logging.googleapis.com/spanId spanId Die Span-ID innerhalb des Trace, das mit dem Logeintrag verknüpft ist. Weitere Informationen finden Sie unter spanId auf der LogEntry-Seite. "logging.googleapis.com/spanId":"000000000000004a
logging.googleapis.com/trace trace Der Ressourcenname des mit dem Logeintrag verknüpften Trace, falls vorhanden. Weitere Informationen finden Sie unter trace auf der LogEntry-Seite. "logging.googleapis.com/trace":"projects/my-projectid/traces/0679686673a"

Hinweis: Wenn nicht in stdout oder stderr geschrieben wird, sollte der Wert dieses Felds als projects/[PROJECT-ID]/traces/[TRACE-ID] formatiert werden, damit er vom Log Explorer und vom Trace Viewer verwendet werden kann, um Logeinträge zu gruppieren und sie im Einklang mit Traces anzuzeigen. Wenn autoformat_stackdriver_trace auf [V] gesetzt ist und dem Format des ResourceTracetraceId-Feldes trace entspricht, hat das LogEntry-Feld den Wert projects/[PROJECT-ID]/traces/[V].
logging.googleapis.com/trace_sampled traceSampled Der Wert dieses Felds muss true oder false sein. Weitere Informationen finden Sie unter traceSampled auf der LogEntry-Seite. logging.googleapis.com/trace_sampled": false

Wenn Sie Logeinträge im vereinfachten Format erstellen möchten, erstellen Sie eine JSON-Darstellung des Eintrags mithilfe der Felder. Alle Felder sind optional.

Das folgende Beispiel zeigt einen vereinfachten JSON-Logeintrag:

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

Hier sehen Sie ein Beispiel für den resultierenden Logeintrag:

{
  "insertId": "42",
  "jsonPayload": {
    "message": "There was an error in the application",
    "times": "2019-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"
}

Logging-Agent-Konfiguration

Der Logging-Agent google-fluentd ist eine Cloud Logging-spezifische Paket für den Logdaten-Collector Fluentd. Der Logging-Agent verwendet die Fluentd-Standardkonfiguration und die Eingabe-Plugins von Fluentd, um Ereignislogs aus externen Quellen wie Dateien auf der Festplatte abzurufen oder um eingehende Logdatensätze zu analysieren.

Für Fluentd gibt es verschiedene unterstützte Parser, die Logs extrahieren und in strukturierte (JSON-)Nutzlasten konvertieren.

Wenn Sie eine Logquelle mit format [PARSER_NAME] konfigurieren, können Sie die integrierten Parser von Fluentd nutzen.

Die folgenden Codebeispiele zeigen die Fluentd-Konfiguration, den Eingangslogdatensatz und die Ausgabe der strukturierten Nutzlast, die Teil eines Cloud Logging-Logeintrags ist:

  • Fluentd-Konfiguration:

      <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>
    
  • Logdatensatz (Eingabe):

      <6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test
    
  • Strukturierte Nutzlast (Ausgabe):

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

Weitere Informationen zur Funktionsweise des Parsers syslog finden Sie in der ausführlichen Dokumentation zu Fluentd.

Standardmäßig aktivierte Standardparser

Die folgende Tabelle enthält die Standardparser, die im Agent enthalten sind, wenn Sie das strukturierte Logging aktivieren:

Parsername Konfigurationsdatei
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

Hinweise zum Aktivieren des strukturierten Loggings während der Installation des Logging-Agents finden Sie im Abschnitt Installation.

Installation

Zum Aktivieren des strukturierten Loggings müssen Sie die Standardkonfiguration des Logging-Agents bei der Installation oder Neuinstallation ändern. Wenn Sie das strukturierte Logging aktivieren, werden die zuvor aufgeführten Konfigurationsdateien ersetzt, der Vorgang selbst wird jedoch nicht geändert.

Wenn Sie das strukturierte Logging aktivieren, werden die aufgelisteten Logs in Logeinträge mit anderen Formaten als vor der Aktivierung strukturierter Logs umgewandelt. Wenn die Logs an Ziele außerhalb von Logging weitergeleitet werden, kann sich die Änderung auf Nachbearbeitungsanwendungen auswirken. Wenn beispielsweise Logs an BigQuery weitergeleitet werden, lehnt BigQuery die neuen Logeinträge für den Rest des Tages als falsches Schema ab.

Eine Anleitung zur Installation des Logging-Agents und zur Aktivierung des strukturierten Loggings erhalten Sie unter Agent installieren.

Die Konfigurationsdateien des Logging-Agents finden Sie unter /etc/google-fluentd/config.d/, wo nun auch der Standardparser standardmäßig aktiviert sein sollte.

Apache-Zugriffslogformat konfigurieren

Der Logging-Agent speichert Apache-Zugriffslogdaten standardmäßig im Feld jsonPayload. Beispiel:

{
  "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"
  },
  ...
}

Alternativ können Sie den Logging-Agent so konfigurieren, dass bestimmte Felder in das Feld httpRequest extrahiert werden. Beispiel:

{
  "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"
  },
  ...
}

Die Konfiguration des Felds httpRequest, wie im vorstehenden Beispiel dargestellt, unterstützt Tracing, das heißt, die Cloud Console zeigt alle Logs für eine bestimmte HTTP-Anfrage in einer Hierarchie an.

Fügen Sie zum Konfigurieren dieser Extraktion am Ende von /etc/google-fluentd/config.d/apache.conf Folgendes hinzu:

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

Weitere Informationen zum Konfigurieren von Logeinträgen finden Sie unter Logeinträge ändern.

Nginx-Zugriffslogformat konfigurieren

Der Logging-Agent speichert nginx-Zugriffslogdaten standardmäßig im Feld jsonPayload. Beispiel:

  {
    "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"
    },
    ...
  }

Alternativ können Sie den Logging-Agent so konfigurieren, dass bestimmte Felder in das Feld httpRequest extrahiert werden. Beispiel:

  {
    "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"
    },
    ...
  }

Die Konfiguration des Felds httpRequest, wie im vorstehenden Beispiel dargestellt, unterstützt Tracing, das heißt, die Cloud Console zeigt alle Logs für eine bestimmte HTTP-Anfrage in einer Hierarchie an.

Fügen Sie zum Konfigurieren dieser Extraktion am Ende von /etc/google-fluentd/config.d/nginx.conf Folgendes hinzu:

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

Weitere Informationen zum Konfigurieren von Logeinträgen finden Sie unter Logeinträge ändern.

Eigenen Parser schreiben

Wenn Ihre Logs von Standardparsern nicht unterstützt werden, können Sie eigene schreiben. Parser bestehen aus einem regulären Ausdruck, mit dem Logdatensätze abgeglichen und Labels auf die Teile angewendet werden.

Die folgenden Codebeispiele zeigen eine Logzeile im Logdatensatz, eine Konfiguration mit einem regulären Ausdruck, der das Format der Logzeile angibt, und den aufgenommenen Protokolleintrag:

  • Eine Logzeile im Logdatensatz:

    REPAIR CAR $500
    
  • Eine Konfiguration mit einem regulären Ausdruck, der das Format der Logzeile angibt:

    $ 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>
    
  • Der resultierende Protokolleintrag:

     {
      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:  "2018-03-21T01:47:11.475065313Z"
      resource: {
        labels: {
          instance_id:  "3914079432219560274"
          project_id:  "my-sample-project-12345"
          zone:  "us-central1-c"
        }
        type:  "gce_instance"
      }
      timestamp:  "2018-03-21T01:47:05.051902169Z"
     }
    

Strukturierte Logs ansehen

Informationen zum Durchsuchen von Logs und zum Aufrufen von Logeinträgen in der Cloud Console finden Sie unter Log-Explorer verwenden.

Informationen zum Lesen von Logeinträgen mit dem gcloud-Befehlszeilentool unter Logeinträge lesen

Informationen zum Lesen von Logeinträgen über die Logging API finden Sie unter der Methode entries.list.

Probleme beheben

Informationen zur Behebung häufig auftretender Probleme bei der Installation oder der Interaktion mit dem Logging-Agent finden Sie unter Fehlerbehebung beim Agent.