Skip to content

feat(observability): profiles end-to-end (gateway + UI panel) - #5382

Closed
Ma77Ball wants to merge 8 commits into
apache:mainfrom
Ma77Ball:obs/pr8/profiles
Closed

Ma77Ball wants to merge 8 commits into
apache:mainfrom
Ma77Ball:obs/pr8/profiles

Conversation

@Ma77Ball

@Ma77Ball Ma77Ball commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Continuous-profiling retrieval through the gateway plus a flame-graph UI panel.
Backend:

  • ParcaClient: gRPC-Web client to the Parca server, with a profiles query builder and parseProfiles.
  • ProfilesResource exposing scoped profile queries (optional workflow and execution id), registered in TexeraWebApplication.
    Frontend:
  • Profiles panel with a flame-chart component, scoped by workflow and execution id, with explicit empty states for "no samples yet" and for environments where profiling is disabled.

Any related issues, documentation, or discussions?

Closes: #5374
Part of #4070. Stacked on #5381.

How was this PR tested?

  • Backend specs for ParcaClient, the profiles query builder, and the parser; sbt scalafmtCheckAll passes.
  • Frontend profiles-panel and flame-chart specs; prettier-eslint and eslint pass.
  • Compile and the full test suites run in this PR's CI.

Was this PR authored or co-authored using generative AI tooling?

Co-authored with Claude Opus 4.8 in compliance with ASF

@github-actions github-actions Bot added engine dependencies Pull requests that update a dependency file frontend Changes related to the frontend GUI docs Changes related to documentations infra common labels Jun 5, 2026
@codecov-commenter

codecov-commenter commented Jun 5, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.08497% with 160 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.43%. Comparing base (b143a4f) to head (d438104).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
...observability/logs-panel/logs-panel.component.html 56.71% 58 Missing ⚠️
...ala/org/apache/texera/observability/OtelInit.scala 70.86% 25 Missing and 12 partials ⚠️
...rg/apache/texera/web/service/WorkflowService.scala 74.13% 11 Missing and 4 partials ⚠️
...r/observability/logs-panel/logs-panel.component.ts 87.37% 2 Missing and 11 partials ⚠️
.../apache/texera/observability/WorkflowMetrics.scala 84.93% 4 Missing and 7 partials ⚠️
...e/texera/observability/TexeraOtelLogAppender.scala 78.04% 2 Missing and 7 partials ⚠️
...org/apache/texera/observability/TexeraTracer.scala 0.00% 4 Missing ⚠️
...ra/web/observability/WorkflowMetricsRecorder.scala 83.33% 2 Missing and 1 partial ⚠️
...org/apache/texera/observability/LogSanitizer.scala 92.10% 0 Missing and 3 partials ⚠️
...ervice/user/observability/observability.service.ts 91.17% 2 Missing and 1 partial ⚠️
... and 3 more
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5382      +/-   ##
============================================
- Coverage     92.60%   92.43%   -0.17%     
- Complexity     5043     5061      +18     
============================================
  Files          1252     1269      +17     
  Lines         53587    54303     +716     
  Branches       6673     6790     +117     
============================================
+ Hits          49623    50194     +571     
- Misses         2315     2418     +103     
- Partials       1649     1691      +42     
Flag Coverage Δ *Carryforward flag
access-control-service 77.44% <100.00%> (+0.06%) ⬆️ Carriedforward from 8a3f596
agent-service 99.16% <ø> (ø)
amber 87.98% <80.24%> (-0.11%) ⬇️ Carriedforward from 8a3f596
computing-unit-managing-service 60.52% <100.00%> (+0.03%) ⬆️
config-service 87.50% <100.00%> (+0.12%) ⬆️
file-service 81.45% <0.00%> (-0.09%) ⬇️ Carriedforward from 8a3f596
frontend 96.31% <77.61%> (-0.27%) ⬇️ Carriedforward from 8a3f596
notebook-migration-service 83.77% <100.00%> (+0.03%) ⬆️
pyamber 98.52% <ø> (-0.07%) ⬇️
workflow-compiling-service 74.25% <100.00%> (+0.15%) ⬆️ Carriedforward from 8a3f596

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added the platform Non-amber Scala service paths label Jun 6, 2026
@Ma77Ball
Ma77Ball marked this pull request as ready for review June 7, 2026 00:40
@Ma77Ball
Ma77Ball marked this pull request as draft June 7, 2026 00:40
@github-actions

github-actions Bot commented Jun 12, 2026 •

Copy link
Copy Markdown
Contributor

📊 Arrow Flight E2E bench

