Skip to content

[Heartbeat] Pass synthetics monitor context for APM cross-linking#51083

Open
shahzad31 wants to merge 3 commits into
elastic:mainfrom
shahzad31:synth/apm-crosslink-env
Open

[Heartbeat] Pass synthetics monitor context for APM cross-linking#51083
shahzad31 wants to merge 3 commits into
elastic:mainfrom
shahzad31:synth/apm-crosslink-env

Conversation

@shahzad31

@shahzad31 shahzad31 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

When Heartbeat spawns the @elastic/synthetics runner for browser/api monitors, it now injects the monitor execution context as environment variables so the synthetics runner's APM plugin can cross-link a journey execution with the APM data of the application it traces:

  • ELASTIC_SYNTHETICS_TRACE_ID — the monitor.check_group of the current attempt
  • ELASTIC_SYNTHETICS_MONITOR_IDstdfields.ID
  • ELASTIC_SYNTHETICS_MONITOR_TYPEstdfields.Type (browser/api)
  • ELASTIC_SYNTHETICS_MONITOR_LOCATIONstdfields.RunFrom.ID

Only non-empty values are emitted so the synthetics agent can fall back to its own defaults.

Trace id == check_group

The trace id must equal the monitor.check_group published on the heartbeat documents so APM data can be joined back to the exact journey execution. monitor.check_group is owned by the summarizer (formatted as <uuid>-<attempt>) and was previously only written after the job ran. This PR sets it in the state plugins' BeforeEachEvent (before the synthetics process is spawned), and synthexec reads it off the event to use as the trace id. This keeps the two identical, including across retries (the root job re-runs with the bumped attempt). When run outside the summarizer (e.g. unit tests), it falls back to the synthexec stream enricher's own check group.

This is the Heartbeat side of the Synthetics ⇄ APM cross-linking effort and the counterpart to elastic/synthetics#595.

Why is it important?

It lets the Synthetics waterfall be correlated with APM traces of the monitored application, enabling end-to-end performance analysis across Elastic Observability. See elastic/apm#823.

Scope / follow-ups (intentionally out of this PR)

Checklist

  • Unit test for the env builder (TestSyntheticsCrosslinkEnv)
  • Unit test for reading the check group off the event (TestCheckGroupFromEvent)
  • Unit test for the state plugins setting monitor.check_group early (TestStateStatusPluginBeforeEachEventSetsCheckGroup)
  • Manual end-to-end validation once the synthetics-side plugin enablement lands

How to test this PR locally

go test -tags synthetics -run 'TestSyntheticsCrosslinkEnv|TestCheckGroupFromEvent' ./x-pack/heartbeat/monitors/browser/synthexec/
go test ./heartbeat/monitors/wrappers/...

Related issues

Inject the monitor id, type, location and per-execution trace id as
ELASTIC_SYNTHETICS_* environment variables when spawning the
@elastic/synthetics runner, so its APM plugin can cross-link synthetics
journey executions with the traced application's APM data.

Relates to elastic/synthetics#595

Co-authored-by: Cursor <cursoragent@cursor.com>
@botelastic botelastic Bot added the needs_team Indicates that the issue/PR needs a Team:* label label Jun 3, 2026
@botelastic

botelastic Bot commented Jun 3, 2026

Copy link
Copy Markdown

This pull request doesn't have a Team:<team> label.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🤖 GitHub comments

Just comment with:

  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)
  • /test : Run the Buildkite pipeline.

@mergify

mergify Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

This pull request does not have a backport label.
If this is a bug or security fix, could you label this PR @shahzad31? 🙏.
For such, you'll need to label your PR with:

  • The upcoming major version of the Elastic Stack
  • The upcoming minor version of the Elastic Stack (if you're not pushing a breaking change)

To fixup this pull request, you need to add the backport labels for the needed
branches, such as:

  • backport-8./d is the label to automatically backport to the 8./d branch. /d is the digit
  • backport-active-all is the label that automatically backports to all active branches.
  • backport-active-8 is the label that automatically backports to all active minor branches for the 8 major.
  • backport-active-9 is the label that automatically backports to all active minor branches for the 9 major.

shahzad31 and others added 2 commits June 3, 2026 12:21
Set monitor.check_group in the state plugins' BeforeEachEvent (before the
synthetics process is spawned) and read it back in synthexec so the injected
ELASTIC_SYNTHETICS_TRACE_ID equals the published monitor.check_group (including
the attempt suffix). Falls back to the stream enricher's check group when run
outside the summarizer.

Co-authored-by: Cursor <cursoragent@cursor.com>
Beats lints with --whole-files, so touching a file surfaces all of its
pre-existing issues. Address the ones dragged in by this PR:

- synthexec.go: route the package's global-logger access through a single
  synthexecLog() helper so logp.L() (forbidigo) isn't referenced throughout;
  threading a *logp.Logger through the synthetics job factory is out of scope.
- plugstatestat.go: suppress the two pre-existing logp.L() usages.
- Move the new BeforeEachEvent check_group test into plugstatestat_test.go so
  summarizer_test.go is no longer modified and doesn't inherit its lint debt.
- synthexec_test.go: use require.Empty, fix expected/actual order, and suppress
  gosec/noctx on the existing go-toolchain test helper.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shahzad31 shahzad31 marked this pull request as ready for review June 18, 2026 11:20
@shahzad31 shahzad31 requested a review from a team as a code owner June 18, 2026 11:20
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 27d73b9d-9c43-4e85-a252-6d382204c62e

📥 Commits

Reviewing files that changed from the base of the PR and between 24b3ef5 and 96a99b4.

📒 Files selected for processing (5)
  • changelog/fragments/1780480871-heartbeat-synthetics-apm-crosslink-env.yaml
  • heartbeat/monitors/wrappers/summarizer/plugstatestat.go
  • heartbeat/monitors/wrappers/summarizer/plugstatestat_test.go
  • x-pack/heartbeat/monitors/browser/synthexec/synthexec.go
  • x-pack/heartbeat/monitors/browser/synthexec/synthexec_test.go

📝 Walkthrough

Walkthrough

commonSSP.setCheckGroup is added to plugstatestat.go, formatting monitor.check_group as <base-check-group>-<attempt> and writing it onto the beat.Event. Both BrowserStateStatusPlugin.BeforeEachEvent and LightweightStateStatusPlugin.BeforeEachEvent are updated to call this helper. In synthexec.go, a new checkGroupFromEvent function reads monitor.check_group from the event (falling back to senr.checkGroup), and syntheticsCrosslinkEnv builds ELASTIC_SYNTHETICS_* environment variables from it plus monitor identity/location fields. runCmd gains a traceID parameter and appends these vars to the subprocess environment. All logp.L() calls in synthexec.go are replaced with a new synthexecLog() helper. Tests and a changelog fragment are included.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • 🛠️ Update Documentation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs_team Indicates that the issue/PR needs a Team:* label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant