Analytics: Environment Filtering — dotCMS core: environment identity, collector plumbing, and config endpoint
@jcastro-dotcms is already working on this.
Since Sep 8, 2026.
- Dominant language
- Java
- Stars
- 970
- Forks
- 486
- Avg merge
- 3d 33m
- Merged PRs (30d)
- 170
Description
Summary
dotCMS core learns its own environment identity and the set of sibling environments from infra-provided configuration, forwards environment on every ingest call the auto-injected collector makes, and exposes a small config endpoint that the Angular dashboard uses to populate its environment selector — entirely config-driven, with no environment name ever hardcoded.
Parent epic: #37349
Spike: #37107
Depends on: the infra sub-task's env var contract (can be developed against local/stubbed env vars without waiting for the full customer rollout); the CAEM ingest sub-task's environment param (for the collector's forwarded value to actually land somewhere).
Background
Self-identity: DOT_ANALYTICS_ENVIRONMENT — a new env var sourced via the Kubernetes downward API from the dotcms.cloud/environment label, same fieldRef pattern already used for DOT_ANALYTICS_TENANT/PROJECT. Auto-derived, no manual per-customer entry. This is the value sent to CAEM at ingest time.
Sibling discovery: no existing mechanism resolves this (k8s labels only describe themselves; no infra registry exists). Decision: a second, Platform-curated env var:
DOT_ANALYTICS_AVAILABLE_ENVIRONMENTS=prod::Production Site,prod-2::APAC Site,qa
key::Label per entry, label optional (falls back to the bare key). Two vars, not merged into one — DOT_ANALYTICS_ENVIRONMENT is mechanically guaranteed correct (Kubernetes populates it from the pod's own manifest); folding it into the hand-curated list would reintroduce copy-paste drift. Parsing is forgiving, never fatal: a malformed entry falls back to using the raw text as both key and label, logged at WARN, never a startup failure or a 500.
New lightweight config endpoint (under the existing EventAnalyticsProxyResource/analytics resource area), returning a ready-to-render contract — the frontend does zero string-matching or environment-naming logic:
{
"current": "prod-2",
"available": [
{ "key": "prod", "label": "Production Site" },
{ "key": "prod-2", "label": "APAC Site" },
{ "key": "qa", "label": "qa" }
]
}
No currentLabel field — the label shown for the current entry is a fixed, locally-defined UI string ("Current env") that Angular substitutes in wherever item.key === current; Angular never computes which key is current or invents a display name for it.
Backed by a small composer that:
- Parses both env vars per the rules above.
- Guarantees
currentis always a member ofavailable— if Platform's curated list and the pod's own derived value ever disagree (e.g. a freshly-provisioned environment whose siblings' lists haven't been updated yet), the response still includescurrentinavailable(falling back to the bare key) rather than omitting an instance from its own dropdown, and logs aWARNso the drift gets fixed. - Uses the same
currentvalue as the one sent as theenvironmentingest param — one single place in dotCMS-core computes "what environment am I," never two that could disagree.
Collector plumbing: the auto-injected collector script (gated by the existing DOT_FEATURE_FLAG_CONTENT_ANALYTICS_AUTO_INJECT feature flag) needs to bake in DOT_ANALYTICS_ENVIRONMENT the same way it already bakes in tenant/project context, so every browser-fired ingest call carries &environment=<self>. Open item, flagged in the spike, not yet resolved: the exact injection code path wasn't traced to a specific file/line — confirmed the mechanism exists, needs a short investigation at the start of implementation, not a blocker to starting this ticket.
Ordering note — two pairs that look like they need careful sequencing but don't, both built to degrade gracefully:
- Infra env vars vs. this ticket's collector code: if the env var exists before the code reads it, it's unused; if the code deploys before the env var exists, it should just skip sending
&environment=— identical to today's behavior. - This ticket's
&environment=vs. CAEM's ingest API: an unrecognized param is silently ignored by Spring MVC either direction.
Minor, non-blocking follow-up: CAEM's existing internal-only GET /v1/report/environments (EnvironmentsRepository) lists distinct (tenant, project) pairs and could later add environment to its SELECT DISTINCT — not required for this epic, noted for whoever picks it up.
Scope
- Read
DOT_ANALYTICS_ENVIRONMENTandDOT_ANALYTICS_AVAILABLE_ENVIRONMENTSin dotCMS core. - Build the composer: parsing (forgiving-never-fatal), the current-in-available guarantee +
WARNon drift, single source of truth for "current." - New config endpoint returning
{current, available:[{key,label}]}. - Wire the auto-injected collector to send
&environment=<self>on every ingest call, once the injection point is located.
Out of scope
- Provisioning the env vars themselves on customer infrastructure (Servers/infrastructure sub-task).
- The Angular selector UI (Dashboard sub-task) — this ticket only ships the endpoint it will call.
EventAnalyticsProxyHelperchanges for the query side — confirmed unnecessary in the spike (it forwards params transparently already).
Dependencies
Can be developed and unit-tested against local/stubbed env var values without waiting for the infra rollout. Needs the CAEM ingest sub-task's environment param to exist for the collector's forwarded value to have any effect end-to-end. The Dashboard sub-task depends on this ticket's config endpoint contract being stable before it can wire up the selector.
Acceptance Criteria
- Config endpoint returns
current+available, withcurrentalways present inavailableeven under curated-list drift, and aWARNlogged when that drift happens - No environment name is ever hardcoded anywhere in this code path
- Auto-injected collector sends
&environment=<self>on every ingest call once the feature flag is on - Malformed
DOT_ANALYTICS_AVAILABLE_ENVIRONMENTSentries degrade gracefully (WARN, fallback to raw text), never a startup failure - Omitting either env var reproduces today's behavior (no
environmentsent, endpoint still returns a sane single-entry response for the current instance)
Next step
Run /speckit-specify against this issue to produce the formal spec (exact endpoint path, composer logic, collector injection point) before implementation.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.