Root-cause analysis (2026-07-21, read-only AWS + IaC repo inspection) of why the FR (France) region BESS services produce no logs in Datadog: the extracted modules/bess_os terraform module creates the regional CloudWatch log groups but never wires the dd-logs-forwarder subscription filters, so logs stop at CloudWatch.
Context: the FR deployment
- The France deployment is
module "bess_os_fr"inoptimization-universe-iacatterraform/bess_os_fr.tf(origin/develop). - Dev-only:
count = var.environment == "dev" ? 1 : 0. - It instantiates the shared
terraform/modules/bess_osmodule withregion_code = "fr". - Runs in the bit-bessos-dev account
794038257734,eu-central-1(same account/profile setup as AWS CLI Access via Identity Center).
Log shipping architecture in this stack
- App containers use the
awslogsdriver, writing into CloudWatch log groups. - Datadog receives logs via CloudWatch subscription filters pointing at the
datadog-forwarderLambda. - Those subscription filters are created by the
dd-logs-forwarderterraform module:git::https://gitlab.com/alpiq_cicd/terraform-modules/bit-tf-modules.git//dd-logs-forwarder?ref=v3.21.4 - The Datadog agent sidecar in each ECS task handles metrics/APM/dogstatsd only, NOT container logs. An FR task looking “healthy” in APM while having zero logs is consistent with this split.
Root cause
The forwarding step was dropped when the module was extracted:
- Top-level
terraform/cloudwatch.tfcreates the original log groups AND onedd-logs-forwardermodule instantiation per group:datadog_mando_logs,datadog_optimization_logs,datadog_forecast_logs,datadog_dashboard_logs. - The extracted
modules/bess_os/cloudwatch.tfcreates the regional log groups (mando-fr,optimization-fr,forecast-fr,dashboard-fr,asset-simulator-fr) but contains nodd-logs-forwarderblocks at all.
For Agents
Failure class: module extraction dropped a side-resource. The log groups moved into
modules/bess_os/cloudwatch.tf; the per-groupdd-logs-forwarderinstantiations stayed only in the top-levelterraform/cloudwatch.tfand were never replicated into the module. Any future regional instantiation ofmodules/bess_osinherits the same gap until fixed in the module.
Live evidence (dev account, 2026-07-21)
- All four original log groups
/aws/ecs/bess-os/eu-central-1/dev/{mando,optimization,forecast,dashboard}each have a subscription filter toarn:aws:lambda:eu-central-1:794038257734:function:datadog-forwarder. - The four
-frcounterparts have zero subscription filters. - The FR services are actively writing to CloudWatch:
mando-frandforecast-frlast events were ~27 minutes old at check time. So logs exist, they just never leave CloudWatch.
Fix direction (not applied)
Add dd-logs-forwarder module instantiations inside modules/bess_os/cloudwatch.tf for each regional log group: mando, optimization, forecast, dashboard, plus asset_simulator when enabled, mirroring the top-level terraform/cloudwatch.tf blocks.
Sibling exemplar
modules/bess_am/ecs.tf(thebess_ammodule) has its own “Datadog Log Forwarding” section: a module that DID carry its forwarding wiring along. Useful shape to copy when patchingmodules/bess_os.
Deployment of the fix follows the usual IaC ceremony (plan on develop, manual terraform_apply:dev), see Mando Deployment Ceremony.
Fix implementation and feature-branch deploy (2026-07-21)
- Fix implemented on branch
bugfix/fr-datadog-log-forwardersofoptimization-universe-iac(worktree.worktrees/fr-dd-forwarders), commitc169afc fix: add datadog log forwarders to bess_os module: sevendd-logs-forwarderblocks appended toterraform/modules/bess_os/cloudwatch.tf, theasset_simulatorone count-gated onvar.asset_simulator_enabled. - Verified before deploy: the
dd-logs-forwarderv3.21.4 source creates exactly oneaws_cloudwatch_log_subscription_filterper instantiation targeting the account’sdatadog-forwarderLambda; the Lambda resource policy allowslogs.amazonaws.comforlog-group:*, so no per-group permission is needed; thecustomer_portalmodule proves in-module instantiation works live. - New ceremony learned: the IaC repo’s
terraform_apply:devjob is gatedonly: [develop]; to deploy a feature branch to dev, temporarily add the branch regex to thatonly:list (commit8592eeb ci: enable dev apply on fr datadog forwarders branch, one line in.gitlab-ci.yml). Apply remainswhen: manual. The CI commit must be dropped before merge, same as mando AGENTS.md section 14.3.
Known drift to watch in the plan
The live FR stack runs a 1.16.1-feat mando image and an asset-simulator container, while develop pins other versions and sets
asset_simulator_enabled = falsefor fr; a develop-based apply may revert those.
- Module source fetched read-only via SSH clone of
git@gitlab.com:alpiq_cicd/terraform-modules/bit-tf-modules.git(https clone fails auth; ssh works).
Follow-up: FR logs show unsuffixed service name (2026-07-22)
After the forwarder fix, FR logs DO arrive in Datadog, but tagged service:bess-os-service-mando instead of bess-os-service-mando-fr.
- Investigated live: the log group AWS tag, container
DD_SERVICEenv, docker labels, ECS resource tags, and forwarder lambda tags are ALL correctly-fr-suffixed. Nothing in the infra is wrong. - Each FR log event in Datadog carries TWO service tags:
service:bess-os-service-mando(wins) andservice:bess-os-service-mando-fr(added by forwarder log-group tag enrichment,DD_ENRICH_CLOUDWATCH_TAGS).
Root cause is in mando, not IaC
mando embeds a
ddtagsfield in every JSON log line viadd_formatter, built from the hardcoded constantpub(crate) const DD_SERVICE: &'static str = "bess-os-service-mando";atmando-lib/src/app/mod.rs:38(develop). A unit test (dd_formatter.rsddtags_include_unified_service_tags) even asserts the hardcoded value. The embedded ddtags service wins over forwarder enrichment in Datadog.
Fix direction (mando repo): read the DD_SERVICE env var at init in dd_formatter/init_log, keeping the constant as fallback; optionally do the same for the hardcoded global::meter("bess-os-service-mando") names in service_base.rs:120-132.
Mando-side fix implemented (2026-07-22)
Shipped as commit ab8cfd11 “fix: read dd service name from env” on feature/BE-3657 (Andras asked to include it in the upcoming MR; full detail in DD_SERVICE env fix (commit ab8cfd11, 2026-07-22)):
resolve_dd_service()+ aOnceLock-cacheddd_service()accessor inmando-lib/src/app/mod.rs(appfeature gated); hardcoded value stays as fallback, also on empty string.- All 6 hardcode sites routed through it: OTel Resource service name (
get_tagging),ddtagsindd_formatter.rs, and 4 rawglobal::meter("bess-os-service-mando")literals inservice_base.rs(3x) +util/http_client_trace.rsthat had bypassed the old const. - Deployment-neutral on the main stack:
terraform/bess_os_ecs.tf:218already setsDD_SERVICE = "bess-os-service-mando"on the mando container, so behavior only changes where the env differs. The FR stack’s-fr-suffixed value finally wins once a build carrying the fix deploys there. - Not yet merged: rides the BE-3657 MR, which waits on Andras’s explicit yes.
Debugging technique
Raw CloudWatch event inspection via
aws logs get-log-eventsis the fastest way to see what mando actually emits, including the embeddedddtagsline.
Related
- Mando Deployment Ceremony
- BE-3657 error_stack Adoption - carries the
ab8cfd11DD_SERVICE env fix for the unsuffixed-service-name follow-up - Dev Deploy 2026-07-14 (error telemetry) (first sighting of the
bess_os_frstack in a plan diff) - BE-1842 Datadog Observability
- BE-3482 Datadog Logs and APM Conformance
- AWS CLI Access via Identity Center