구조화된 로깅

이 문서에서는 구조화된 로깅의 개념과 로그 항목 페이로드 필드에 구조를 추가하는 방법을 설명합니다. 로그 페이로드가 JSON 객체로 형식이 지정되고 해당 객체가 jsonPayload 필드에 저장되면 이 로그 항목을 구조화된 로그라고 합니다. 이러한 로그의 경우 특정 JSON 경로를 검색하는 쿼리를 구성하고 로그 페이로드의 특정 필드에 색인을 생성할 수 있습니다. 반대로 로그 페이로드가 문자열 형식으로 지정되고 textPayload 필드에 저장되면 이 로그 항목은 구조화되지 않은 로그입니다. 텍스트 필드를 검색할 수는 있지만 콘텐츠의 색인은 생성할 수 없습니다.

구조화된 로그 항목을 만들려면 다음 중 하나를 수행합니다.

  • entries.write API 메서드를 호출하고 올바른 형식의 LogEntry를 제공합니다.
  • gcloud logging write 명령어를 사용합니다.
  • 구조화된 로그를 기록하는 Cloud Logging 클라이언트 라이브러리를 사용합니다.
  • BindPlane 서비스를 사용합니다.
  • 에이전트를 사용하여 로그를 작성합니다.

    • 일부 Google Cloud 서비스에는 stdout 또는 stderr에 작성된 데이터를 Cloud Logging에 로그로 전송하는 통합 로깅 에이전트가 포함되어 있습니다. Google Kubernetes Engine, App Engine 가변형 환경, Cloud Run 함수와 같은 Google Cloud 서비스에 이 접근 방식을 사용할 수 있습니다.

    • Compute Engine 가상 머신(VM)의 경우 운영 에이전트 또는 기존 Logging 에이전트를 설치 및 구성한 후 설치된 에이전트를 사용하여 Cloud Logging으로 로그를 전송할 수 있습니다.

이러한 방법에 대한 자세한 내용은 다음 섹션을 참조하세요.

클라이언트 라이브러리 또는 API를 사용하여 로그 작성

Cloud Logging API를 호출하는 Cloud Logging 클라이언트 라이브러리를 사용하거나 Cloud Logging API를 직접 호출하여 로그 데이터를 작성할 수 있습니다. 클라이언트 라이브러리는 일부 정보를 자동으로 캡처하고 필드를 적절하게 채우는 인터페이스를 제공하여 특수 JSON 필드 채우기를 단순화합니다. 하지만 페이로드 구조를 완전히 제어하려면 Cloud Logging API를 직접 호출하고 전체 LogEntry 구조를 Cloud Logging API에 전달합니다.

자세한 내용은 entries.write 참조 문서를 확인하세요.

코드 예시는 구조화된 로그 작성을 참조하세요.

gcloud CLI를 사용하여 로그 쓰기

gcloud CLI를 사용하여 로그 데이터를 작성할 수 있습니다. 이 인터페이스는 비정형 로그와 구조화된 로그를 지원합니다. 구조화된 로그를 작성하려면 명령어에 직렬화된 JSON 객체를 제공합니다.

빠른 시작은 Google Cloud CLI를 사용하여 로그 항목 쓰기 및 쿼리를 참고하세요.

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

BindPlane을 사용하여 로그 작성

BindPlane 서비스를 사용하여 로그를 Logging에 전송할 수 있습니다. 이러한 로그의 경우 페이로드는 JSON 형식이며 소스 시스템에 따라 구조화됩니다. BindPlane을 사용하여 수집된 로그를 찾고 보는 방법은 BindPlane 빠른 시작 가이드를 참조하세요.

에이전트를 사용하여 로그 작성

Compute Engine 인스턴스에서 로그를 가져오려면 운영 에이전트 또는 기존 Cloud Logging 에이전트를 사용하면 됩니다. 두 에이전트 모두 타사 애플리케이션에서 측정항목을 수집할 수 있으며, 둘 다 구조화된 로깅을 지원합니다.

  • 운영 에이전트는 Compute Engine 인스턴스에서 원격 분석을 수집하는 데 권장되는 에이전트입니다. 이 에이전트는 로깅과 측정항목을 단일 에이전트로 결합하여 YAML 기반 구성을 제공하고 대용량 로깅을 지원합니다.

    운영 에이전트를 구성하여 구조화된 로깅을 지원하거나 구조화된 로그의 형식을 맞춤설정하는 방법에 대한 자세한 내용은 운영 에이전트 구성을 참조하세요.

  • 기존 Cloud Logging 에이전트가 로그를 수집합니다. 이 에이전트는 다른 형태의 원격 분석을 수집하지 않습니다.

이 섹션의 나머지 부분은 기존 Logging 에이전트에 적용됩니다.

Logging 에이전트: 특수 JSON 필드

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로 남습니다. detect_json은 Google Kubernetes Engine과 같은 관리형 로깅 환경에 적용되지 않습니다. 로그 항목에 예외 스택 트레이스가 포함된 경우 예외 스택 트레이스는 이 message JSON 로그 필드에 설정되어야 하므로 예외 스택 트레이스를 파싱하고 Error Reporting에 저장할 수 있습니다.
log(기존 Google Kubernetes Engine만 해당) textPayload 기존 Google Kubernetes Engine에만 적용됩니다. 특수 목적 필드를 이동한 후 log 필드만 남으면 그 필드는 textPayload로 저장됩니다.
httpRequest httpRequest LogEntry HttpRequest 필드 형식의 구조화된 레코드 "httpRequest":{"requestMethod":"GET"}
시간 관련 필드 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": "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"
}

Logging 에이전트: 구성

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

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

format [PARSER_NAME]으로 로그 소스를 구성하여 Fluentd에서 기본 제공하는 파서를 기반으로 빌드할 수 있습니다. 기존 Logging 에이전트 구성에 관한 자세한 내용은 Logging 에이전트 구성을 참고하세요.

다음 코드 샘플에서는 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 문서를 참조하세요.

Logging 에이전트: 기본적으로 사용 설정되는 기본 파서

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

파서 이름 구성 파일
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 에이전트의 기본 구성을 변경해야 합니다. 구조화된 로깅을 사용 설정하면 이전에 나열된 구성 파일이 대체되지만 에이전트 자체의 작업은 변경되지 않습니다.

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

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

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

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 필드를 구성하면 추적에 도움이 됩니다. Google Cloud 콘솔에서 특정 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>

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

Logging 에이전트: 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 필드를 구성하면 추적에 도움이 됩니다. Google Cloud 콘솔에서 특정 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:  "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"
    }
    

문제해결

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

다음 단계