Skip to content

feat: PromEx plugin (Task 21)#7

Merged
e-fu merged 3 commits into
developmentfrom
task-21-promex-plugin
May 17, 2026
Merged

feat: PromEx plugin (Task 21)#7
e-fu merged 3 commits into
developmentfrom
task-21-promex-plugin

Conversation

@e-fu
Copy link
Copy Markdown
Contributor

@e-fu e-fu commented May 17, 2026

Summary

Ship-with-the-library PromEx plugin that maps every [:ccxt_ocx, ...] event in CcxtOcx.Telemetry to Prometheus metrics with zero glue. Consumers add {:prom_ex, "~> 1.11"} to their own deps and reference CcxtOcx.PromEx.Plugin in their PromEx config; the dep is declared optional: true here 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 via pool: / poll_rate: opts; drives CcxtOcx.RuntimePool.memory/1 on a timer whose emissions are captured by the event metrics above.
  • Tag normalizers default missing metadata keys to "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 moduledoc
  • mix.exs{:prom_ex, optional: true}; :bandit extended to :test so PromEx.Plug compiles; :telemetry_metrics added to dialyzer plt_add_apps
  • README.md — "Observability — PromEx" section
  • test/ccxt_ocx/prom_ex/plugin_test.exs (new, 12 tests)
  • CHANGELOG.md, roadmap/tasks.toml, ROADMAP.md, roadmap/data.json

Test plan

  • mix test.json --quiet test/ccxt_ocx/prom_ex/plugin_test.exs — 12/12 pass
  • Full suite: 156/156 offline tests pass (15 :integration/:network excluded by project default)
  • Plugin module coverage: 96.15% (only the use PromEx.Plugin macro line uncovered)
  • mix format --check-formatted — clean
  • mix credo --strict — only pre-existing TODO tags in untouched files (expected exit 2)
  • mix dialyzer.json --quiet — 0 warnings after adding :telemetry_metrics to plt_add_apps
  • mix doctor — 100% doc / 100% moduledoc / 100% spec coverage

Notes

This is Scope A of a two-PR pair (Task 21 → PromEx plugin, Task 22 → CcxtOcx.DevTelemetry dogfooding 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

    • Optional PromEx plugin exposing runtime memory, REST, and WebSocket tick Prometheus metrics.
    • Opt-in polling for periodic memory snapshots.
    • Stable automatic label/tag normalization for consistent metric labels.
  • Documentation

    • README, CHANGELOG, and ROADMAP updated with PromEx integration and Phase 5 task completion.
  • Chores

    • Added optional prom_ex dependency and related build configuration changes.
  • Tests

    • Added tests covering metric definitions, polling behavior, and tag normalization.

Review Change Stack

e-fu added 2 commits May 17, 2026 15:53
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).
Copilot AI review requested due to automatic review settings May 17, 2026 08:13
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5b5b7f54-7fcb-44bd-8a48-73fa7fe6370b

📥 Commits

Reviewing files that changed from the base of the PR and between 2bb4702 and 7ae93db.

📒 Files selected for processing (2)
  • lib/ccxt_ocx/prom_ex/plugin.ex
  • mix.exs
🚧 Files skipped from review as they are similar to previous changes (2)
  • mix.exs
  • lib/ccxt_ocx/prom_ex/plugin.ex

📝 Walkthrough

Walkthrough

This PR completes Phase 5 Task 21 by implementing a PromEx plugin that maps [:ccxt_ocx, ...] telemetry events to Prometheus metrics. The plugin provides always-on metrics for runtime memory, REST call duration and exceptions, and WebSocket inbound ticks, with optional polling support for periodic memory snapshots and stable tag normalization via internal helpers.

Changes

PromEx Plugin Integration

Layer / File(s) Summary
Plugin implementation and metric builders
lib/ccxt_ocx/prom_ex/plugin.ex
Defines CcxtOcx.PromEx.Plugin implementing PromEx callbacks event_metrics/1 and polling_metrics/1. Event metrics include runtime memory (malloc, used, live), REST distribution and counters, and WebSocket tick counter, each with tag normalization via normalize_*_metadata/1 helpers that stringify nil to "none", convert atoms to strings, and inspect other terms. Polling metrics optionally schedule periodic RuntimePool.memory/1 calls at configurable :poll_rate (default 5_000).
Build and test dependencies
mix.exs
Adds optional :prom_ex dependency for compile-time plugin availability without requiring downstream consumers to depend on PromEx. Updates Dialyzer PLT to include :telemetry_metrics, and moves :bandit from only: :dev to only: [:dev, :test] to ensure PromEx test compilation.
Plugin test coverage
test/ccxt_ocx/prom_ex/plugin_test.exs
Tests event_metrics/1 metric shape (runtime/REST/WS groups and measurements), polling_metrics/1 behavior (empty result without :pool, correct group and rate with :pool), and telemetry emission smoke tests validating tag normalization for missing :pool/:phase (runtime), REST exception kind, and WebSocket :exchange/:stream/:type.
Documentation and roadmap tracking
lib/ccxt_ocx/telemetry.ex, CHANGELOG.md, README.md, ROADMAP.md, roadmap/data.json, roadmap/tasks.toml
Extends telemetry contract documentation with reserved REST and WS event categories. Adds PromEx section to README showing consumer configuration and enumerating provided Prometheus metrics. Updates CHANGELOG for Task 21 completion. Marks Task 21 complete in ROADMAP and documents task details including acceptance criteria, dependency requirements (:prom_ex optional, :plug in test), and test coverage scope.

Sequence Diagram

sequenceDiagram
  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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ZenHive/ccxt_ocx#6: The PromEx plugin directly consumes the telemetry event contract and metadata structure introduced in that PR.

Poem

🐰 A PromEx plugin hops into view,
Mapping telemetry events to metrics true—
Runtime, REST, and WebSocket streams now shine,
Tags normalized so labels align fine,
🥕 Hooray for observability!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: PromEx plugin (Task 21)' clearly and concisely summarizes the main change: adding a PromEx plugin implementation that is part of Task 21, directly matching the substantial new module and feature additions in the changeset.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-21-promex-plugin

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread lib/ccxt_ocx/prom_ex/plugin.ex Outdated
and exceptions are normalized to `:kind` (`:error | :exit | :throw`).
"""

use PromEx.Plugin
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard optional PromEx use

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 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.Plugin with event-based metric groups (runtime/rest/ws) and optional polling via pool: / 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.

Comment thread lib/ccxt_ocx/prom_ex/plugin.ex Outdated
and exceptions are normalized to `:kind` (`:error | :exit | :throw`).
"""

use PromEx.Plugin
Comment thread lib/ccxt_ocx/prom_ex/plugin.ex Outdated
unit: :byte
),
last_value(
[:ccxt_ocx, :runtime, :memory, :used, :bytes],
Comment thread mix.exs
Comment on lines +53 to +56
# 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.
@e-fu e-fu merged commit 1a3f896 into development May 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants