Skip to content

Commit 3bf4539

Browse files
committed
out_cloudwatch_logs: increase MAX_EVENT_LEN to 1MB with tests
Increase MAX_EVENT_LEN from 262,118 bytes (256 KiB) to 1,000,000 bytes (1 MB) to better align with AWS CloudWatch's documented maximum event size of 1,048,576 bytes (1 MiB). The 1 MB limit provides a ~4.6% safety margin to account for JSON encoding overhead. Testing confirmed messages up to 1,048,546 bytes (encoding to 1,048,586 bytes) succeed, though we use a conservative limit for production safety. Add runtime tests to validate the new limit: - event_size_at_limit: Validates events at exactly MAX_EVENT_LEN (1MB) are accepted - event_size_over_limit: Validates events exceeding MAX_EVENT_LEN are truncated - event_truncation_with_backslash: Validates backslash handling at truncation boundary Signed-off-by: Shelby Hagman <[email protected]>
1 parent 4f8c50b commit 3bf4539

File tree

2 files changed

+149
-2
lines changed

2 files changed

+149
-2
lines changed

plugins/out_cloudwatch_logs/cloudwatch_api.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,17 @@
4343
/* Maximum number of character limits including both the Attributes key and its value */
4444
#define ATTRIBUTES_MAX_LEN 300
4545

46-
/* 256KiB minus 26 bytes for the event */
47-
#define MAX_EVENT_LEN 262118
46+
/*
47+
* https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
48+
* AWS CloudWatch's documented maximum event size is 1,048,576 bytes (1 MiB),
49+
* including JSON encoding overhead (structure, escaping, etc.).
50+
*
51+
* Setting MAX_EVENT_LEN to 1,000,000 bytes (1 MB) provides a ~4.6% safety margin
52+
* to account for JSON encoding overhead and ensure reliable operation.
53+
* Testing confirmed messages up to 1,048,546 bytes (encoding to 1,048,586 bytes)
54+
* succeed, though we use a conservative limit for production safety.
55+
*/
56+
#define MAX_EVENT_LEN 1000000
4857

4958
/* Prefix used for entity fields only */
5059
#define AWS_ENTITY_PREFIX "aws_entity"

tests/runtime/out_cloudwatch.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,17 @@
55
/* Test data */
66
#include "data/td/json_td.h" /* JSON_TD */
77

8+
/* CloudWatch API constants */
9+
#include "../../plugins/out_cloudwatch_logs/cloudwatch_api.h"
10+
811
#define ERROR_ALREADY_EXISTS "{\"__type\":\"ResourceAlreadyExistsException\"}"
912
/* not a real error code, but tests that the code can respond to any error */
1013
#define ERROR_UNKNOWN "{\"__type\":\"UNKNOWN\"}"
1114

15+
/* JSON structure constants for test message generation */
16+
static const char *TEST_JSON_PREFIX = "{\"message\":\"";
17+
static const char *TEST_JSON_SUFFIX = "\"}";
18+
1219
/* It writes a big JSON message (copied from TD test) */
1320
void flb_test_cloudwatch_success(void)
1421
{
@@ -393,6 +400,134 @@ void flb_test_cloudwatch_error_put_retention_policy(void)
393400
flb_destroy(ctx);
394401
}
395402

