구조화된 로깅

이 문서에서는 구조화된 로깅의 개념과 로그 항목 페이로드 필드에 구조를 추가하는 방법을 설명합니다.

개요

Cloud Logging에서 구조화된 로그jsonPayload 필드를 사용하여 페이로드에 구조를 더하는 로그 항목을 말합니다. 구조화된 로깅은 사용자 작성 로그에 적용됩니다.

여러 가지 방법으로 구조화된 로그를 Logging에 작성할 수 있습니다.

  • Cloud Logging API를 사용하여 로그 항목 쓰기
  • gcloud 명령줄 도구를 사용하여 로그 항목 작성
  • BindPlane 서비스를 사용하여 로그 수집
  • 직렬화된 JSON 객체를 Logging 에이전트에 제공

이러한 각 방법은 다음 섹션에서 설명합니다.

Logging API 사용

Cloud Logging API를 사용하여 로그 항목을 작성하는 경우 jsonPayload가 포함된 전체 LogEntry 구조를 Cloud Logging API로 전송하여 페이로드의 구조를 제어할 수 있습니다.

자세한 내용은 entries.write 참조를 확인하세요. 코드 예시는 구조화된 로그 작성을 참조하세요.

gcloud 도구 사용

gcloud 도구를 사용하여 로그 항목을 작성하는 경우 jsonPayload가 있는 전체 LogEntry 구조를 Cloud Logging API로 전송하여 페이로드의 구조를 제어할 수 있습니다.

코드 예시는 gcloud logging write 참조를 확인하세요.

BindPlane 사용

BindPlane 서비스를 사용하여 로그를 수집하는 경우 페이로드는 JSON 형식이며 소스 시스템에 따라 구조화됩니다. BindPlane을 통해 수집된 로그를 찾고 보는 방법은 로그 데이터 찾기에 대한 BindPlane 문서를 참조하세요.

Logging 에이전트 사용

Cloud Logging 에이전트를 사용하여 로그 항목을 가져오는 경우 Logging 에이전트가 페이로드를 JSON 형식으로 변환하도록 지정할 수 있습니다.

Google Kubernetes Engine 또는 App Engine 가변형 환경을 사용하는 경우 한 줄에 직렬화된 JSON 객체로 stdout 또는 stderr에 구조화된 로그를 작성할 수 있습니다. 그러면 Logging 에이전트가 구조화된 로그를 LogEntry 구조의 jsonPayload로 Cloud Logging에 보냅니다.

JSON 객체의 일부 필드는 Logging 에이전트에서 특수 필드로 인식하여 LogEntry 구조로 추출됩니다. 이러한 특수 JSON 필드를 사용하여 LogEntry에 다음 필드를 설정할 수 있습니다.

  • severity
  • spanId
  • 사용자가 정의한 labels
  • httpRequest

JSON은 텍스트 줄보다 더 정확하고 다양한 용도로 사용되므로 JSON 객체를 사용하여 여러 줄 메시지를 작성하고 메타데이터를 추가할 수 있습니다.

단순화된 형식을 사용하여 애플리케이션의 구조화된 로그 항목을 만들려면 JSON으로 필드와 값이 나열된 다음 표를 참조하세요.

JSON 로그 필드 LogEntry 필드 Cloud Logging 에이전트 함수 예시 값
severity severity Logging 에이전트는 Logging API가 인식하는 LogSeverity 문자열의 목록을 포함한 다양한 일반적인 심각도 문자열을 일치시키려고 시도합니다. "severity":"ERROR"
message textPayload(또는 jsonPayload의 일부) 로그 탐색기의 로그 항목 줄에 표시되는 메시지입니다. "message":"There was an error in the application."