(no arrow-flight-e2e.csv in artifact)

Full dashboard · Workflow run

@github-actions

github-actions Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Committers with relevant context: @parshimers
    You can request their reviews formally with /request-review @parshimers.

  • Contributors with relevant context: @bobbai00, @dzueck, @aicam
    You can notify them by mentioning @bobbai00, @dzueck, @aicam in a comment.

Ma77Ball and others added 8 commits October 8, 2026 19:50
Introduce the OpenTelemetry foundation for Texera: an SDK bootstrap, a
Logback-to-OTel log bridge, and a log sanitizer, wired into every service
entry point. This is PR 1 of the observability stack; it ships logging only,
with the trace and metric exporters wired but not yet emitted (those arrive in
follow-up PRs).

New module (common/observability):
- OtelInit: one-call SDK bootstrap per service. Reads OTEL_* settings from
  observability.conf, validates the OTLP endpoint (scheme + host allowlist)
  before any exporter is built, wires the span/log/metric providers explicitly
  (no sdk-extension-autoconfigure), clamps the metric export interval, and
  attaches the log appender to the Logback ROOT logger. The whole init body is
  guarded so a missing or malformed config returns None with one WARN instead
  of throwing into a service's run(), even when telemetry is disabled.
- TexeraOtelLogAppender: bridges Logback events to OTel log records. Maps
  severity, forwards MDC, sets exception.type/message/stacktrace semantic
  attributes, and drops records from io.opentelemetry loggers so export-failure
  diagnostics are not fed back to the collector that just failed.
- LogSanitizer: strips C0 control characters, redacts secrets, and caps body
  length (MaxBodyChars) to keep individual records bounded.

Config (common/config):
- observability.conf with the OTEL_* defaults, ObservabilityConfig to read it,
  and ENV_OTEL_* entries in EnvironmentalVariable.

Wiring:
- build.sbt defines the Observability module and adds dependsOn(Observability)
  to the eight Dropwizard services.
- Each service entry point calls OtelInit.init with its own service name, and
  each service config gains a logging block.

Deployment:
- OTEL_* env entries in bin/single-node/.env, bin/k8s/values.yaml, and
  bin/k8s/values-development.yaml (kept a name-for-name mirror).
- LICENSE-binary manifests updated with the pinned OTel 1.50.0 jars.

Tests: OtelInitSpec, TexeraOtelLogAppenderSpec, LogSanitizerSpec, and
ObservabilityConfigSpec cover endpoint validation, interval clamping,
severity/MDC/exception mapping, self-diagnostic filtering, redaction, and
truncation.

Rebased onto current main.
Build on the logging foundation (PR 1) with the backend emit path: workflow
lifecycle metrics and a run-level setup trace span, plus the tracing/metrics
primitives they use. This is PR 2 of the observability stack.

Primitives (common/observability):
- TexeraTracer: lazy accessor for the process tracer off GlobalOpenTelemetry.
- SpanAttrs: typed AttributeKey constants so span attributes use standard keys
  rather than ad hoc strings.
- WorkflowMetrics: the OTel instruments (start/completion/failure/cancellation
  counters and run-duration histogram) keyed by workflow kind.
- TraceparentValidator: validates W3C traceparent headers before use.

Emit path (amber):
- WorkflowMetricsRecorder: single owner of the metric instruments. init() wires
  them once; onStart stamps a run's start; onStateChange records terminal
  counters and duration exactly once on the first transition into a terminal
  state (idempotent, safe to call on every transition).
- WorkflowService.initExecutionService runs inside a run-level setup span so
  setup-path logs carry its trace id. The span covers only the synchronous
  setup; synchronous setup failures are recorded on it in the catch block, and
  the span is ended in finally. The async errorHandler deliberately does not
  touch the span: it is invoked after setup returns and the span has ended, so
  the failure is surfaced through the metadata store instead.
- ExecutionStateStore.updateWorkflowState is the single chokepoint that feeds
  every state transition to WorkflowMetricsRecorder.onStateChange.
- ComputingUnitMaster initializes the recorder at startup.

All observability sources live under common/observability (the module from
PR 1); the tracing/metrics classes are not duplicated into common/config.

Tests: WorkflowMetricsSpec, SpanAttrsSpec, and TraceparentValidatorSpec.

Review follow-ups addressed: span attributes use SpanAttrs keys instead of
plain strings; the error handler no longer records onto a span that may have
already ended (documented and handled via the metadata store).