403+
/* Helper function to create a large JSON message of specified size */
404+
static char* create_large_json_message(size_t target_size)
405+
{
406+
size_t prefix_len = strlen(TEST_JSON_PREFIX);
407+
size_t suffix_len = strlen(TEST_JSON_SUFFIX);
408+
size_t overhead = prefix_len + suffix_len;
409+
size_t data_size;
410+
char *json;
411+
size_t i;
412+
413+
/* Ensure target size can accommodate JSON structure */
414+
if (target_size < overhead + 1) {
415+
target_size = overhead + 1;
416+
}
417+
418+
json = flb_malloc(target_size + 1);
419+
if (!json) {
420+
return NULL;
421+
}
422+
423+
/* Build JSON: prefix + data + suffix */
424+
strcpy(json, TEST_JSON_PREFIX);
425+
data_size = target_size - overhead;
426+
427+
/* Fill with 'A' characters */
428+
for (i = 0; i < data_size; i++) {
429+
json[prefix_len + i] = 'A';
430+
}
431+
432+
/* Close JSON object */
433+
strcpy(json + prefix_len + data_size, TEST_JSON_SUFFIX);
434+
json[target_size] = '\0';
435+
436+
return json;
437+
}
438+
439+
/* Helper to setup and run a CloudWatch test with custom JSON data */
440+
static void run_cloudwatch_test_with_data(char *data, size_t data_len)
441+
{
442+
int ret;
443+
flb_ctx_t *ctx;
444+
int in_ffd;
445+
int out_ffd;
446+
447+
setenv("FLB_CLOUDWATCH_PLUGIN_UNDER_TEST", "true", 1);
448+
449+
ctx = flb_create();
450+
TEST_CHECK(ctx != NULL);
451+
452+
in_ffd = flb_input(ctx, (char *) "lib", NULL);
453+
TEST_CHECK(in_ffd >= 0);
454+
flb_input_set(ctx, in_ffd, "tag", "test", NULL);
455+
456+
out_ffd = flb_output(ctx, (char *) "cloudwatch_logs", NULL);
457+
TEST_CHECK(out_ffd >= 0);
458+
flb_output_set(ctx, out_ffd, "match", "test", NULL);
459+
flb_output_set(ctx, out_ffd, "region", "us-west-2", NULL);
460+
flb_output_set(ctx, out_ffd, "log_group_name", "fluent", NULL);
461+
flb_output_set(ctx, out_ffd, "log_stream_prefix", "from-fluent-", NULL);
462+
flb_output_set(ctx, out_ffd, "auto_create_group", "On", NULL);
463+
flb_output_set(ctx, out_ffd, "net.keepalive", "Off", NULL);
464+
flb_output_set(ctx, out_ffd, "Retry_Limit", "1", NULL);
465+
466+
ret = flb_start(ctx);
467+
TEST_CHECK(ret == 0);
468+
469+
if (data) {
470+
flb_lib_push(ctx, in_ffd, data, data_len);
471+
}
472+
473+
sleep(2);
474+
flb_stop(ctx);
475+
flb_destroy(ctx);
476+
}
477+
478+
/* Test event size at maximum allowed limit (should succeed without truncation) */
479+
void flb_test_cloudwatch_event_size_at_limit(void)
480+
{
481+
char *large_json;
482+
483+
/* Create message at MAX_EVENT_LEN */
484+
large_json = create_large_json_message(MAX_EVENT_LEN);
485+
TEST_CHECK(large_json != NULL);
486+
487+
if (large_json) {
488+
run_cloudwatch_test_with_data(large_json, strlen(large_json));
489+
flb_free(large_json);
490+
}
491+
}
492+
493+
/* Test event size exceeding limit (should be truncated to MAX_EVENT_LEN) */
494+
void flb_test_cloudwatch_event_size_over_limit(void)
495+
{
496+
char *large_json;
497+
498+
/* Create message exceeding MAX_EVENT_LEN by 1 byte to test truncation */
499+
large_json = create_large_json_message(MAX_EVENT_LEN + 1);
500+
TEST_CHECK(large_json != NULL);
501+
502+
if (large_json) {
503+
run_cloudwatch_test_with_data(large_json, strlen(large_json));
504+
flb_free(large_json);
505+
}
506+
}
507+
508+
/* Test event with trailing backslash at truncation boundary */
509+
void flb_test_cloudwatch_event_truncation_with_backslash(void)
510+
{
511+
char *large_json;
512+
size_t prefix_len = strlen(TEST_JSON_PREFIX);
513+
size_t i;
514+
515+
/* Create base message near MAX_EVENT_LEN */
516+
large_json = create_large_json_message(MAX_EVENT_LEN + 100);
517+
TEST_CHECK(large_json != NULL);
518+
519+
if (large_json) {
520+
/* Post-process: replace every 100th character with backslash */
521+
for (i = 99; large_json[prefix_len + i] != '\0' &&
522+
large_json[prefix_len + i] != '"'; i += 100) {
523+
large_json[prefix_len + i] = '\\';
524+
}
525+
526+
run_cloudwatch_test_with_data(large_json, strlen(large_json));
527+
flb_free(large_json);
528+
}
529+
}
530+
396531
/* Test list */
397532
TEST_LIST = {
398533
{"success", flb_test_cloudwatch_success },
@@ -405,5 +540,8 @@ TEST_LIST = {
405540
{"put_retention_policy_success", flb_test_cloudwatch_put_retention_policy_success },
406541
{"already_exists_create_group_put_retention_policy", flb_test_cloudwatch_already_exists_create_group_put_retention_policy },
407542
{"error_put_retention_policy", flb_test_cloudwatch_error_put_retention_policy },
543+
{"event_size_at_limit", flb_test_cloudwatch_event_size_at_limit },
544+
{"event_size_over_limit", flb_test_cloudwatch_event_size_over_limit },
545+
{"event_truncation_with_backslash", flb_test_cloudwatch_event_truncation_with_backslash },
408546
{NULL, NULL}
409547
};

0 commit comments

Comments
 (0)