diff --git a/src/foundation/log.c b/src/foundation/log.c index 3e5f90c43..fd916524a 100644 --- a/src/foundation/log.c +++ b/src/foundation/log.c @@ -66,11 +66,9 @@ parse_format:; return; } - const char *env = getenv("ENVIRONMENT"); - const char *cloud_run = getenv("K_SERVICE"); - if ((env && strcmp(env, "production") == 0) || (cloud_run && cloud_run[0] != '\0')) { - cbm_log_set_format(CBM_LOG_FORMAT_JSON); - } + /* Format is intentionally explicit-only. Logs stay local to stderr and the + * optional in-process sink; deployment environment variables must not + * silently change the operator-selected output shape. */ } void cbm_log_set_sink(cbm_log_sink_fn fn) { diff --git a/src/foundation/log.h b/src/foundation/log.h index 74c68d45a..7f247f086 100644 --- a/src/foundation/log.h +++ b/src/foundation/log.h @@ -4,7 +4,7 @@ * Design: * - All output goes to stderr (stdout is reserved for MCP JSON-RPC) * - Structured text format: "level=info msg=pass.timing pass=defs elapsed_ms=42" - * - Optional JSON format for production collectors / Cloud Logging + * - Optional JSON format for local structured parsing * - Levels: DEBUG, INFO, WARN, ERROR * - Level filtering at runtime via cbm_log_set_level() or the * CBM_LOG_LEVEL env var (see cbm_log_init_from_env) @@ -40,9 +40,8 @@ typedef enum { * the numeric equivalents 0..4 matching CBMLogLevel. Unknown, empty, or * unset values leave the level unchanged (fail-open). * - * Also applies CBM_LOG_FORMAT=text|json. If unset, JSON is auto-enabled for - * production / Cloud Run style environments (ENVIRONMENT=production or - * K_SERVICE present). Call once at startup before any threads or log lines. */ + * Also applies CBM_LOG_FORMAT=text|json. If unset, the current format is left + * unchanged. Call once at startup before any threads or log lines. */ void cbm_log_init_from_env(void); /* Set minimum log level (default: INFO). */ diff --git a/tests/test_log.c b/tests/test_log.c index 8165d8967..5a1fdb7aa 100644 --- a/tests/test_log.c +++ b/tests/test_log.c @@ -197,6 +197,19 @@ TEST(log_format_from_env) { PASS(); } +TEST(log_format_is_explicit_only) { + cbm_unsetenv("CBM_LOG_FORMAT"); + cbm_setenv("ENVIRONMENT", "production", 1); + cbm_setenv("K_SERVICE", "cbm", 1); + cbm_log_set_format(CBM_LOG_FORMAT_TEXT); + cbm_log_init_from_env(); + ASSERT_EQ(cbm_log_get_format(), CBM_LOG_FORMAT_TEXT); + + cbm_unsetenv("ENVIRONMENT"); + cbm_unsetenv("K_SERVICE"); + PASS(); +} + /* CBM_LOG_LEVEL parsing — distilled from #414 (closes #413). */ TEST(log_level_from_env_textual) { cbm_setenv("CBM_LOG_LEVEL", "error", 1); @@ -281,6 +294,7 @@ SUITE(log) { RUN_TEST(log_sink_tee_keeps_stderr); RUN_TEST(log_operational_helpers); RUN_TEST(log_format_from_env); + RUN_TEST(log_format_is_explicit_only); RUN_TEST(log_level_from_env_textual); RUN_TEST(log_level_from_env_numeric); RUN_TEST(log_level_from_env_invalid_ignored);