Stacked on PR 1 (obs/pr1/foundations).
Add the local deployment wiring that receives and stores the telemetry
emitted by PR 2: an OpenTelemetry Collector plus logs, metrics, traces,
and profiles backends, for both the single-node docker-compose stack and
the Kubernetes Helm chart. Infrastructure only; the application runs
unchanged whether or not these services are started. This is PR 3 of the
observability stack.

Single-node compose (bin/single-node, bin/observability):
- otel-collector as the single OTLP ingress, routing to VictoriaLogs
  (logs), VictoriaMetrics (metrics), and Jaeger (traces); the collector
  config caps OTLP message size and bounds memory via memory_limiter.
- Parca server + eBPF agent for continuous profiling, configured in
  bin/observability/parca.
- Each signal is its own compose profile and is OFF by default:
  COMPOSE_PROFILES is empty, so observability is opt-in. docker compose
  reads COMPOSE_PROFILES natively via the canonical launcher.
- Emission is gated by OTEL_SDK_DISABLED (default disabled); nothing is
  emitted unless an operator opts in alongside the collector profile.

Kubernetes Helm chart (bin/k8s):
- Templates for the collector, Jaeger, VictoriaLogs, and VictoriaMetrics
  under templates/base/observability, plus values.yaml knobs and an
  install.sh helper.
- Service deployments gain the OTLP endpoint wiring.

Every observability port binds to loopback (127.0.0.1) or stays on the
bridge network; no 0.0.0.0 host bindings.

Tests: ObservabilityComposeSpec and ParcaConfigSpec (string-level smoke
tests over the compose, collector, and Parca config, pinning image
versions, the opt-in default, loopback binding, and the no-high-
cardinality-label rule). All 24 pass; sbt scalafmtCheckAll is clean.

Review follow-ups addressed: observability is opt-in rather than on by
default, and the privileged eBPF agent is opt-in with its host-wide scope
documented; removed the non-canonical up.sh launcher so COMPOSE_PROFILES
is the single switch; corrected the Parca README (the agent profiles every
host process, and profiles cannot be joined to traces by trace_id); moved
the local-dev-only OTLP host ports into docker-compose.override.yml so the
base compose stays deploy-clean; the release workflow now ships
bin/observability and rewrites the mount paths so the compose works from
the released single-node bundle; and removed the inter-PR references from
the configs, comments, and specs.

Stacked on PR 2 (obs/pr2/backend-emit).
Add the tenant-scoped read path the dashboard queries, plus the Angular
shell that hosts the per-signal panels. This is the first user-visible
observability surface: it enforces tenancy and rate limiting on every
query and gates each panel on signal reachability. This is PR 4 of the
observability stack.

Query gateway (amber, org.apache.texera.web.observability.gateway):
- BackendClient issues the read-only HTTP queries to the telemetry
  backends (VictoriaLogs, VictoriaMetrics, Jaeger).
- ScopeResolver derives the caller's tenant scope and constrains every
  query to it, so a user never reads another tenant's telemetry.
- RateLimiter, AuditLogger, and GatewayContext add per-request rate
  limiting, audit logging, and the shared request context.
- dtos.scala and builders.scala define the typed request objects and
  their validators (time window, page size, free text, service name).
- ObservabilityResources exposes /observability/health and is
  registered in TexeraWebApplication.
- RequestContextMdcFilter and UserContextMdcFilter inject the request
  and user context into the logging MDC.
- ObservabilityGatewayConfig plus observability-gateway.conf hold the
  gateway settings.

Dashboard shell (frontend):
- Admin-only Observability page, route, and navigation entry, plus the
  observability service, its types, and the traces-pivot service.
- Each tab is guarded by the per-signal reachability check: an
  unreachable signal renders an explicit state rather than a broken
  panel. The signal panels themselves follow in later PRs of the stack.

Tests: backend specs for the gateway core, DTO validation, scope
resolver, rate limiter, and the MDC filters, plus frontend component and
service specs. sbt scalafmtCheckAll is clean.

Stacked on PR 3 (obs/pr3/deployment).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the first complete signal: log search through the query gateway plus
the UI panel that drives it. Builds on the gateway core and dashboard
shell from PR 4. This is PR 5 of the observability stack.

Gateway (amber):
- ResponseParsers turns the log store's responses into the typed results
  the UI consumes (parseLogs, parseLogSources), applying per-field
  redaction so sensitive values never reach the client.
- builders and dtos gain the logs query builder and its validated
  request objects (time window, service, workflow, computing unit,
  execution, level, free text, page size).
- ObservabilityResources exposes the log-search and source-facets
  endpoints, registered in TexeraWebApplication.

