MXC Telemetry
August 20, 2026 · View on GitHub
MXC uses the Rust tracelogging crate
(published by Microsoft) for TraceLogging ETW telemetry. No C++ shim, WIL, or
FFI is required.
Overview
┌──────────────────────────────────────────────────────┐
│ wxc_common::telemetry │
│ (Rust — config resolution, sanitisation, types) │
│ │
│ init() / log_execution() / log_error() / shutdown() │
└───────────────┬──────────────────────────────────────┘
│ Direct Rust function calls
▼
┌──────────────────────────────────────────────────────┐
│ mxc_telemetry (Rust crate) │
│ src/lib.rs — define_provider! + write_event! │
│ │
│ Windows: ETW events via tracelogging crate │
│ Linux/macOS: no-op stubs │
└──────────────────────────────────────────────────────┘
Why the Rust tracelogging Crate (Not WIL C++ Shim)
An earlier design used a WIL C++ shim compiled via the cc crate. PR review
feedback correctly noted that the WIL dependency added C++ compilation, NuGet
download, FFI unsafety, and blocked non-Windows contributors from building the
crate. The Rust tracelogging crate provides the core ETW primitives needed,
and the small set of WIL features MXC actually uses can be replicated with
Rust constants and write_event! struct fields.
Feature comparison
| Feature | WIL (wil/TraceLogging.h) | Rust tracelogging crate | MXC approach |
|---|---|---|---|
| Provider group GUID | TraceLoggingOptionMicrosoftTelemetry() | group_id("...") in define_provider! | build.rs generates provider_def.rs with/without group_id based on env var |
| Sampling keywords | MICROSOFT_KEYWORD_MEASURES named constant | Raw u64 in keyword(...) | const MICROSOFT_KEYWORD_MEASURES: u64 = 0x0000_4000_0000_0000 |
| Common event fields | _GENERIC_PARTB_FIELDS_ENABLED pattern | struct("Name", { ... }) in write_event! | struct("COMMON_MXC_PARAMS", { Version, Channel, IsDebugging, UTCReplace_AppSessionGuid }) |
| Provider lifecycle | IMPLEMENT_TRACELOGGING_CLASS singleton | define_provider! static + register()/unregister() | OnceLock<ProviderState> for version/channel, manual lifecycle |
| Privacy Data Tags | TelemetryPrivacyDataTag(PDT_*) | u64("PartA_PrivTags", &val) field | PDT_PRODUCT_AND_SERVICE_USAGE on all events |
| Activity tracking | DEFINE_TELEMETRY_ACTIVITY | Manual Opcode | Not needed for current events |
The remaining gap (activity tracking) is not needed for current events. If needed later, it can be added incrementally.
Common Event Fields (Part C)
Every MXC telemetry event includes a COMMON_MXC_PARAMS struct grouping
shared Part C custom event fields:
| Field | Type | Description |
|---|---|---|
Version | string | MXC crate version from CARGO_PKG_VERSION |
Channel | string | "dev" for debug builds, "release" for release |
IsDebugging | bool | cfg!(debug_assertions) — true for debug builds |
UTCReplace_AppSessionGuid | bool | Always true — tells UTC to replace the app session GUID with a per-session identifier for privacy |
Events
The provider-qualified uploaded event identities are
Microsoft.MXC/MXC.Execution and Microsoft.MXC/MXC.Error. Microsoft.MXC
is the TraceLogging provider name; MXC.Execution and MXC.Error are the
event names.
MXC.Execution
Emitted when a one-shot execution completes (success or failure). It is also
emitted on early-exit failures in the one-shot executors — configuration,
policy, and backend-init failures that terminate before a runner produces a
result (with mxc.exit_code = 1 and mxc.outcome = failure).
The state-aware lifecycle (provision / start / exec / stop /
deprovision) is also instrumented: each dispatched phase emits one
MXC.Execution tagged with mxc.phase. Non-exec phases and exec dry-runs
report success with mxc.exit_code = 0; a completed exec reports the sandbox
process exit code; a dispatch error reports failure plus an MXC.Error. As in
the one-shot path, a clean non-zero sandbox exit is not treated as an MXC error.
| Field | Type | Description |
|---|---|---|
mxc.sandbox_kind | string | Containment kind requested by the caller (process, vm, or a concrete backend name) |
mxc.backend | string | Concrete containment backend selected on the host |
mxc.exit_code | int32 | Process exit code |
mxc.outcome | string | "success" or "failure" |
mxc.duration_ms | uint64 | Total execution time |
mxc.failure_reason | string | Failure category (if applicable) |
mxc.phase | string | State-aware lifecycle phase (provision|start|exec|stop|deprovision); empty for one-shot executions |
MXC.Error
Emitted on execution errors.
| Field | Type | Description |
|---|---|---|
mxc.sandbox_kind | string | Containment kind requested by the caller (process, vm, or a concrete backend name) |
mxc.backend | string | Concrete containment backend selected on the host |
mxc.error_type | string | Error category (config_error, policy_error, process_error, timeout, init_error, internal_error, cancelled, unknown) |
mxc.exit_code | int32 | Process exit code |
mxc.phase | string | State-aware lifecycle phase; empty for one-shot executions |
No free-form error text is emitted. Error messages can contain paths, usernames, or credentials, so
MXC.Errordeliberately carries only the boundederror_typecategory and the numericexit_code— never the message string itself.
Crash telemetry (panic hook)
When telemetry is active, the executors install a global
[std::panic::set_hook] handler — both the one-shot executors and the
state-aware path (run_state_aware_main). If any thread panics, the hook emits
a failure MXC.Execution plus an MXC.Error categorised as internal_error
(with mxc.exit_code = 101, the conventional Rust panic/abort exit code),
attributed to the containment backend recorded at telemetry init and, on the
state-aware path, the mxc.phase in progress. Consistent
with the PII policy, no panic message or backtrace text is emitted — only
the bounded category and exit code. The hook chains the previously-installed
hook, so the default stderr backtrace still prints.
Limitation: Only failures that occur after telemetry initialisation can be reported. A panic during argument parsing or config load — before
telemetry::initruns — cannot emit an event, because the provider is not yet registered.
Limitation: On backends that recover panics via
catch_unwind(the LXC runner does this for container-cleanup safety), the panic hook still fires during unwinding and records the crash event with the101sentinel exit code, then claims the exactly-once terminal-emit slot. The recoveredMXC.Executioncompletion event is therefore suppressed, so telemetry reportsmxc.exit_code= 101 even though the recovered process ultimately exits with a different code (-1). the101here is a "a panic occurred" sentinel, not a claim about the observed process exit code;outcomeanderror_typeremain accurate. Backends that do not catch panics (the Windows one-shot executor) abort with101, so the recorded code matches the real exit.
Cancellation telemetry (console control handler)
On Windows, when telemetry is active, wxc-exec's console control handler emits
a failure MXC.Execution plus an MXC.Error categorised as cancelled when the
operator interrupts a run (Ctrl-C, console close, or a system shutdown/logoff).
The reported mxc.exit_code is 130 (the conventional "terminated by Ctrl-C"
code, 128 + SIGINT) — a bounded attribution sentinel, since the OS ultimately
terminates the process with its own status. The handler runs on a short,
OS-imposed budget and does not shut the provider down; the events carry no
free-form text.
Cross-Platform Behaviour
| Platform | Behaviour |
|---|---|
| Windows | Full ETW telemetry via tracelogging crate |
| Linux | No-op — all telemetry functions return immediately |
| macOS | No-op — all telemetry functions return immediately |
Consent
Telemetry emission is gated by the per-run request, MXC-owned consent,
administrative policy, and provider availability. See
docs/telemetry/telemetry-consent-design.md.
Privacy review status
The version 1 en-US consent wording is approved for release review. The
canonical title, body, action labels, and privacy link are documented in
Telemetry consent design
and must be rendered verbatim by every EXE and SDK presenter.
Data sent
MXC's optional diagnostic events contain:
- MXC version and channel
- Whether the build has debug assertions enabled (
IsDebugging) - Caller-requested sandbox kind and the concrete backend selected on the host
- Run outcome and exit code
- Run duration
- Bounded failure category
- State-aware lifecycle phase
UTCReplace_AppSessionGuid, which asks the telemetry pipeline to supply a random per-session app identifier
MXC does not emit commands, file paths, credentials, customer content, or free-form error text. The consent notice's phrase “other customer content” covers any such values that a host or sandbox may process but MXC does not include in these events.
Review status
The consent wording and canonical resource are approved for release review. The broader data inventory, retention, access, regional processing, deletion, and localization/accessibility decisions remain pending explicit privacy review.