Skip to content

out_opentelemetry: Add 'encoding' option for OTLP/HTTP JSON export#11844

Draft
meehanman wants to merge 1 commit into
fluent:masterfrom
meehanman:out_otel-json-export
Draft

out_opentelemetry: Add 'encoding' option for OTLP/HTTP JSON export#11844
meehanman wants to merge 1 commit into
fluent:masterfrom
meehanman:out_otel-json-export

Conversation

@meehanman
Copy link
Copy Markdown

The OTLP/HTTP specification (v1.10.0) states:

"OTLP/HTTP uses Protobuf payloads encoded either in binary format or in JSON format."

Prior to this change, the out_opentelemetry plugin only supported binary Protobuf (Content-Type: application/x-protobuf). This PR adds an encoding configuration option so users can select JSON encoding (Content-Type: application/json) when their OTLP/HTTP receiver requires or prefers it.

Changes

New encoding config option (out_opentelemetry)

Value Content-Type sent Behaviour
protobuf (default) application/x-protobuf Existing behaviour, unchanged
json application/json New — encodes via existing flb_opentelemetry_*_to_otlp_json() helpers

Per the OTLP spec, JSON encoding applies to OTLP/HTTP only. The encoding option is silently ignored when grpc on is set (gRPC always uses protobuf framing). Profiles have no JSON encoder in the current codebase; protobuf is used and a warning is emitted.

content_type parameter threaded through opentelemetry_post()
Previously the Content-Type was hardcoded to application/x-protobuf. The parameter is now passed explicitly at each call site, keeping existing callers unaffected.

Config map description fixes

  • grpc: removed misleading auto value (not valid for FLB_CONFIG_MAP_BOOL; accepted values are on/off only).
  • logs_body_key: was copy-pasted from logs_uri and incorrectly said "Specify an optional HTTP URI for the target OTel endpoint." Corrected to describe the record accessor pattern used to populate the OTLP log body.

$-prefixed body key lookup fix
Record accessor patterns (e.g. $message) include a $ prefix that does not appear in the raw msgpack key. A body_key_lookup_name() helper now strips the prefix before searching the record map, applied consistently in both the JSON and Protobuf encoders.

Spec references


Enter [N/A] in the box, if an item is not applicable to your change.

Testing
Before we can approve your change; please submit the following in a comment:

  • Example configuration file for the change
  • Debug log output from testing the change
  • Attached Valgrind output that shows no leaks or memory corruption was found
Example configuration (YAML)
pipeline:
  inputs:
    - name: dummy
      dummy: '{"message": "hello otel", "level": "info"}'
      tag: test
      rate: 1

  outputs:
    - name: opentelemetry
      match: "*"
      host: localhost
      port: 4318
      logs_uri: /v1/logs
      encoding: json
Example configuration (classic .conf)
[INPUT]
    name  dummy
    dummy {"message": "hello otel", "level": "info"}
    tag   test
    rate  1

[OUTPUT]
    name     opentelemetry
    match    *
    host     localhost
    port     4318
    logs_uri /v1/logs
    encoding json
Debug log output
Fluent Bit v5.0.7
* Copyright (C) 2015-2026 The Fluent Bit Authors
* Fluent Bit is a CNCF graduated project under the Fluent organization
* https://fluentbit.io

______ _                  _    ______ _ _           _____  _____ 
|  ___| |                | |   | ___ (_) |         |  ___||  _  |
| |_  | |_   _  ___ _ __ | |_  | |_/ /_| |_  __   _|___ \ | |/' |
|  _| | | | | |/ _ \ '_ \| __| | ___ \ | __| \ \ / /   \ \|  /| |
| |   | | |_| |  __/ | | | |_  | |_/ / | |_   \ V //\__/ /\ |_/ /
\_|   |_|\__,_|\___|_| |_|\__| \____/|_|\__|   \_/ \____(_)\___/


[2026/05/25 17:32:17.299] [ info] Configuration:
[2026/05/25 17:32:17.299] [ info]  flush time     | 1.000000 seconds
[2026/05/25 17:32:17.299] [ info]  grace          | 5 seconds
[2026/05/25 17:32:17.300] [ info]  daemon         | 0
[2026/05/25 17:32:17.300] [ info] ___________
[2026/05/25 17:32:17.300] [ info]  inputs:
[2026/05/25 17:32:17.300] [ info]      dummy
[2026/05/25 17:32:17.300] [ info] ___________
[2026/05/25 17:32:17.300] [ info]  filters:
[2026/05/25 17:32:17.300] [ info] ___________
[2026/05/25 17:32:17.300] [ info]  outputs:
[2026/05/25 17:32:17.300] [ info]      opentelemetry.0
[2026/05/25 17:32:17.301] [ info] ___________
[2026/05/25 17:32:17.301] [ info]  collectors:
[2026/05/25 17:32:17.301] [ info] [fluent bit] version=5.0.7, commit=, pid=12827
[2026/05/25 17:32:17.301] [debug] [engine] coroutine stack size: 196608 bytes (192.0K)
[2026/05/25 17:32:17.301] [ info] [storage] ver=1.5.4, type=memory, sync=normal, checksum=off, max_chunks_up=128
[2026/05/25 17:32:17.301] [ info] [simd    ] NEON
[2026/05/25 17:32:17.301] [ info] [cmetrics] version=2.1.4
[2026/05/25 17:32:17.301] [ info] [ctraces ] version=0.7.1
[2026/05/25 17:32:17.301] [ info] [input:dummy:dummy.0] initializing
[2026/05/25 17:32:17.301] [ info] [input:dummy:dummy.0] storage_strategy='memory' (memory only)
[2026/05/25 17:32:17.301] [debug] [dummy:dummy.0] created event channels: read=21 write=22
[2026/05/25 17:32:17.301] [debug] [opentelemetry:opentelemetry.0] created event channels: read=23 write=24
[2026/05/25 17:32:17.302] [ info] [sp] stream processor started
[2026/05/25 17:32:17.302] [ info] [engine] Shutdown Grace Period=5, Shutdown Input Grace Period=2
[2026/05/25 17:32:17.302] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:17.477] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:17.728] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:17.728] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:17.977] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:18.229] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:18.478] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:18.731] [trace] [task 0xffffa4036de0] created (id=0)
[2026/05/25 17:32:18.731] [debug] [task] created task=0xffffa4036de0 id=0 OK
[2026/05/25 17:32:18.732] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:18.732] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:18.733] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:18.734] [trace] [engine] resuming coroutine=0xffffa4036f70
[2026/05/25 17:32:18.734] [trace] [io] connection OK
[2026/05/25 17:32:18.734] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:18.734] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:18.734] [trace] [io coro=0xffffa4036f70] [net_write] trying 149 bytes
[2026/05/25 17:32:18.734] [trace] [io coro=0xffffa4036f70] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:18.734] [trace] [io coro=0xffffa4036f70] [net_write] ret=149 total=149/149
[2026/05/25 17:32:18.734] [trace] [io coro=0xffffa4036f70] [net_write] trying 156 bytes
[2026/05/25 17:32:18.734] [trace] [io coro=0xffffa4036f70] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:18.734] [trace] [io coro=0xffffa4036f70] [net_write] ret=156 total=156/156
[2026/05/25 17:32:18.734] [trace] [io coro=0xffffa4036f70] [net_read] try up to 4095 bytes
[2026/05/25 17:32:18.734] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:18.736] [trace] [engine] resuming coroutine=0xffffa4036f70
[2026/05/25 17:32:18.736] [trace] [io coro=0xffffa4036f70] [net_read] ret=127
[2026/05/25 17:32:18.736] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:18.736] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:18.736] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:18.736] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:18.736] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:18.736] [debug] [out flush] cb_destroy coro_id=0
[2026/05/25 17:32:18.736] [trace] [coro] destroy coroutine=0xffffa4036f70 data=0xffffa4036f88
[2026/05/25 17:32:18.736] [debug] [task] destroy task=0xffffa4036de0 (task_id=0)
[2026/05/25 17:32:18.736] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:18.979] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:19.229] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:19.480] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:19.731] [trace] [task 0xffffa403b620] created (id=0)
[2026/05/25 17:32:19.731] [debug] [task] created task=0xffffa403b620 id=0 OK
[2026/05/25 17:32:19.732] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:19.732] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:19.733] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:19.733] [trace] [engine] resuming coroutine=0xffffa40389e0
[2026/05/25 17:32:19.733] [trace] [io] connection OK
[2026/05/25 17:32:19.733] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:19.733] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:19.733] [trace] [io coro=0xffffa40389e0] [net_write] trying 149 bytes
[2026/05/25 17:32:19.733] [trace] [io coro=0xffffa40389e0] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:19.733] [trace] [io coro=0xffffa40389e0] [net_write] ret=149 total=149/149
[2026/05/25 17:32:19.733] [trace] [io coro=0xffffa40389e0] [net_write] trying 156 bytes
[2026/05/25 17:32:19.733] [trace] [io coro=0xffffa40389e0] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:19.733] [trace] [io coro=0xffffa40389e0] [net_write] ret=156 total=156/156
[2026/05/25 17:32:19.733] [trace] [io coro=0xffffa40389e0] [net_read] try up to 4095 bytes
[2026/05/25 17:32:19.733] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:19.736] [trace] [engine] resuming coroutine=0xffffa40389e0
[2026/05/25 17:32:19.736] [trace] [io coro=0xffffa40389e0] [net_read] ret=127
[2026/05/25 17:32:19.736] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:19.736] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:19.736] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:19.736] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:19.736] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:19.736] [debug] [out flush] cb_destroy coro_id=1
[2026/05/25 17:32:19.736] [trace] [coro] destroy coroutine=0xffffa40389e0 data=0xffffa40389f8
[2026/05/25 17:32:19.736] [debug] [task] destroy task=0xffffa403b620 (task_id=0)
[2026/05/25 17:32:19.736] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:19.977] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:20.230] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:20.481] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:20.727] [trace] [task 0xffffa40376d0] created (id=0)
[2026/05/25 17:32:20.727] [debug] [task] created task=0xffffa40376d0 id=0 OK
[2026/05/25 17:32:20.727] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:20.727] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:20.728] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:20.728] [trace] [engine] resuming coroutine=0xffffa40389c0
[2026/05/25 17:32:20.728] [trace] [io] connection OK
[2026/05/25 17:32:20.728] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:20.728] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:20.728] [trace] [io coro=0xffffa40389c0] [net_write] trying 149 bytes
[2026/05/25 17:32:20.728] [trace] [io coro=0xffffa40389c0] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:20.728] [trace] [io coro=0xffffa40389c0] [net_write] ret=149 total=149/149
[2026/05/25 17:32:20.728] [trace] [io coro=0xffffa40389c0] [net_write] trying 156 bytes
[2026/05/25 17:32:20.728] [trace] [io coro=0xffffa40389c0] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:20.728] [trace] [io coro=0xffffa40389c0] [net_write] ret=156 total=156/156
[2026/05/25 17:32:20.728] [trace] [io coro=0xffffa40389c0] [net_read] try up to 4095 bytes
[2026/05/25 17:32:20.728] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:20.731] [trace] [engine] resuming coroutine=0xffffa40389c0
[2026/05/25 17:32:20.731] [trace] [io coro=0xffffa40389c0] [net_read] ret=127
[2026/05/25 17:32:20.731] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:20.731] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:20.731] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:20.731] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:20.731] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:20.731] [debug] [out flush] cb_destroy coro_id=2
[2026/05/25 17:32:20.731] [trace] [coro] destroy coroutine=0xffffa40389c0 data=0xffffa40389d8
[2026/05/25 17:32:20.731] [debug] [task] destroy task=0xffffa40376d0 (task_id=0)
[2026/05/25 17:32:20.731] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:20.980] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:21.231] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:21.481] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:21.732] [trace] [task 0xffffa40388b0] created (id=0)
[2026/05/25 17:32:21.732] [debug] [task] created task=0xffffa40388b0 id=0 OK
[2026/05/25 17:32:21.732] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:21.732] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:21.733] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:21.733] [trace] [engine] resuming coroutine=0xffffa403c5a0
[2026/05/25 17:32:21.733] [trace] [io] connection OK
[2026/05/25 17:32:21.733] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:21.733] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:21.733] [trace] [io coro=0xffffa403c5a0] [net_write] trying 149 bytes
[2026/05/25 17:32:21.733] [trace] [io coro=0xffffa403c5a0] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:21.733] [trace] [io coro=0xffffa403c5a0] [net_write] ret=149 total=149/149
[2026/05/25 17:32:21.733] [trace] [io coro=0xffffa403c5a0] [net_write] trying 156 bytes
[2026/05/25 17:32:21.733] [trace] [io coro=0xffffa403c5a0] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:21.733] [trace] [io coro=0xffffa403c5a0] [net_write] ret=156 total=156/156
[2026/05/25 17:32:21.733] [trace] [io coro=0xffffa403c5a0] [net_read] try up to 4095 bytes
[2026/05/25 17:32:21.733] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:21.737] [trace] [engine] resuming coroutine=0xffffa403c5a0
[2026/05/25 17:32:21.737] [trace] [io coro=0xffffa403c5a0] [net_read] ret=127
[2026/05/25 17:32:21.737] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:21.737] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:21.737] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:21.737] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:21.737] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:21.737] [debug] [out flush] cb_destroy coro_id=3
[2026/05/25 17:32:21.737] [trace] [coro] destroy coroutine=0xffffa403c5a0 data=0xffffa403c5b8
[2026/05/25 17:32:21.737] [debug] [task] destroy task=0xffffa40388b0 (task_id=0)
[2026/05/25 17:32:21.737] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:21.976] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:22.229] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:22.479] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:22.731] [trace] [task 0xffffa4043920] created (id=0)
[2026/05/25 17:32:22.732] [debug] [task] created task=0xffffa4043920 id=0 OK
[2026/05/25 17:32:22.732] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:22.732] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:22.733] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:22.733] [trace] [engine] resuming coroutine=0xffffa40393b0
[2026/05/25 17:32:22.733] [trace] [io] connection OK
[2026/05/25 17:32:22.733] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:22.733] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:22.733] [trace] [io coro=0xffffa40393b0] [net_write] trying 149 bytes
[2026/05/25 17:32:22.733] [trace] [io coro=0xffffa40393b0] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:22.733] [trace] [io coro=0xffffa40393b0] [net_write] ret=149 total=149/149
[2026/05/25 17:32:22.733] [trace] [io coro=0xffffa40393b0] [net_write] trying 156 bytes
[2026/05/25 17:32:22.733] [trace] [io coro=0xffffa40393b0] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:22.733] [trace] [io coro=0xffffa40393b0] [net_write] ret=156 total=156/156
[2026/05/25 17:32:22.733] [trace] [io coro=0xffffa40393b0] [net_read] try up to 4095 bytes
[2026/05/25 17:32:22.733] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:22.736] [trace] [engine] resuming coroutine=0xffffa40393b0
[2026/05/25 17:32:22.736] [trace] [io coro=0xffffa40393b0] [net_read] ret=127
[2026/05/25 17:32:22.736] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:22.736] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:22.736] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:22.736] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:22.736] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:22.736] [debug] [out flush] cb_destroy coro_id=4
[2026/05/25 17:32:22.736] [trace] [coro] destroy coroutine=0xffffa40393b0 data=0xffffa40393c8
[2026/05/25 17:32:22.736] [debug] [task] destroy task=0xffffa4043920 (task_id=0)
[2026/05/25 17:32:22.736] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:22.980] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:23.227] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:23.476] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:23.727] [trace] [task 0xffffa40435f0] created (id=0)
[2026/05/25 17:32:23.727] [debug] [task] created task=0xffffa40435f0 id=0 OK
[2026/05/25 17:32:23.727] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:23.727] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:23.728] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:23.728] [trace] [engine] resuming coroutine=0xffffa403be10
[2026/05/25 17:32:23.728] [trace] [io] connection OK
[2026/05/25 17:32:23.728] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:23.728] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:23.728] [trace] [io coro=0xffffa403be10] [net_write] trying 149 bytes
[2026/05/25 17:32:23.728] [trace] [io coro=0xffffa403be10] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:23.728] [trace] [io coro=0xffffa403be10] [net_write] ret=149 total=149/149
[2026/05/25 17:32:23.728] [trace] [io coro=0xffffa403be10] [net_write] trying 156 bytes
[2026/05/25 17:32:23.728] [trace] [io coro=0xffffa403be10] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:23.728] [trace] [io coro=0xffffa403be10] [net_write] ret=156 total=156/156
[2026/05/25 17:32:23.728] [trace] [io coro=0xffffa403be10] [net_read] try up to 4095 bytes
[2026/05/25 17:32:23.728] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:23.732] [trace] [engine] resuming coroutine=0xffffa403be10
[2026/05/25 17:32:23.732] [trace] [io coro=0xffffa403be10] [net_read] ret=127
[2026/05/25 17:32:23.732] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:23.732] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:23.732] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:23.732] [debug] [out flush] cb_destroy coro_id=5
[2026/05/25 17:32:23.732] [trace] [coro] destroy coroutine=0xffffa403be10 data=0xffffa403be28
[2026/05/25 17:32:23.732] [debug] [task] destroy task=0xffffa40435f0 (task_id=0)
[2026/05/25 17:32:23.732] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:23.732] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:23.732] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:23.977] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:24.228] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:24.479] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:24.730] [trace] [task 0xffffa4039c80] created (id=0)
[2026/05/25 17:32:24.730] [debug] [task] created task=0xffffa4039c80 id=0 OK
[2026/05/25 17:32:24.730] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:24.730] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:24.731] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:24.731] [trace] [engine] resuming coroutine=0xffffa40389c0
[2026/05/25 17:32:24.731] [trace] [io] connection OK
[2026/05/25 17:32:24.731] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:24.731] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:24.731] [trace] [io coro=0xffffa40389c0] [net_write] trying 149 bytes
[2026/05/25 17:32:24.731] [trace] [io coro=0xffffa40389c0] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:24.731] [trace] [io coro=0xffffa40389c0] [net_write] ret=149 total=149/149
[2026/05/25 17:32:24.731] [trace] [io coro=0xffffa40389c0] [net_write] trying 156 bytes
[2026/05/25 17:32:24.731] [trace] [io coro=0xffffa40389c0] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:24.731] [trace] [io coro=0xffffa40389c0] [net_write] ret=156 total=156/156
[2026/05/25 17:32:24.731] [trace] [io coro=0xffffa40389c0] [net_read] try up to 4095 bytes
[2026/05/25 17:32:24.731] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:24.734] [trace] [engine] resuming coroutine=0xffffa40389c0
[2026/05/25 17:32:24.734] [trace] [io coro=0xffffa40389c0] [net_read] ret=127
[2026/05/25 17:32:24.734] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:24.734] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:24.734] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:24.734] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:24.734] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:24.734] [debug] [out flush] cb_destroy coro_id=6
[2026/05/25 17:32:24.734] [trace] [coro] destroy coroutine=0xffffa40389c0 data=0xffffa40389d8
[2026/05/25 17:32:24.734] [debug] [task] destroy task=0xffffa4039c80 (task_id=0)
[2026/05/25 17:32:24.734] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:24.977] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:25.227] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:25.481] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:25.730] [trace] [task 0xffffa4113f80] created (id=0)
[2026/05/25 17:32:25.730] [debug] [task] created task=0xffffa4113f80 id=0 OK
[2026/05/25 17:32:25.731] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:25.731] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:25.732] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:25.732] [trace] [engine] resuming coroutine=0xffffa403b540
[2026/05/25 17:32:25.732] [trace] [io] connection OK
[2026/05/25 17:32:25.732] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:25.732] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:25.732] [trace] [io coro=0xffffa403b540] [net_write] trying 149 bytes
[2026/05/25 17:32:25.732] [trace] [io coro=0xffffa403b540] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:25.732] [trace] [io coro=0xffffa403b540] [net_write] ret=149 total=149/149
[2026/05/25 17:32:25.732] [trace] [io coro=0xffffa403b540] [net_write] trying 156 bytes
[2026/05/25 17:32:25.732] [trace] [io coro=0xffffa403b540] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:25.732] [trace] [io coro=0xffffa403b540] [net_write] ret=156 total=156/156
[2026/05/25 17:32:25.732] [trace] [io coro=0xffffa403b540] [net_read] try up to 4095 bytes
[2026/05/25 17:32:25.732] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:25.735] [trace] [engine] resuming coroutine=0xffffa403b540
[2026/05/25 17:32:25.735] [trace] [io coro=0xffffa403b540] [net_read] ret=125
[2026/05/25 17:32:25.735] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:25.735] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:25.735] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:25.735] [debug] [out flush] cb_destroy coro_id=7
[2026/05/25 17:32:25.735] [trace] [coro] destroy coroutine=0xffffa403b540 data=0xffffa403b558
[2026/05/25 17:32:25.735] [debug] [task] destroy task=0xffffa4113f80 (task_id=0)
[2026/05/25 17:32:25.735] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:25.735] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:25.735] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:25.735] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:25.979] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:26.229] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:26.479] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:26.730] [trace] [task 0xffffa40431b0] created (id=0)
[2026/05/25 17:32:26.730] [debug] [task] created task=0xffffa40431b0 id=0 OK
[2026/05/25 17:32:26.731] [trace] [input chunk] update output instances with new chunk size diff=49, records=1, input=dummy.0
[2026/05/25 17:32:26.731] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:26.732] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:26.732] [trace] [engine] resuming coroutine=0xffffa4043c10
[2026/05/25 17:32:26.732] [trace] [io] connection OK
[2026/05/25 17:32:26.732] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:26.732] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:26.732] [trace] [io coro=0xffffa4043c10] [net_write] trying 149 bytes
[2026/05/25 17:32:26.732] [trace] [io coro=0xffffa4043c10] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:26.732] [trace] [io coro=0xffffa4043c10] [net_write] ret=149 total=149/149
[2026/05/25 17:32:26.732] [trace] [io coro=0xffffa4043c10] [net_write] trying 156 bytes
[2026/05/25 17:32:26.732] [trace] [io coro=0xffffa4043c10] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:26.732] [trace] [io coro=0xffffa4043c10] [net_write] ret=156 total=156/156
[2026/05/25 17:32:26.732] [trace] [io coro=0xffffa4043c10] [net_read] try up to 4095 bytes
[2026/05/25 17:32:26.732] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:26.735] [trace] [engine] resuming coroutine=0xffffa4043c10
[2026/05/25 17:32:26.736] [trace] [io coro=0xffffa4043c10] [net_read] ret=127
[2026/05/25 17:32:26.736] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:26.736] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:26.736] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:26.736] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:26.736] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:26.736] [debug] [out flush] cb_destroy coro_id=8
[2026/05/25 17:32:26.736] [trace] [coro] destroy coroutine=0xffffa4043c10 data=0xffffa4043c28
[2026/05/25 17:32:26.736] [debug] [task] destroy task=0xffffa40431b0 (task_id=0)
[2026/05/25 17:32:26.736] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:26.979] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:27.227] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:27] [engine] caught signal (SIGCONT)
[2026/05/25 17:32:27] Fluent Bit Dump

