前回の続き。システムの一部をCloud RunからVPS化のために、
VPS上のログをGCPのCloud Loggingに送信したい。。
利用するプラグイン
利用するのはこのあたり。
アプリのログファイルをtailで読み込んで、
Cloud Logging(旧Stackdriver)に送信する。
ローカルでの環境
開発・確認用の環境は前回と同じ感じ。
myapp-io.confの[OUTPUT]部分を設定していく。
とりあえず、転送する
まずは、とりあえず、転送するだけの設定。
追加した[OUTPUT]以外は前回と同じ。
# myapp-io.conf
[INPUT]
name tail
path ./logs/application.log
read_from_head true
multiline.parser myapp-multiline
tag myapp.applog
[FILTER]
name parser
match *.applog
key_name log
parser myapp-multiline-capture
[OUTPUT]
name stdout
match *
[OUTPUT]
Name stackdriver
Match *
# myapp-parser.conf
[MULTILINE_PARSER]
name myapp-multiline
type regex
flush_timeout 1000
# rules | state name | regex pattern | next state
# ------|---------------|--------------------------------------------
rule "start_state" "/^\[.+\] [a-zA-Z]+ .+/" "cont"
rule "cont" "/^\s+.*/" "cont"
[PARSER]
name myapp-multiline-capture
format regex
regex /^\[(?<time>[^ ]+)\] (?<type>[a-zA-Z]+) +(?<message>.*)\n/m
time_key time
time_format %Y-%m-%dT%H:%M:%S.%L%z
google_service_credentialsを設定する必要があるけど、
デフォルトは環境変数(GOOGLE_APPLICATION_CREDENTIALS)で設定できる。
GCPのコンソール上でサービスアカウントを作成し、
「ログ書き込み」権限を付与した上で鍵(.json)をダウンロード。
あとは、環境変数にキーファイルのパスを指定すればOK
#!/bin/bash export GOOGLE_APPLICATION_CREDENTIALS="./credentials.json" fluent-bit -c myapp.conf
Cloud Logging上で確認すると、ログが転送されていることがわかる。

Cloud Loggingでのログの詳細はこんな感じ。
logNameには、Fluent側のTagが使われるっぽい。
{ "insertId": "c7yrtzfh5u8wj", "jsonPayload": { "message": "message info", "type": "INFO" }, "resource": { "type": "global", "labels": { "project_id": "myproject" } }, "timestamp": "2024-01-17T04:36:17.654Z", "logName": "projects/myproject/logs/myapp.applog", "receiveTimestamp": "2024-01-17T04:36:18.407382698Z" }
ログレベルを反映する
なにも指定していないので、ログレベル(severity)がない状態。
severity_keyを使えばOK。
FILTERのPARSERで?<type>と名前をつけているので、
[PARSER]
name myapp-multiline-capture
regex /^\[(?<time>[^ ]+)\] (?<type>[a-zA-Z]+) +(?<message>.*)\n/m
# ...略
OUTPUTでそのキー名を指定するかたち。
[OUTPUT]
Name stackdriver
Match *
severity_key type
設定した状態だとこんな感じ。

{ "insertId": "u7gxdeb6i2f", "jsonPayload": { "message": "message info" }, "resource": { "type": "global", "labels": { "project_id": "myproject" } }, "timestamp": "2024-01-17T06:54:36.083Z", "severity": "INFO", "logName": "projects/myproject/logs/myapp.applog", "receiveTimestamp": "2024-01-17T06:54:37.155753816Z" }
絞り込むための情報を追加する
Cloud Functionsでのログを見ると、こんな感じになっていて、
関数名などでも絞り込みできるようになっている。
{ "textPayload": "message cloud function log...", "insertId": "65a77280000b92741134b1ba", "resource": { "type": "cloud_function", "labels": { "function_name": "my_foo_function", "project_id": "myproject", "region": "asia-northeast1" } }, "timestamp": "2024-01-17T06:24:00.758388Z", "severity": "INFO", "labels": { "runtime_version": "nodejs20_20231231_20_10_0_RC00", "execution_id": "hv2mzdad42fl", "instance_id": "0087599d42325f0791879e25b0b66159bb57157855155a263ba635c671e1f8d42a60150e295dff896773521f08fa3a955ae28311f8733a1da8dea95f7bf859472008d2" }, "logName": "projects/myproject/logs/cloudfunctions.googleapis.com%2Fcloud-functions", "trace": "projects/myproject/traces/7abc1b0a9c4923b83d12c081cfa31d12", "receiveTimestamp": "2024-01-17T06:24:01.085097754Z" }
それぞれなにを設定すればよいかなどは、このあたりを参照。
- 構造化ロギング | Cloud Logging | Google Cloud
- LogEntry | Cloud Logging | Google Cloud
- MonitoredResource | Cloud Logging | Google Cloud
resourceはログエクスプローラで絞り込みするときに使うやつ。

設定の例
設定はこんな感じ。
[OUTPUT]
Name stackdriver
Match *
severity_key type
### resource関連の設定
# resourceのtype
resource generic_node
# location/region
location asia-northeast1
# 実行環境やclaster名などの識別子
namespace my_foo_func
# namespace内ノードのID。IPやhost名など
node_id my_vpn_host
### labelsの設定
# key=valueをカンマ区切りで指定
labels machine_name=my_vpn_host,app_version=1.0.0
設定した状態で実行するとこんな感じのログになる。
{ "insertId": "6xuk1uf767oyw", "jsonPayload": { "message": "message info" }, "resource": { "type": "generic_node", "labels": { "project_id": "myproject", "location": "asia-northeast1", "namespace": "my_foo_func", "node_id": "my_vpn_host" } }, "timestamp": "2024-01-17T07:20:58.129Z", "severity": "INFO", "labels": { "machine_name": "my_vpn_host", "app_version": "1.0.0" }, "logName": "projects/myproject/logs/myapp.applog", "receiveTimestamp": "2024-01-17T07:20:58.890547211Z" }
コンソールにも「汎用ノード」が増え、フィルタリングできる。
(未指定だったのは「グローバル」)

resource.labelsについて
resource.labelsで指定できるものは、
resource.typeごとに決まっているので、
それ以外はlabelsに追加するといい感じ。
resource.typeは、k8sやgceなど指定できるけど、
global、generic_node、generic_taskを使いそう。
それぞれの詳細は、以下に書かれている。
- global | Monitored resource types
project_idのみ
- generic_node | Monitored resource types
project_id、location、namespace、node_id
- generic_task | Monitored resource types
project_id、location、namespace、job、task_id
ラベルでの変数の利用
各ラベルの値は、
severity(type)のようにログ自体の値や、
環境変数の値を埋め組むことができる。
Fluent側のログがこんな感じの場合。
{ "keyA": "valA", "toplevel": { "keyB": "valB" } }
こんな感じで$<キー名>を指定すると、
ログ内のデータを使ってくれる。
[OUTPUT]
name stackdriver
match *
labels keyC=$keyA,keyD=$toplevel['keyB'],keyE=valC
細かい文法はこのあたり。
なので、アプリのバージョンなどで絞り込みたい場合には、
ログに含めておいて、labelsにわたす感じだとよい感じ。
以上!! やっとやりたいことができた気がするぞ。。(*´ω`*)