Skip to content
This repository was archived by the owner on Jan 2, 2026. It is now read-only.

Commit 7a0ee7f

Browse files
committed
docs: update observability documentation and tooling
- Fix docker-compose.yml: document correct env vars (MEMORY_PLUGIN_OTLP_*) - Add SSRF override documentation (MEMORY_PLUGIN_OTLP_ALLOW_INTERNAL) - Add --export flag to metrics.py for manual OTLP export - Update metrics.md command help with export option - Update CLAUDE.md observability section
1 parent b3d9306 commit 7a0ee7f

4 files changed

Lines changed: 76 additions & 4 deletions

File tree

CLAUDE.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,33 @@ def capture_service(tmp_path, monkeypatch):
253253
| `SECRETS_FILTER_AUDIT_ENABLED` | Enable audit logging | `true` |
254254
| `SECRETS_FILTER_AUDIT_DIR` | Audit log directory | `~/.local/share/memory-plugin/audit/` |
255255

256+
### Observability Configuration (OTLP)
257+
258+
| Variable | Description | Default |
259+
|----------|-------------|---------|
260+
| `MEMORY_PLUGIN_OTLP_ENDPOINT` | OTLP HTTP endpoint (e.g., `http://localhost:4318`) | (none) |
261+
| `MEMORY_PLUGIN_OTLP_ALLOW_INTERNAL` | Allow internal/localhost endpoints (SEC-H-001 SSRF override) | `false` |
262+
263+
To enable observability with the local Docker stack:
264+
265+
```bash
266+
# Add to ~/.bashrc or ~/.zshrc for persistence
267+
export MEMORY_PLUGIN_OTLP_ENDPOINT=http://localhost:4318
268+
export MEMORY_PLUGIN_OTLP_ALLOW_INTERNAL=true
269+
```
270+
271+
**Note**: The `ALLOW_INTERNAL` flag is required because the OTLP exporter has SSRF protection (SEC-H-001) that blocks localhost/private IPs by default. This is a security feature for production environments.
272+
273+
Start the observability stack with:
274+
```bash
275+
cd docker && docker compose up -d
276+
```
277+
278+
Access dashboards:
279+
- **Grafana**: http://localhost:3000 (admin/admin) - Memory Operations and Hook Performance dashboards
280+
- **Prometheus**: http://localhost:9090 - Direct metrics queries
281+
- **Tempo traces**: Access via Grafana → Explore → Tempo datasource (no direct web UI)
282+
256283
### Remote Sync (Team Collaboration)
257284

258285
For team environments where multiple developers share memories:

commands/metrics.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
description: Display observability metrics for the memory system
3-
argument-hint: "[--format=text|json|prometheus] [--filter=<pattern>]"
3+
argument-hint: "[--format=text|json|prometheus] [--filter=<pattern>] [--export]"
44
allowed-tools: ["Bash", "Read"]
55
---
66

@@ -19,7 +19,7 @@ NAME
1919
metrics - Display observability metrics for the memory system
2020
2121
SYNOPSIS
22-
/memory:metrics [--format=text|json|prometheus] [--filter=<pattern>]
22+
/memory:metrics [--format=text|json|prometheus] [--filter=<pattern>] [--export]
2323
2424
DESCRIPTION
2525
Display collected observability metrics including counters, histograms, and gauges.
@@ -29,6 +29,7 @@ OPTIONS
2929
--help, -h Show this help message
3030
--format=FORMAT Output format: text (default), json, prometheus
3131
--filter=PATTERN Filter metrics by name pattern (e.g., "capture", "hook")
32+
--export Export metrics/traces to OTLP endpoint (for Grafana)
3233
3334
EXAMPLES
3435
/memory:metrics

docker/docker-compose.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@
88
# - Prometheus: http://localhost:9090
99
# - OTEL Collector: localhost:4317 (gRPC), localhost:4318 (HTTP)
1010
#
11-
# To send metrics from the plugin:
12-
# export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
11+
# To send metrics/traces from the plugin, set these environment variables:
12+
# export MEMORY_PLUGIN_OTLP_ENDPOINT=http://localhost:4318
13+
# export MEMORY_PLUGIN_OTLP_ALLOW_INTERNAL=true
14+
#
15+
# The ALLOW_INTERNAL flag is required because the OTLP exporter has SSRF
16+
# protection that blocks localhost/private IPs by default (SEC-H-001).
17+
#
18+
# For persistent configuration, add to your shell profile (~/.bashrc, ~/.zshrc):
19+
# # git-notes-memory observability
20+
# export MEMORY_PLUGIN_OTLP_ENDPOINT=http://localhost:4318
21+
# export MEMORY_PLUGIN_OTLP_ALLOW_INTERNAL=true
1322

1423
services:
1524
# OpenTelemetry Collector - receives metrics/traces from the plugin

scripts/metrics.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ def parse_args() -> argparse.Namespace:
3232
default=None,
3333
help="Filter metrics by name pattern",
3434
)
35+
parser.add_argument(
36+
"--export",
37+
action="store_true",
38+
help="Export metrics to OTLP endpoint (requires MEMORY_PLUGIN_OTLP_ENDPOINT)",
39+
)
3540
return parser.parse_args()
3641

3742

@@ -40,12 +45,42 @@ def main() -> int:
4045
args = parse_args()
4146
format_type = args.format
4247
filter_pattern = args.filter
48+
do_export = args.export
4349

4450
# Import after parsing to avoid slow imports if --help is used
4551
from git_notes_memory.observability.metrics import get_metrics
4652

4753
metrics = get_metrics()
4854

55+
# Handle OTLP export if requested
56+
if do_export:
57+
from git_notes_memory.observability.exporters.otlp import (
58+
export_metrics_if_configured,
59+
export_traces_if_configured,
60+
get_otlp_exporter,
61+
)
62+
from git_notes_memory.observability.tracing import get_completed_spans
63+
64+
exporter = get_otlp_exporter()
65+
if not exporter.enabled:
66+
print(
67+
"OTLP export not configured. Set environment variables:\n"
68+
" export MEMORY_PLUGIN_OTLP_ENDPOINT=http://localhost:4318\n"
69+
" export MEMORY_PLUGIN_OTLP_ALLOW_INTERNAL=true",
70+
file=sys.stderr,
71+
)
72+
return 1
73+
74+
# Export metrics
75+
metrics_ok = export_metrics_if_configured()
76+
77+
# Export any pending traces
78+
spans = get_completed_spans()
79+
traces_ok = export_traces_if_configured(spans) if spans else True
80+
81+
print(f"OTLP export: metrics={'OK' if metrics_ok else 'FAILED'}, traces={'OK' if traces_ok else 'FAILED'} ({len(spans)} spans)")
82+
return 0 if (metrics_ok and traces_ok) else 1
83+
4984
if format_type == "json":
5085
output = metrics.export_json()
5186
if filter_pattern:

0 commit comments

Comments
 (0)