Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/fluent-bit/multiline/flb_ml.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ struct flb_ml_parser_ins {

/* Link to struct flb_ml_group->parsers */
struct mk_list _head;

/* drop a flush whose key_content is an empty string (off) */
int drop_empty_content;
};

struct flb_ml_group {
Expand Down
2 changes: 2 additions & 0 deletions plugins/in_tail/tail_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ static int multiline_load_parsers(struct flb_tail_config *ctx)
if (!parser_i) {
return -1;
}

parser_i->drop_empty_content = ctx->skip_empty_lines;
}
}

Expand Down
14 changes: 12 additions & 2 deletions src/multiline/flb_ml.c
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,7 @@ int flb_ml_flush_stream_group(struct flb_ml_parser *ml_parser,
int ret;
int size;
int len;
int key_id;
size_t off = 0;
msgpack_object map;
msgpack_object k;
Expand Down Expand Up @@ -1682,8 +1683,17 @@ int flb_ml_flush_stream_group(struct flb_ml_parser *ml_parser,
}
}
else {
/* The buffer is empty, so just pack the original map from the context */
msgpack_pack_object(&mp_pck, map);
key_id = -1;
if (parser_i->drop_empty_content) {
key_id = get_key_id(&map, parser_i->key_content);
}
Comment on lines +1687 to +1689

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the required control-block brace style.

Move each control-block opening brace to the next line.

  • src/multiline/flb_ml.c#L1687-L1689: move the if opening brace to the next line.
  • tests/internal/multiline.c#L650-L655: move the for opening brace to the next line.
  • tests/internal/multiline.c#L659-L661: move the if opening brace to the next line.
  • tests/internal/multiline.c#L697-L702: move the for opening brace to the next line.
  • tests/internal/multiline.c#L706-L708: move the if opening brace to the next line.
  • tests/internal/multiline.c#L744-L749: move the for opening brace to the next line.
  • tests/internal/multiline.c#L753-L755: move the if opening brace to the next line.

As per coding guidelines, C control blocks must place the opening brace on the next line.

📍 Affects 2 files
  • src/multiline/flb_ml.c#L1687-L1689 (this comment)
  • tests/internal/multiline.c#L650-L655
  • tests/internal/multiline.c#L659-L661
  • tests/internal/multiline.c#L697-L702
  • tests/internal/multiline.c#L706-L708
  • tests/internal/multiline.c#L744-L749
  • tests/internal/multiline.c#L753-L755
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/multiline/flb_ml.c` around lines 1687 - 1689, Apply the project’s C brace
style by moving each control-block opening brace onto its own following line:
the if block in src/multiline/flb_ml.c (1687-1689), and the for/if blocks in
tests/internal/multiline.c (650-655, 659-661, 697-702, 706-708, 744-749, and
753-755). Preserve all existing control-flow behavior.

Source: Coding guidelines


if (key_id == -1 ||
map.via.map.ptr[key_id].val.type != MSGPACK_OBJECT_STR ||
map.via.map.ptr[key_id].val.via.str.size > 0) {
/* The buffer is empty, so just pack the original map from the context */
msgpack_pack_object(&mp_pck, map);
}
}

msgpack_unpacked_destroy(&result);
Expand Down
178 changes: 178 additions & 0 deletions tests/internal/multiline.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,37 @@ struct record_check cri_output[] = {
{"4b. non multiline 2"}
};

/* CRI, empty payload dropped via drop_empty_content */
struct record_check issue_6703_input[] = {
{"2025-01-01T00:00:00.000000000Z stdout F hello world"},
{"2025-01-01T00:00:00.000000001Z stdout F "},
{"2025-01-01T00:00:00.000000002Z stdout F goodbye world"}
};

struct record_check issue_6703_output[] = {
{"hello world"},
{"goodbye world"}
};

/* Same input, drop_empty_content left off (default): behavior is unchanged */
struct record_check issue_6703_disabled_output[] = {
{"hello world"},
{""},
{"goodbye world"}
};

/* CRI, every line in isolation is an empty payload */
struct record_check issue_6703_all_empty_input[] = {
{"2025-01-01T00:00:00.000000000Z stdout F "},
{"2025-01-01T00:00:00.000000001Z stdout F "},
{"2025-01-01T00:00:00.000000002Z stdout F "}
};

/* Sink for issue_6703_all_empty: only read if the fix regresses */
struct record_check issue_6703_all_empty_output[] = {
{""}, {""}, {""}
};
Comment on lines +85 to +114

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use four-space indentation in the new fixtures.

Lines 87-113 use two-space indentation for initializer entries. Use four spaces.

As per coding guidelines, **/*.{c,h,cc,cpp,cxx} requires four-space indentation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/internal/multiline.c` around lines 85 - 114, Update the initializer
entries in the new issue_6703 fixtures to use four-space indentation instead of
two spaces, including issue_6703_input, issue_6703_output,
issue_6703_disabled_output, issue_6703_all_empty_input, and
issue_6703_all_empty_output.

Source: Coding guidelines


/* ENDSWITH */
struct record_check endswith_input[] = {
{"1a. some multiline log \\"},
Expand Down Expand Up @@ -585,6 +616,150 @@ static void test_parser_cri()
flb_config_exit(config);
}

static void test_issue_6703()
{
int i;
int len;
int ret;
int entries;
uint64_t stream_id;
struct record_check *r;
struct flb_config *config;
struct flb_time tm;
struct flb_ml *ml;
struct flb_ml_parser_ins *mlp_i;
struct expected_result res = {0};

res.key = "log";
res.out_records = issue_6703_output;

config = flb_config_init();

ml = flb_ml_create(config, "cri-drop-empty-test");
TEST_CHECK(ml != NULL);

mlp_i = flb_ml_parser_instance_create(ml, "cri");
TEST_CHECK(mlp_i != NULL);
mlp_i->drop_empty_content = FLB_TRUE;

ret = flb_ml_stream_create(ml, "cri-drop-empty", -1, flush_callback,
(void *) &res, &stream_id);
TEST_CHECK(ret == 0);

entries = sizeof(issue_6703_input) / sizeof(struct record_check);
for (i = 0; i < entries; i++) {
r = &issue_6703_input[i];
len = strlen(r->buf);
flb_time_get(&tm);
ret = flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
TEST_CHECK(ret == FLB_MULTILINE_OK);
}
Comment on lines +649 to +656

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check every flb_ml_append_text() result.

The tests discard the append result. In test_issue_6703 and test_issue_6703_all_empty, failure to process empty input can still satisfy the expected flush count. Assign the result to ret and check ret >= 0 in each loop.

  • tests/internal/multiline.c#L649-L655: check each append result in the enabled test.
  • tests/internal/multiline.c#L696-L702: check each append result in the disabled test.
  • tests/internal/multiline.c#L743-L749: check each append result in the all-empty test.

As per coding guidelines, validate both success and failure paths.

📍 Affects 1 file
  • tests/internal/multiline.c#L649-L655 (this comment)
  • tests/internal/multiline.c#L696-L702
  • tests/internal/multiline.c#L743-L749
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/internal/multiline.c` around lines 649 - 655, In
tests/internal/multiline.c at lines 649-655, 696-702, and 743-749, update the
append loops in test_issue_6703 and test_issue_6703_all_empty to assign each
flb_ml_append_text() result to ret and assert ret >= 0, covering both success
and failure paths.

Source: Coding guidelines


TEST_CHECK(res.current_record == 2);

if (ml) {
flb_ml_destroy(ml);
}

flb_config_exit(config);
}

static void test_issue_6703_disabled()
{
int i;
int len;
int ret;
int entries;
uint64_t stream_id;
struct record_check *r;
struct flb_config *config;
struct flb_time tm;
struct flb_ml *ml;
struct flb_ml_parser_ins *mlp_i;
struct expected_result res = {0};

res.key = "log";
res.out_records = issue_6703_disabled_output;

config = flb_config_init();

ml = flb_ml_create(config, "cri-drop-empty-disabled-test");
TEST_CHECK(ml != NULL);

mlp_i = flb_ml_parser_instance_create(ml, "cri");
TEST_CHECK(mlp_i != NULL);
TEST_CHECK(mlp_i->drop_empty_content == FLB_FALSE);

ret = flb_ml_stream_create(ml, "cri-drop-empty-disabled", -1, flush_callback,
(void *) &res, &stream_id);
TEST_CHECK(ret == 0);

entries = sizeof(issue_6703_input) / sizeof(struct record_check);
for (i = 0; i < entries; i++) {
r = &issue_6703_input[i];
len = strlen(r->buf);
flb_time_get(&tm);
ret = flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
TEST_CHECK(ret == FLB_MULTILINE_OK);
}

TEST_CHECK(res.current_record == 3);

if (ml) {
flb_ml_destroy(ml);
}

flb_config_exit(config);
}

static void test_issue_6703_all_empty()
{
int i;
int len;
int ret;
int entries;
uint64_t stream_id;
struct record_check *r;
struct flb_config *config;
struct flb_time tm;
struct flb_ml *ml;
struct flb_ml_parser_ins *mlp_i;
struct expected_result res = {0};

res.key = "log";
res.out_records = issue_6703_all_empty_output;

config = flb_config_init();

ml = flb_ml_create(config, "cri-drop-empty-all-empty-test");
TEST_CHECK(ml != NULL);

mlp_i = flb_ml_parser_instance_create(ml, "cri");
TEST_CHECK(mlp_i != NULL);
mlp_i->drop_empty_content = FLB_TRUE;

ret = flb_ml_stream_create(ml, "cri-drop-empty-all-empty", -1, flush_callback,
(void *) &res, &stream_id);
TEST_CHECK(ret == 0);

entries = sizeof(issue_6703_all_empty_input) / sizeof(struct record_check);
for (i = 0; i < entries; i++) {
r = &issue_6703_all_empty_input[i];
len = strlen(r->buf);
flb_time_get(&tm);
ret = flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
TEST_CHECK(ret == FLB_MULTILINE_OK);
}

TEST_CHECK(res.current_record == 0);

if (ml) {
flb_ml_destroy(ml);
}

flb_config_exit(config);
}

static void test_container_mix()
{
int i;
Expand Down Expand Up @@ -2149,5 +2324,8 @@ TEST_LIST = {
{ "issue_5504" , test_issue_5504},
{ "issue_10576" , test_issue_10576},
{ "issue_truncation_10576", test_issue_truncation_10576 },
{ "issue_6703" , test_issue_6703},
{ "issue_6703_disabled", test_issue_6703_disabled},
{ "issue_6703_all_empty", test_issue_6703_all_empty},
{ 0 }
};