feat: PromEx plugin (Task 21)#7
Conversation
Closes Task 21. Tests cover plugin shape (3 Event groups, 1 conditional Polling group), polling gating, custom poll rates, and live-emission tag normalization. README gains an "Observability — PromEx" section with the consumer config snippet. `:telemetry_metrics` added to dialyzer plt_add_apps so the optional :prom_ex dep type-checks cleanly. Plugin coverage: 96.15% (12/12 tests pass). Harness clean: format, credo (TODO-tagged debt only in untouched files), dialyzer (0 warnings), doctor (100/100/100).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR completes Phase 5 Task 21 by implementing a PromEx plugin that maps ChangesPromEx Plugin Integration
Sequence DiagramsequenceDiagram
participant App as CcxtOcx Application
participant Telemetry as [:ccxt_ocx] Events
participant PromExPlugin as PromEx Plugin
participant Metrics as Prometheus Metrics
App->>Telemetry: emit [:ccxt_ocx, :runtime, :memory] event
Telemetry->>PromExPlugin: event_metrics/1 listener
PromExPlugin->>PromExPlugin: normalize_memory_metadata/1
PromExPlugin->>Metrics: record last_value (malloc, used, live)
App->>Telemetry: emit [:ccxt_ocx, :rest, :stop] event
Telemetry->>PromExPlugin: duration distribution
PromExPlugin->>PromExPlugin: normalize_rest_metadata/1
PromExPlugin->>Metrics: record duration + call counter
opt polling_metrics configured
PromExPlugin->>App: periodic RuntimePool.memory/1 call
App->>Telemetry: emit runtime event (polling)
Telemetry->>Metrics: update memory metrics
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bb470214c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| and exceptions are normalized to `:kind` (`:error | :exit | :throw`). | ||
| """ | ||
|
|
||
| use PromEx.Plugin |
There was a problem hiding this comment.
Because mix.exs declares :prom_ex as optional, downstream projects that depend only on :ccxt_ocx do not fetch or compile PromEx. This module is still compiled from lib/, and the unguarded use PromEx.Plugin expands at compile time, so those consumers fail to compile ccxt_ocx even though they did not opt into PromEx. Wrap the module definition behind a Code.ensure_loaded?(PromEx.Plugin) guard or otherwise keep it out of the normal compile path when PromEx is absent.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds a first-party PromEx plugin to expose CcxtOcx.Telemetry events as Prometheus metrics (with optional polling to drive pool memory snapshots), plus docs/roadmap updates to describe and track the feature.
Changes:
- Introduces
CcxtOcx.PromEx.Pluginwith event-based metric groups (runtime/rest/ws) and optional polling viapool:/poll_rate:. - Adds PromEx as an optional dependency, adjusts Dialyzer PLT apps, and updates docs (Telemetry moduledoc + README) and changelog/roadmap artifacts.
- Adds a new ExUnit test module covering metric group shapes, polling gating, and tag normalization behavior.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
lib/ccxt_ocx/prom_ex/plugin.ex |
New PromEx plugin defining event metrics, polling driver, and tag normalization. |
test/ccxt_ocx/prom_ex/plugin_test.exs |
New tests validating plugin metric definitions and tag normalization. |
lib/ccxt_ocx/telemetry.ex |
Adds PromEx plugin cross-reference in moduledoc. |
mix.exs |
Adds optional :prom_ex, extends :bandit to :test, and adds :telemetry_metrics to Dialyzer PLT apps. |
mix.lock |
Locks new PromEx-related dependencies. |
README.md |
Documents reserved telemetry events and adds “Observability — PromEx” section with consumer config + metrics list. |
CHANGELOG.md |
Records completion of Task 21 and summarizes delivered functionality. |
roadmap/tasks.toml |
Adds Task 21 entry as done with acceptance criteria. |
roadmap/data.json |
Mirrors Task 21 roadmap data. |
ROADMAP.md |
Marks Task 21 as completed in the roadmap table. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| and exceptions are normalized to `:kind` (`:error | :exit | :throw`). | ||
| """ | ||
|
|
||
| use PromEx.Plugin |
| unit: :byte | ||
| ), | ||
| last_value( | ||
| [:ccxt_ocx, :runtime, :memory, :used, :bytes], |
| # Optional PromEx plugin (Task 21 — CcxtOcx.PromEx.Plugin). | ||
| # Consumers add prom_ex to their own deps; we only need it present | ||
| # at compile time so `use PromEx.Plugin` expands cleanly. | ||
| {:prom_ex, "~> 1.11", optional: true}, |
Wraps CcxtOcx.PromEx.Plugin in `if Code.ensure_loaded?(PromEx.Plugin)` so downstream projects that don't depend on :prom_ex still compile ccxt_ocx cleanly — the module simply isn't defined for them. The existing `optional: true` flag in mix.exs only stops Mix from fetching the dep; it does NOT stop `use PromEx.Plugin` from failing to expand when the consumer recompiles ccxt_ocx without :prom_ex on the path. Also renames one metric path segment `:used` → `:used_size` so the exported series matches the moduledoc + README claim of `ccxt_ocx_runtime_memory_used_size_bytes` (was emitting `_used_bytes`). Both flagged on PR #7 by Codex (P1) and Copilot.
Summary
Ship-with-the-library PromEx plugin that maps every
[:ccxt_ocx, ...]event inCcxtOcx.Telemetryto Prometheus metrics with zero glue. Consumers add{:prom_ex, "~> 1.11"}to their own deps and referenceCcxtOcx.PromEx.Pluginin their PromEx config; the dep is declaredoptional: truehere so consumers who don't want PromEx aren't forced into it.event_metrics/1— covers runtime memory (live today via Task 14), REST (Phase 2-reserved), WS tick (Phase 3-reserved). Reserved metrics register as zero-count series so dashboards stay stable when emission lights up.polling_metrics/1— opt-in viapool:/poll_rate:opts; drivesCcxtOcx.RuntimePool.memory/1on a timer whose emissions are captured by the event metrics above."none", stringify atoms, inspect PIDs — Prometheus labels stay stable across emission sites.Touched
lib/ccxt_ocx/prom_ex/plugin.ex(new)lib/ccxt_ocx/telemetry.ex— PromEx cross-reference in moduledocmix.exs—{:prom_ex, optional: true};:banditextended to:testsoPromEx.Plugcompiles;:telemetry_metricsadded to dialyzerplt_add_appsREADME.md— "Observability — PromEx" sectiontest/ccxt_ocx/prom_ex/plugin_test.exs(new, 12 tests)CHANGELOG.md,roadmap/tasks.toml,ROADMAP.md,roadmap/data.jsonTest plan
mix test.json --quiet test/ccxt_ocx/prom_ex/plugin_test.exs— 12/12 passuse PromEx.Pluginmacro line uncovered)mix format --check-formatted— cleanmix credo --strict— only pre-existing TODO tags in untouched files (expected exit 2)mix dialyzer.json --quiet— 0 warnings after adding:telemetry_metricstoplt_add_appsmix doctor— 100% doc / 100% moduledoc / 100% spec coverageNotes
This is Scope A of a two-PR pair (Task 21 → PromEx plugin, Task 22 →
CcxtOcx.DevTelemetrydogfooding loop, planned next). The macro-generated callers landing in Phases 2/3 will emit into the same event contract; no plugin changes needed when emission lights up.Summary by CodeRabbit
New Features
Documentation
Chores
Tests