Open5

既存のログをopentelemetryで送りたい

mrasumrasu

jsonでローカルに出力されているログがある (/app/log/production.jsonに出力しているとする)

これをopentelemetryの形式で送りたい。

mrasumrasu

opentelemetry-collectorにある、File Log Exporterを使えば送れる。

たとえば、下のconfigを書けばいい。

receivers:
  filelog:
    include:
      - /app/log/production.json

exporters:
  debug:
    verbosity: detailed

service:
  pipelines:
    logs:
      receivers: [filelog]
      exporters: [debug]

実行例:

  1. dockerでotel-collector起動
    docker run -v $(pwd)/config.yaml:/etc/otelcol-contrib/config.yaml -v $(pwd)/log.json:/app/log/production.json  otel/opentelemetry-collector-contrib:latest`
    
  2. ログを書く
    echo '{"hello": "world", "span_id": "951b4fe22451495a", "trace_id": "951b4fe22451495aaa5a79e9ff254fa0"}' >> log.json
    
  3. dockerのstdoutを見ると、ログが取り込まれたことがわかる
    Resource SchemaURL: 
     ScopeLogs #0
     ScopeLogs SchemaURL: 
     InstrumentationScope  
     LogRecord #0
     ObservedTimestamp: 2025-09-06 11:30:18.032015596 +0000 UTC
     Timestamp: 1970-01-01 00:00:00 +0000 UTC
     SeverityText: 
     SeverityNumber: Unspecified(0)
     Body: Str({"hello": "world", "span_id": "951b4fe22451495a", "trace_id": "951b4fe22451495aaa5a79e9ff254fa0"})
     Attributes:
          -> log.file.name: Str(production.json)
     Trace ID: 
     Span ID: 
     Flags: 0
     	{"resource": {"service.instance.id": "cd0ef880-afbf-447a-b19d-1882d5485169", "service.name": "otelcol-contrib", "service.version": "0.134.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs"}
    
mrasumrasu

ただし、上の例だとTrace IDSpan IDが空白になっていて、trace_idが紐付いていないことがわかる。
紐付いていなくても検索はできるけれど、紐付いていたほうがotelに則っているから、都合が良いことがあるはず。

紐付けたい

mrasumrasu

operatorsを使って、ログの内容を変えればいい。

つまり、File Log Receiverをこう書けばいい。

receivers:
  filelog:
    include:
      - /app/log/production.json
    operators:
      - type: json_parser
        parse_from: body
        parse_to: attributes.tmp
      - type: trace_parser
        trace_id:
          parse_from: attributes.tmp.trace_id
        span_id:
          parse_from: attributes.tmp.span_id
      - type: remove
        field: attributes.tmp

すると、Trace IDSpan IDが増える。

...
SeverityNumber: Unspecified(0)
Body: Str({"hello": "world", "span_id": "951b4fe22451495a", "trace_id": "951b4fe22451495aaa5a79e9ff254fa0"})
Attributes:
     -> log.file.name: Str(production.json)
+ Trace ID: 951b4fe22451495aaa5a79e9ff254fa0
+ Span ID: 951b4fe22451495a
- Trace ID: 
- Span ID: 
Flags: 0
	{"resource": {"service.instance.id": "5fb1aac0-c76e-41b2-a569-b4e39824df7e", "service.name": "otelcol-contrib", "service.version": "0.134.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs"}

operatorsに書いた内容は、

  1. ファイルの内容(body)をパースする
    -  type: json_parser
       parse_from: body
       parse_to: attributes.tmp
    
  2. パース結果(attributes.tmp)からtrace_idとspan_idを抜き出す
    - type: trace_parser
       trace_id:
         parse_from: attributes.tmp.trace_id
       span_id:
         parse_from: attributes.tmp.span_id
    
  3. パース結果を削除する
    - type: remove
       field: attributes.tmp
    
mrasumrasu

紐付いた。
あとは出力先をstdoutから適当なバックエンドに変えればいい。
(attributes.tmpを経由しているのが気持ち悪いが、動いたからよしとする)

これで、既存のファイルをotelで出力できるようになった。

以上