참고: message가 Logging 에이전트가 다른 특수 목적 필드를 제거하고 남은 유일한 필드이고 detect_json가 사용 설정되어 있지 않은 경우 textPayload으로 저장되며, 그렇지 않으면 messagejsonPayload로 남습니다. 로그 항목에 예외 스택 트레이스가 포함된 경우 예외 스택 트레이스는 이 message JSON 로그 필드에 설정되어야 하므로 예외 스택 트레이스를 파싱하고 Error Reporting에 저장할 수 있습니다.
log(기존 Google Kubernetes Engine만 해당) textPayload 기존 Google Kubernetes Engine에만 적용됩니다. 특수 목적 필드를 제거한 후 로그 필드만 남으면 로그는 textPayload로 저장됩니다.
httpRequest httpRequest LogEntry HttpRequest 필드 형식의 구조화된 레코드 "httpRequest":{"requestMethod":"GET"}
시간 관련 필드 time, timestamp, 등. 자세한 내용은 시간 관련 필드를 참조하세요. "time":"2020-10-12T07:20:50.52Z"
logging.googleapis.com/insertId insertId 자세한 내용은 LogEntry 페이지의 insertId를 참조하세요. "logging.googleapis.com/insertId":"42"
logging.googleapis.com/labels labels 이 필드의 값은 구조화된 레코드여야 합니다. 자세한 내용은 LogEntry 페이지의 labels를 참조하세요. "logging.googleapis.com/labels": {"user_label_1":"value_1","user_label_2":"value_2"}
logging.googleapis.com/operation operation 또한 이 필드의 값은 로그 탐색기에서 관련 로그 항목을 그룹화하는 데 사용됩니다. 자세한 내용은 LogEntry 페이지의 operation를 참조하세요. "logging.googleapis.com/operation": {"id":"get_data","producer":"github.com/MyProject/MyApplication", "first":"true"}
logging.googleapis.com/sourceLocation sourceLocation 로그 항목과 연결된 소스 코드 위치 정보(있는 경우) 자세한 내용은 LogEntry 페이지의 LogEntrySourceLocation를 참조하세요. "logging.googleapis.com/sourceLocation": {"file":"get_data.py","line":"142","function":"getData"}
logging.googleapis.com/spanId spanId 로그 항목과 연결된 trace 내의 스팬 ID입니다. 자세한 내용은 LogEntry 페이지의 spanId를 참조하세요. "logging.googleapis.com/spanId":"000000000000004a
logging.googleapis.com/trace trace 로그 항목과 연결된 trace의 리소스 이름입니다(있는 경우). 자세한 내용은 LogEntry 페이지의 trace를 참조하세요. "logging.googleapis.com/trace":"projects/my-projectid/traces/0679686673a"

참고: stdout 또는 stderr에 쓰지 않는 경우 이 필드의 값은 로그 탐색기와 Trace 뷰어가 로그 항목을 그룹화하고 trace와 나란히 표시하는 데 사용할 수 있도록 projects/[PROJECT-ID]/traces/[TRACE-ID] 형식으로 지정되어야 합니다. autoformat_stackdriver_trace가 true이고 [V]ResourceTrace traceId의 형식과 일치하는 경우 LogEntry trace 필드는 값을 가집니다. projects/[PROJECT-ID]/traces/[V].
logging.googleapis.com/trace_sampled traceSampled 이 필드의 값은 true 또는 false이어야 합니다. 자세한 내용은 LogEntry 페이지의 traceSampled를 참조하세요. logging.googleapis.com/trace_sampled": false

단순화된 형식의 로그 항목을 만들려면 필드를 사용하여 항목의 JSON 표현을 만듭니다. 모든 필드는 선택사항입니다.

다음은 단순화된 JSON 로그 항목의 예시입니다.

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

다음은 그 결과로 생성되는 로그 항목의 예시입니다.

