Export BEAM processes to OTLP metrics endpoint#6188
Conversation
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
📝 WalkthroughWalkthroughThis pull request introduces experimental OpenTelemetry BEAM metrics collection. The changes add runtime configuration for conditional metrics export, a new BeamMetrics module that registers observable gauge instruments for monitoring BEAM processes, conditional initialization during application startup, and three new dependencies to support the functionality. Changes
Sequence DiagramsequenceDiagram
participant App as Plausible.Application
participant OTel as OpenTelemetry
participant BM as BeamMetrics
participant Recon as recon (BEAM)
participant Exporter as OTLP Exporter
App->>BM: setup_opentelemetry() calls setup()
BM->>OTel: Create instrumentation scope & meter
BM->>OTel: Register 3 observable gauges (memory, reductions, msg_queue)
BM->>OTel: Register callback with meter
BM-->>App: Return :ok
loop Collection Cycle (every interval_ms)
OTel->>BM: Invoke registered callback
BM->>Recon: collect_observations() calls recon.proc_count()
Recon-->>BM: Return top N processes
BM->>BM: Filter valid processes & build attributes
BM-->>OTel: Return {value, attributes} observations
OTel->>Exporter: Export metric batch
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
lib/plausible/open_telemetry/beam_metrics.ex (2)
64-70: Consider simplifying the return structure.The
flat_mapwith a single-element list can be simplified toEnum.map:♻️ Simplified implementation
def observe_top_processes(_callback_args) do - Enum.flat_map(`@metrics`, fn metric -> + Enum.map(`@metrics`, fn metric -> {gauge_name, _opts} = Map.fetch!(`@instruments`, metric) observations = collect_observations(metric) - [{gauge_name, observations}] + {gauge_name, observations} end) end🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/plausible/open_telemetry/beam_metrics.ex` around lines 64 - 70, The observe_top_processes function is using Enum.flat_map to return single-item lists; replace the flat_map with Enum.map so each metric maps directly to a tuple instead of a single-element list. In the observe_top_processes function, iterate over `@metrics` with Enum.map, fetch the instrument via Map.fetch!(`@instruments`, metric) to get gauge_name, call collect_observations(metric) for observations, and return {gauge_name, observations} for each entry (preserving the current tuple shape used elsewhere).
94-102: Numeric metric values are converted to strings in attributes.The
memory,reductions, andmessage_queue_lenvalues are converted to strings in the attributes map. While these values are already captured as the gauge observation value, including them as string attributes may affect:
- Query capabilities: Numeric attributes enable range queries and aggregations in observability backends
- Storage efficiency: String representation uses more storage
If the string conversion is intentional (e.g., for label-based filtering in Grafana), this is fine. Otherwise, consider keeping them as integers or omitting them from attributes since they're already the observation values.
♻️ Keep numeric attributes as integers
%{ "beam.process.pid" => inspect(pid), "beam.process.registered_name" => registered_name, "beam.process.current_function" => current_function, - "beam.process.initial_call" => initial_call, - "beam.process.memory" => to_string(Keyword.get(info, :memory, 0)), - "beam.process.reductions" => to_string(Keyword.get(info, :reductions, 0)), - "beam.process.message_queue_len" => to_string(Keyword.get(info, :message_queue_len, 0)) + "beam.process.initial_call" => initial_call }Or keep as integers if needed for cross-referencing:
- "beam.process.memory" => to_string(Keyword.get(info, :memory, 0)), - "beam.process.reductions" => to_string(Keyword.get(info, :reductions, 0)), - "beam.process.message_queue_len" => to_string(Keyword.get(info, :message_queue_len, 0)) + "beam.process.memory" => Keyword.get(info, :memory, 0), + "beam.process.reductions" => Keyword.get(info, :reductions, 0), + "beam.process.message_queue_len" => Keyword.get(info, :message_queue_len, 0)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/plausible/open_telemetry/beam_metrics.ex` around lines 94 - 102, The attributes map in lib/plausible/open_telemetry/beam_metrics.ex currently converts numeric values to strings for "beam.process.memory", "beam.process.reductions", and "beam.process.message_queue_len"; update the map so these attributes remain numeric (or remove them entirely if redundant with the gauge observation) by replacing the to_string(Keyword.get(info, :memory, 0)), to_string(Keyword.get(info, :reductions, 0)), and to_string(Keyword.get(info, :message_queue_len, 0)) entries with their integer values (e.g., Keyword.get(info, :memory, 0) etc.) or drop the keys, ensuring this change is applied where the attributes map is constructed in the BeamMetrics module so observability backends can treat them as numbers.mix.exs (1)
112-123: Git-based experimental dependencies pinned to a specific commit.Using experimental OpenTelemetry packages from git with a pinned ref is reasonable for this feature, but be aware:
- These packages are explicitly "experimental" and may have breaking changes
- The
override: trueflag can mask version conflicts with other OpenTelemetry dependencies- Updates require manually tracking the upstream repository
Consider documenting the rationale for this specific commit ref and establishing a process for periodic updates.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mix.exs` around lines 112 - 123, The git-pinned experimental OpenTelemetry deps (opentelemetry_api_experimental and opentelemetry_experimental) currently pin a specific ref and use override: true without explanation; update mix.exs to add a concise comment just above these dependency entries that documents why this exact commit/ref is used, the risks (experimental/breaking changes and override masking), and a defined update cadence or owner responsible for tracking upstream (e.g., add a TODO with cadence and contact), and either justify or remove the override: true setting for these deps in that comment so future maintainers know whether the override is intentional.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@lib/plausible/open_telemetry/beam_metrics.ex`:
- Around line 64-70: The observe_top_processes function is using Enum.flat_map
to return single-item lists; replace the flat_map with Enum.map so each metric
maps directly to a tuple instead of a single-element list. In the
observe_top_processes function, iterate over `@metrics` with Enum.map, fetch the
instrument via Map.fetch!(`@instruments`, metric) to get gauge_name, call
collect_observations(metric) for observations, and return {gauge_name,
observations} for each entry (preserving the current tuple shape used
elsewhere).
- Around line 94-102: The attributes map in
lib/plausible/open_telemetry/beam_metrics.ex currently converts numeric values
to strings for "beam.process.memory", "beam.process.reductions", and
"beam.process.message_queue_len"; update the map so these attributes remain
numeric (or remove them entirely if redundant with the gauge observation) by
replacing the to_string(Keyword.get(info, :memory, 0)),
to_string(Keyword.get(info, :reductions, 0)), and to_string(Keyword.get(info,
:message_queue_len, 0)) entries with their integer values (e.g.,
Keyword.get(info, :memory, 0) etc.) or drop the keys, ensuring this change is
applied where the attributes map is constructed in the BeamMetrics module so
observability backends can treat them as numbers.
In `@mix.exs`:
- Around line 112-123: The git-pinned experimental OpenTelemetry deps
(opentelemetry_api_experimental and opentelemetry_experimental) currently pin a
specific ref and use override: true without explanation; update mix.exs to add a
concise comment just above these dependency entries that documents why this
exact commit/ref is used, the risks (experimental/breaking changes and override
masking), and a defined update cadence or owner responsible for tracking
upstream (e.g., add a TODO with cadence and contact), and either justify or
remove the override: true setting for these deps in that comment so future
maintainers know whether the override is intentional.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 06e4b32d-a049-46dd-a503-240fe9b8c395
⛔ Files ignored due to path filters (1)
mix.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
config/runtime.exslib/plausible/application.exlib/plausible/open_telemetry/beam_metrics.exmix.exs
Vibed dashboard: https://plausible.grafana.net/d/beam-process-metrics/beam-process-metrics?orgId=1&from=now-1h&to=now&timezone=browser&refresh=30s&var-interval_s=10&var-top_n=20&var-process=$__all&var-namespace=plausible-app-6188
about pinned deps
opentelemetry_*_experimental: