Skip to content

processor_content_modifier: add new otel context for logs attrs#11500

Open
edsiper wants to merge 2 commits intomasterfrom
processor_content_modifier-otel-logs-attr
Open

processor_content_modifier: add new otel context for logs attrs#11500
edsiper wants to merge 2 commits intomasterfrom
processor_content_modifier-otel-logs-attr

Conversation

@edsiper
Copy link
Member

@edsiper edsiper commented Feb 25, 2026

Extend content_modifier processor with a new otel_log_attributes context which directly modify the log record attributes field. Config example:

service:
    log_level: info

  pipeline:
    inputs:
      - name: dummy
        dummy: '{"message":"hello","level":"info"}'
        tag: test.logs
        processors:
          logs:
            - name: opentelemetry_envelope          
            - name: content_modifier
              context: otel_log_attributes
              action: upsert
              key: service.name
              value: payments-api
            - name: content_modifier
              context: otel_log_attributes
              action: upsert
              key: deployment.environment
              value: prod
            - name: content_modifier
              context: otel_log_attributes
              action: delete
              key: old_attr

    outputs:
      - name: opentelemetry
        match: test.logs
        host: 127.0.0.1
        port: 4318
        tls: off

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.

Summary by CodeRabbit

  • New Features

    • Added support for OpenTelemetry log attributes, enabling extraction and processing of log event metadata within the content modifier.
  • Tests

    • Expanded test coverage with new test cases for OpenTelemetry log attributes operations including insert, upsert, delete, and validation of invalid metadata scenarios.

Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

This PR introduces support for OpenTelemetry log attributes in the content modifier processor. Changes include a new enum constant, configuration mapping for the "otel_log_attributes" context string, processing logic to extract and handle log attributes, a new public API function for attribute retrieval, and comprehensive test coverage for insert, upsert, delete, and invalid metadata scenarios.

Changes

Cohort / File(s) Summary
Core Enum Definition
plugins/processor_content_modifier/cm.h
Adds CM_CONTEXT_OTEL_LOG_ATTR enum value to the CM_CONTEXT_OTEL_* telemetry context enum following CM_CONTEXT_OTEL_SCOPE_ATTR.
Configuration Mapping
plugins/processor_content_modifier/cm_config.c
Maps the "otel_log_attributes" configuration string to the new CM_CONTEXT_OTEL_LOG_ATTR enum value in the set_context function for log event processing.
Processing Logic & API
plugins/processor_content_modifier/cm_logs.c, plugins/processor_content_modifier/cm_opentelemetry.h
Introduces helper function otel_get_log_attributes() to extract/initialize OTLP log attributes from record metadata; adds processing logic in cm_logs_process() to handle the new context; exposes public API function cm_otel_get_or_create_attributes().
Test Suite
tests/runtime/processor_content_modifier.c
Adds metadata verification helper cb_check_metadata_result() and four new test cases covering OTLP log attributes: insert, upsert, delete, and invalid metadata handling. Includes new includes and test infrastructure setup.

Sequence Diagram

sequenceDiagram
    participant Log as Log Event
    participant Proc as cm_logs_process()
    participant Helper as otel_get_log_attributes()
    participant OTEL as cm_otel_get_or_create_attributes()
    participant Meta as Metadata/KVList

    Log->>Proc: FLB_LOG_EVENT_NORMAL with CM_CONTEXT_OTEL_LOG_ATTR
    Proc->>Helper: Call otel_get_log_attributes(record)
    Helper->>Meta: Extract/read OTLP metadata from record
    Helper->>Meta: Create intermediate KVList if needed
    Meta->>OTEL: Pass KVList to cm_otel_get_or_create_attributes()
    OTEL->>Meta: Retrieve or initialize attributes structure
    Meta->>Helper: Return cfl_variant with attributes
    Helper->>Proc: Return attributes variant
    Proc->>Proc: Wrap in KVLIST variant for processing
    Proc->>Proc: Set as current object for modification
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A new path for attributes bright,
Log data dancing left and right,
OTEL's whispers we now hear,
Metadata transforms crystal clear,
Tests ensure our logic's right! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a new OpenTelemetry context for log attributes to the processor_content_modifier plugin.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch processor_content_modifier-otel-logs-attr

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…ntext

Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
Copy link

@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: 99803c57a5

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +324 to +326
if (ret != 0) {
cfl_kvlist_destroy(kvlist_tmp);
return NULL;

Choose a reason for hiding this comment

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

P2 Badge Avoid double-freeing otlp kvlist on insert failure

When cfl_kvlist_insert_kvlist_s fails here, it already destroys the wrapped variant (and therefore the kvlist_tmp payload) before returning non-zero (lib/cfl/src/cfl_kvlist.c calls cfl_variant_destroy in its error path). Calling cfl_kvlist_destroy(kvlist_tmp) again in this branch introduces a double free that can crash the process under allocation/insert failure conditions (e.g., memory pressure).

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
plugins/processor_content_modifier/cm_config.c (1)

196-203: Remove unreachable duplicate logs-context branches in this block.

The new "otel_log_attributes" mapping is correct, but the subsequent duplicate "otel_scope_name" (empty body) and second "otel_scope_version" branches are unreachable and make future edits error-prone.

♻️ Proposed cleanup
         else if (strcasecmp(ctx->context_str, "otel_log_attributes") == 0) {
             context = CM_CONTEXT_OTEL_LOG_ATTR;
         }
-        else if (strcasecmp(ctx->context_str, "otel_scope_name") == 0) {
-        }
-        else if (strcasecmp(ctx->context_str, "otel_scope_version") == 0) {
-            context = CM_CONTEXT_OTEL_SCOPE_VERSION;
-        }
         else {
             flb_plg_error(ctx->ins, "unknown logs context '%s'", ctx->context_str);
             return -1;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/processor_content_modifier/cm_config.c` around lines 196 - 203, The
context-parsing block contains redundant/unreachable branches: remove the empty
else-if for strcasecmp(ctx->context_str, "otel_scope_name") == 0 and delete the
duplicate else-if for "otel_scope_version", leaving the proper mapping for
"otel_log_attributes" (setting context = CM_CONTEXT_OTEL_LOG_ATTR) and a single,
correct mapping for "otel_scope_version" (CM_CONTEXT_OTEL_SCOPE_VERSION); also,
if a mapping for "otel_scope_name" is required, replace the empty branch with
setting the appropriate constant (e.g., CM_CONTEXT_OTEL_SCOPE_NAME) instead of
leaving it empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/runtime/processor_content_modifier.c`:
- Around line 89-151: cb_check_metadata_result leaks the incoming record buffer;
ensure you free it on all paths. Add a flb_free(record) (or the appropriate free
used in the test suite) before every early return and before the final return 0
so the buffer is released; locate the freeing calls around
flb_log_event_decoder_destroy(&decoder) and flb_sds_destroy(result) in
cb_check_metadata_result to insert the flb_free(record) calls so both success
and error paths free the record.

---

Nitpick comments:
In `@plugins/processor_content_modifier/cm_config.c`:
- Around line 196-203: The context-parsing block contains redundant/unreachable
branches: remove the empty else-if for strcasecmp(ctx->context_str,
"otel_scope_name") == 0 and delete the duplicate else-if for
"otel_scope_version", leaving the proper mapping for "otel_log_attributes"
(setting context = CM_CONTEXT_OTEL_LOG_ATTR) and a single, correct mapping for
"otel_scope_version" (CM_CONTEXT_OTEL_SCOPE_VERSION); also, if a mapping for
"otel_scope_name" is required, replace the empty branch with setting the
appropriate constant (e.g., CM_CONTEXT_OTEL_SCOPE_NAME) instead of leaving it
empty.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc966ed and ed52699.

📒 Files selected for processing (5)
  • plugins/processor_content_modifier/cm.h
  • plugins/processor_content_modifier/cm_config.c
  • plugins/processor_content_modifier/cm_logs.c
  • plugins/processor_content_modifier/cm_opentelemetry.h
  • tests/runtime/processor_content_modifier.c

Comment on lines +89 to +151
static int cb_check_metadata_result(void *record, size_t size, void *data)
{
int ret;
char *p;
flb_sds_t result;
size_t result_size = 1024;
struct expect_str *expected;
struct flb_log_event event;
struct flb_log_event_decoder decoder;

expected = (struct expect_str *) data;
result = NULL;

ret = flb_log_event_decoder_init(&decoder, (char *) record, size);
if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
return -1;
}

flb_log_event_decoder_read_groups(&decoder, FLB_TRUE);

ret = flb_log_event_decoder_next(&decoder, &event);
if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
flb_log_event_decoder_destroy(&decoder);
return -1;
}

result = flb_sds_create_size(result_size);
if (!TEST_CHECK(result != NULL)) {
flb_log_event_decoder_destroy(&decoder);
return -1;
}

ret = flb_msgpack_to_json(result, result_size, event.metadata, FLB_TRUE);
if (!TEST_CHECK(ret >= 0)) {
flb_sds_destroy(result);
flb_log_event_decoder_destroy(&decoder);
return -1;
}

while(expected != NULL && expected->str != NULL) {
if (expected->found == FLB_TRUE) {
p = strstr(result, expected->str);
if(!TEST_CHECK(p != NULL)) {
flb_error("Expected to find: '%s' in metadata '%s'",
expected->str, result);
}
}
else {
p = strstr(result, expected->str);
if(!TEST_CHECK(p == NULL)) {
flb_error("'%s' should be removed from metadata '%s'",
expected->str, result);
}
}

expected++;
}

flb_sds_destroy(result);
flb_log_event_decoder_destroy(&decoder);

return 0;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Free callback record buffer in cb_check_metadata_result.

record is not released on either success or error paths here, unlike cb_check_result, so this leaks memory during test runs.

🧹 Proposed fix
 static int cb_check_metadata_result(void *record, size_t size, void *data)
 {
-    int ret;
+    int ret;
+    int result = -1;
@@
     ret = flb_log_event_decoder_init(&decoder, (char *) record, size);
     if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
-        return -1;
+        goto cleanup_record;
     }
@@
     ret = flb_log_event_decoder_next(&decoder, &event);
     if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
         flb_log_event_decoder_destroy(&decoder);
-        return -1;
+        goto cleanup_record;
     }
@@
     result = flb_sds_create_size(result_size);
     if (!TEST_CHECK(result != NULL)) {
         flb_log_event_decoder_destroy(&decoder);
-        return -1;
+        goto cleanup_record;
     }
@@
     ret = flb_msgpack_to_json(result, result_size, event.metadata, FLB_TRUE);
     if (!TEST_CHECK(ret >= 0)) {
         flb_sds_destroy(result);
         flb_log_event_decoder_destroy(&decoder);
-        return -1;
+        goto cleanup_record;
     }
@@
     flb_sds_destroy(result);
     flb_log_event_decoder_destroy(&decoder);
+    result = 0;
 
-    return 0;
+cleanup_record:
+    flb_free(record);
+    return result;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/runtime/processor_content_modifier.c` around lines 89 - 151,
cb_check_metadata_result leaks the incoming record buffer; ensure you free it on
all paths. Add a flb_free(record) (or the appropriate free used in the test
suite) before every early return and before the final return 0 so the buffer is
released; locate the freeing calls around
flb_log_event_decoder_destroy(&decoder) and flb_sds_destroy(result) in
cb_check_metadata_result to insert the flb_free(record) calls so both success
and error paths free the record.

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