{
  "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 에이전트 구성

Logging 에이전트 google-fluentdFluentd 로그 데이터 수집기의 Cloud Logging 관련 패키징입니다. Logging 에이전트는 기본 Fluentd 구성과 함께 제공되며, Fluentd 입력 플러그인을 사용하여 디스크상의 파일과 같은 외부 소스에서 이벤트 로그를 가져오거나 수신되는 로그 레코드를 파싱할 수 있습니다.

Fluentd에는 로그를 추출하여 구조화된(JSON) 페이로드로 변환해 주는 지원 파서 목록이 있습니다.

format [PARSER_NAME]으로 로그 소스를 구성하면 Fluentd에서 기본 제공하는 파서를 활용할 수 있습니다.

다음 코드 샘플에서는 Fluentd 구성, 로그 레코드 입력, Cloud Logging 로그 항목의 일부인 구조화된 페이로드 출력을 보여줍니다.

  • Fluentd 구성:

      <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>
    
  • 로그 레코드(입력):

      <6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test
    
  • 구조화된 페이로드(출력):

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

syslog 파서 작동 방식에 관한 자세한 내용은 Fluentd 문서를 참조하세요.

기본적으로 사용 설정되는 기본 파서

아래 표에는 구조화된 로깅을 사용 설정할 때 에이전트에 포함되는 기본 파서가 나와 있습니다.

파서 이름 구성 파일
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

Logging 에이전트를 설치할 때 구조화된 로깅을 사용 설정하는 방법에 관한 안내는 설치 섹션을 참조하세요.

설치

구조화된 로깅을 사용 설정하려면 설치 또는 재설치할 때 Logging 에이전트의 기본 구성을 변경해야 합니다. 구조화된 로깅을 사용 설정하면 이전에 나열된 구성 파일이 대체되지만 에이전트 자체의 작업은 변경되지 않습니다.

구조화된 로깅을 사용 설정하면 구조화된 로그를 사용 설정하기 전보다 다양한 형식을 가진 로그 항목으로 변환됩니다. 로그가 Logging 외부의 대상으로 라우팅되는 경우 변경사항이 후속 처리 애플리케이션에 영향을 미칠 수 있습니다. 예를 들어 BigQuery로 로그를 라우팅하는 경우 BigQuery는 남은 시간 동안 새로운 로그 항목을 잘못된 스키마가 포함된 것으로 인식해 거부합니다

Logging 에이전트 설치 및 구조화된 로깅 사용 설정에 대한 안내는 Logging 에이전트 설치를 참조하세요.

이제 /etc/google-fluentd/config.d/의 Logging 에이전트 구성 파일에 기본적으로 사용 설정된 표준 파서가 포함된 것을 확인할 수 있습니다.

Apache 액세스 로그 형식 구성

기본적으로 Logging 에이전트는 Apache 액세스 로그 데이터를 jsonPayload 필드에 저장합니다. 예를 들면 다음과 같습니다.

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

또는 특정 필드를 httpRequest 필드로 추출하도록 Logging 에이전트를 구성할 수도 있습니다. 예를 들면 다음과 같습니다.

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

위와 같이 httpRequest 필드를 구성하면 추적에 도움이 됩니다. Cloud Console에서 특정 HTTP 요청의 모든 로그를 상위-하위 계층 구조로 표현합니다.

추출을 구성하려면 /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>

로그 항목을 구성하는 방법에 대한 자세한 내용은 로그 레코드 수정을 참조하세요.

nginx 액세스 로그 형식 구성

기본적으로 Logging 에이전트는 nginx 액세스 로그 데이터를 jsonPayload 필드에 저장합니다. 예를 들면 다음과 같습니다.

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

또는 특정 필드를 httpRequest 필드로 추출하도록 Logging 에이전트를 구성할 수도 있습니다. 예를 들면 다음과 같습니다.

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

위와 같이 httpRequest 필드를 구성하면 추적에 도움이 됩니다. Cloud Console에서 특정 HTTP 요청의 모든 로그를 상위-하위 계층 구조로 표현합니다.

추출을 구성하려면 /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>

로그 항목을 구성하는 방법에 대한 자세한 내용은 로그 레코드 수정을 참조하세요.

자체 파서 제작

로그가 기본 파서에서 지원되지 않는다면 자체 파서를 제작할 수 있습니다. 파서는 로그 레코드를 일치시키고 조각에 라벨을 적용하는 데 사용되는 정규 표현식으로 구성됩니다.

다음 코드 예제에는 로그 레코드의 로그 줄, 로그 줄의 형식을 나타내는 정규 표현식이 포함된 구성, 내부 데이터화된 로그 항목이 나와 있습니다.

  • 로그 레코드의 로그 줄:

    REPAIR CAR $500
    
  • 로그 줄의 형식을 나타내는 정규식이 포함된 구성:

    $ 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>
    
  • 결과로 만들어진 로그 항목:

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

구조화된 로그 보기

Cloud Console을 사용하여 로그를 검색하고 로그 항목을 보려면 로그 탐색기 사용을 참조하세요.

gcloud 명령줄 도구를 사용하여 로그 항목을 읽거나 로그 항목 읽기를 참조하세요.

Logging API를 사용하여 로그 항목을 읽으려면 entries.list 메서드를 참조하세요.

문제 해결

Logging 에이전트 설치 또는 상호작용 시 일반적으로 나타나는 문제를 해결하려면 에이전트 문제 해결을 참조하세요.