Logs panel (frontend):
- A logs panel under the observability page with time-window, service,
  workflow, computing-unit, execution, level, and free-text filters,
  server-side paging, and sources-backed autofill.
- observability-prefs persists the panel's filter choices, and the
  observability component hosts the panel behind its reachability gate.

Tests: backend specs for the logs query builder and the response
parsers, plus the frontend logs-panel and service specs. sbt
scalafmtCheckAll is clean.

Stacked on PR 4 (obs/pr4/gateway-core).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the metrics signal: workflow metrics through the query gateway plus
an ECharts-based UI panel that renders them. Builds on the logs signal
from PR 5. This is PR 6 of the observability stack.

Gateway (amber):
- builders and parseMetrics add the metrics query builder and turn the
  metrics store's responses into typed series.
- ObservabilityResources serves a closed allowlist of named queries
  (throughput, outcome rates, and duration percentiles), enforced on the
  gateway so an arbitrary query can never be run; registered in
  TexeraWebApplication.
- WorkflowRunCounter and the GatewayContext and dtos changes supply the
  run counts and validated request objects the named queries need.

Metrics panel (frontend):
- A metrics panel under the observability page that renders each named
  query as a typed series with Apache ECharts. Chart data is bound as
  typed arrays; backend output is never interpreted as a formatter or
  template.
- The ECharts dependency is added to package.json, yarn.lock, and the
  frontend LICENSE-binary.

Tests: backend specs for the metrics query builder and the parsers, plus
the frontend metrics-panel and service specs. sbt scalafmtCheckAll is
clean.

Stacked on PR 5 (obs/pr5/logs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the traces signal: distributed-trace retrieval through the query
gateway plus a trace-tree UI panel. Builds on the metrics signal from
PR 6. This is PR 7 of the observability stack.

Gateway (amber):
- builders and parseTraces add the trace query builder and turn the
  trace store's responses into the typed spans the UI consumes.
- ObservabilityResources exposes trace retrieval by id, with id
  validation and scope enforcement at the gateway so a caller can only
  read traces within its tenant scope; registered in
  TexeraWebApplication.

Traces panel (frontend):
- A traces panel under the observability page that renders spans as a
  tree built from parent-span relationships, with pivot-from-log so a
  log row can open its trace directly. Span fields are bound with text
  interpolation only, never as markup.

Tests: backend specs for the trace query builder and the parsers, plus
the frontend traces-panel specs, including the span-tree builder. sbt
scalafmtCheckAll is clean.

Stacked on PR 6 (obs/pr6/metrics).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the final signal: continuous-profiling retrieval through the query
gateway plus a flame-graph UI panel. Builds on the traces signal from
PR 7 and completes the observability stack. This is PR 8 of the stack.

Gateway (amber):
- ParcaClient is the gRPC-Web client to the Parca server, with a
  profiles query builder and parseProfiles turning its response into the
  typed flame-graph data the UI consumes.
- ObservabilityResources exposes scoped profile queries (optional
  workflow and execution id), registered in TexeraWebApplication.

Profiles panel (frontend):
- A profiles panel under the observability page with a flame-chart
  component, scoped by workflow and execution id, with explicit empty
  states for "no samples yet" and for environments where profiling is
  disabled. observability-util holds the shared helpers the panels reuse.

Log sanitizer hardening:
- SecretPatterns now also redacts a bare JWT (header.payload.signature)
  and a combined labelled-credential form covering password, secret,
  token, api_key, client_secret, and access_token in key=value, JSON,
  and quoted shapes, with tests for each and a case asserting ordinary
  prose that merely mentions a keyword is left intact.

Tests: backend specs for ParcaClient, the profiles query builder, and
the parsers, plus the frontend profiles-panel and flame-chart specs and
the expanded sanitizer specs. sbt scalafmtCheckAll is clean.

Stacked on PR 7 (obs/pr7/traces).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Ma77Ball

Copy link
Copy Markdown
Contributor Author

Closing as part of scoping the observability work to instrumentation and export only. Per maintainer feedback on #5378, Texera will export OpenTelemetry data to external tools (Grafana, OpenSearch, Jaeger, etc.) instead of shipping its own query gateway and dashboard UI. The retained work is in #5375, #5376, #5377 (issues #5367, #5368, #5369).

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

Labels

ci changes related to CI common dependencies Pull requests that update a dependency file docs Changes related to documentations engine frontend Changes related to the frontend GUI infra platform Non-amber Scala service paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Observability] Continuous profiling: gateway endpoint and flame-graph panel

2 participants