===== Input =====
dummy.0 (dummy)
│
├─ status
│  └─ overlimit     : no
│     ├─ mem size   : 49b (49 bytes)
│     └─ mem limit  : 0b (0 bytes)
│
├─ tasks
│  ├─ total tasks   : 0
│  ├─ new           : 0
│  ├─ running       : 0
│  └─ size          : 0b (0 bytes)
│
└─ chunks
   └─ total chunks  : 1
      ├─ up chunks  : 1
      ├─ down chunks: 0
      └─ busy chunks: 0
         ├─ size    : 0b (0 bytes)
         └─ size err: 0


===== Storage Layer =====
total chunks     : 1
├─ mem chunks    : 1
└─ fs chunks     : 0
   ├─ up         : 0
   └─ down       : 0
[2026/05/25 17:32:27] [engine] caught signal (SIGTERM)
[2026/05/25 17:32:27.298] [trace] [engine] flush enqueued data
[2026/05/25 17:32:27.298] [trace] [task 0xffffa40a3d90] created (id=0)
[2026/05/25 17:32:27.298] [debug] [task] created task=0xffffa40a3d90 id=0 OK
[2026/05/25 17:32:27.298] [ warn] [engine] service will shutdown in max 5 seconds
[2026/05/25 17:32:27.298] [debug] [engine] task 0 already scheduled to run, not re-scheduling it.
[2026/05/25 17:32:27.298] [ info] [engine] pausing all inputs..
[2026/05/25 17:32:27.298] [ info] [input] pausing dummy.0
[2026/05/25 17:32:27.298] [trace] [upstream] get new connection for localhost:4318, net setup:
net.connect_timeout        = 10 seconds
net.source_address         = any
net.keepalive              = enabled
net.keepalive_idle_timeout = 30 seconds
net.max_worker_connections = 0
[2026/05/25 17:32:27.299] [trace] [net] connection #31 in process to localhost:4318
[2026/05/25 17:32:27.299] [trace] [engine] resuming coroutine=0xffffa4043870
[2026/05/25 17:32:27.299] [trace] [io] connection OK
[2026/05/25 17:32:27.299] [debug] [upstream] KA connection #31 to localhost:4318 is connected
[2026/05/25 17:32:27.299] [debug] [http_client] not using http_proxy for header
[2026/05/25 17:32:27.299] [trace] [io coro=0xffffa4043870] [net_write] trying 149 bytes
[2026/05/25 17:32:27.299] [trace] [io coro=0xffffa4043870] [fd 31] write_async(2)=149 (149/149)
[2026/05/25 17:32:27.299] [trace] [io coro=0xffffa4043870] [net_write] ret=149 total=149/149
[2026/05/25 17:32:27.299] [trace] [io coro=0xffffa4043870] [net_write] trying 156 bytes
[2026/05/25 17:32:27.299] [trace] [io coro=0xffffa4043870] [fd 31] write_async(2)=156 (156/156)
[2026/05/25 17:32:27.299] [trace] [io coro=0xffffa4043870] [net_write] ret=156 total=156/156
[2026/05/25 17:32:27.299] [trace] [io coro=0xffffa4043870] [net_read] try up to 4095 bytes
[2026/05/25 17:32:27.299] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:27.302] [trace] [engine] resuming coroutine=0xffffa4043870
[2026/05/25 17:32:27.302] [trace] [io coro=0xffffa4043870] [net_read] ret=127
[2026/05/25 17:32:27.302] [ info] [output:opentelemetry:opentelemetry.0] localhost:4318, HTTP status=200
[2026/05/25 17:32:27.302] [debug] [upstream] KA connection #31 to localhost:4318 is now available
[2026/05/25 17:32:27.302] [debug] [upstream] KA connection #31 to localhost:4318 has been disconnected by the remote service
[2026/05/25 17:32:27.302] [trace] [upstream] destroy connection #31 to localhost:4318
[2026/05/25 17:32:27.302] [trace] �[93m[engine] [task event]�[0m task_id=0 out_id=0 return=OK
[2026/05/25 17:32:27.302] [debug] [out flush] cb_destroy coro_id=9
[2026/05/25 17:32:27.302] [trace] [coro] destroy coroutine=0xffffa4043870 data=0xffffa4043888
[2026/05/25 17:32:27.302] [debug] [task] destroy task=0xffffa40a3d90 (task_id=0)
[2026/05/25 17:32:27.302] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:27.480] [trace] [sched] 0 timer coroutines destroyed
[2026/05/25 17:32:27.730] [ info] [engine] service has stopped (0 pending tasks)
[2026/05/25 17:32:27.730] [ info] [input] pausing dummy.0
Valgrind output
==12828== Memcheck, a memory error detector
==12828== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12828== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==12828== Command: /build/bin/flb-it-opentelemetry
==12828== Parent PID: 1
==12828== 
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== Warning: invalid file descriptor -1 in syscall close()
==12828== 
==12828== HEAP SUMMARY:
==12828==     in use at exit: 0 bytes in 0 blocks
==12828==   total heap usage: 28,602 allocs, 28,602 frees, 18,362,453 bytes allocated
==12828== 
==12828== All heap blocks were freed -- no leaks are possible
==12828== 
==12828== For lists of detected and suppressed errors, rerun with: -s
==12828== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Documentation

  • Documentation required for this feature

fluent/fluent-bit-docs#2587

Backporting

  • Backport to latest stable release.

Fluent Bit is licensed under Apache 2.0, by submitting this pull request I understand that this code will be released under the terms of that license.

@meehanman meehanman marked this pull request as ready for review May 26, 2026 00:40
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2bd72a7804

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +554 to +556
if (strcasecmp(tmp, "json") == 0) {
ctx->use_json_encoding = FLB_TRUE;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject JSON encoding when gRPC transport is enabled

Allowing encoding=json here breaks OTLP/gRPC exports: ctx->use_json_encoding drives the logs/metrics/traces JSON paths, but opentelemetry_post() still wraps that payload in gRPC framing (application/grpc) when grpc on is active. OTLP/gRPC expects protobuf messages, so this combination sends invalid wire payloads and causes export failures for gRPC users who enable the new option.

Useful? React with 👍 / 👎.

Comment on lines +780 to +782
if (ctx->use_json_encoding) {
return process_metrics_json(event_chunk, ctx);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve add_label behavior for JSON metric encoding

This early return bypasses the existing metrics path that calls append_labels(ctx, cmt) before encoding, so configured add_label entries are silently dropped whenever encoding=json is used. That changes exported metric content compared to protobuf mode and regresses a documented plugin option.

Useful? React with 👍 / 👎.

Comment on lines +1211 to +1213
if (ctx->use_json_encoding) {
return otel_process_logs_json(event_chunk, ctx);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply logs_max_resources and logs_max_scopes in JSON mode

Returning to otel_process_logs_json() here skips the protobuf log-building path that enforces logs_max_resources and logs_max_scopes, so those limits are ignored under encoding=json. This can produce much larger-than-configured payloads and defeats users' resource/scope cap settings.

Useful? React with 👍 / 👎.

@meehanman
Copy link
Copy Markdown
Author

Moving to draft to test changes

…encoder

Introduces a new `encoding` config property for the out_opentelemetry
plugin that accepts `protobuf` (default, existing behaviour) or `json`
(new OTLP/HTTP JSON).  gRPC + JSON is rejected at context-creation time
with a clear error per the OTLP specification.

New config property
-------------------
  encoding   protobuf (default) | json
  When json, all log and metrics flushes use OTLP/HTTP JSON encoding.
  Profiles always use protobuf (no JSON encoder exists for cprofiles).

Streaming log encoder (flb_opentelemetry_logs_to_otlp_json)
------------------------------------------------------------
Replace the previous AST-based encoder that called flb_json_mut_obj/
arr/str for every node (~80 malloc() calls per log record) with a
single-pass streaming encoder that writes directly into a growing
flb_sds_t output buffer.  Allocation cost per flush drops from ~80×N
heap opeations to ~4 geometric SDS reallocs regardless of N.

Key properties:
- Produces byte-identical output to the AST path for all unit tests
- Handles all option flags: logs_body_key (singular + plural),
  require_otel_metadata, body_key_attributes, max_resources, max_scopes
- Enforces max_scopes per resource via a stack-allocated
  (resource_hash → scope_count) tracker — no heap allocation
- _pretty variant retains the AST path (debug tool, not hot path)

Metrics encoder
---------------
- Eliminate the intermediate msgpack re-encode step in
  process_metrics_json(); call flb_opentelemetry_metrics_to_otlp_json()
  directly after label injection, cutting encoding from 3 msgpack
  passes to 1
- Replace per-GROUP_START msgpack_sbuffer malloc+serialize for resource/
  scope identity hashing with a zero-allocation XXH3 streaming hash
  (cfl_hash_64bits_reset/update/digest)
- Check flb_sds_cat_safe() return value and propagate FLB_RETRY on
  allocation failure instead of continuing with a corrupt buffer
- Remove_encode_msgpack.h include (no longer needed)

Bug fixes
---------
- Pass max_resources/max_scopes into ensure_default_logs_scope_state()
  so the default-scope path (require_otel_metadata=false) respects caps
- Return 1 (skip record) instead of -1 (fatal) when a resource/scope
  limit is hit; treat it as a dropped record, not a pipeline error
- Set skip_current_group=FLB_TRUE when append_logs_resource_state()
  fails after the resource-limit check
- Fix sign-comparison in resource/scope limit guards: compare counts as
  size_t rather than casting to int
- Move the "profiles JSON encoding is not supported" warning from
  process_profiles() (per-flush) to context creation (once at startup)
- Return FLB_ERROR (not FLB_RETRY) when body_keys calloc fails in
  otel_process_logs_json() to avoid an infinite OOM retry loop

Tests (tests/internal/opentelemetry.c)
---------------------------------------
Add 7 new unit tests covering the JSON encoding path:
- test_opentelemetry_logs_otlp_json_body_keys_array
- test_opentelemetry_logs_otlp_json_body_key_attributes
- test_opentelemetry_metrics_otlp_json_add_label
- test_opentelemetry_logs_otlp_json_max_resources_cap
- test_opentelemetry_logs_otlp_json_max_scopes_cap
- test_opentelemetry_logs_otlp_json_body_key_dollar_prefix
- test_opentelemetry_metrics_otlp_json_protobuf_default

Verified: 26/26 unit tests pass, Valgrind 0 errors, integration test
produces valid OTLP/HTTP JSON accepted by mock receiver.

Signed-off-by: Dean Meehan <d3an.meehan@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant