Skip to content

Commit f6b92d1

Browse files
author
savathoon
authored
Metrics Plugin + Datadog Example Metrics Plugin (#302)
1 parent 162d023 commit f6b92d1

File tree

8 files changed

+420
-2
lines changed

8 files changed

+420
-2
lines changed

api/plugins/conditional_access.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
hookspec = pluggy.HookspecMarker(conditional_access_plugin_name)
1313
hookimpl = pluggy.HookimplMarker(conditional_access_plugin_name)
1414

15-
_cached_conditional_access_hook = None
15+
_cached_conditional_access_hook: pluggy.HookRelay | None = None
1616

1717
logger = logging.getLogger(__name__)
1818

api/plugins/metrics_reporter.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import logging
2+
import sys
3+
from typing import ContextManager, Dict, List, Optional
4+
5+
import pluggy
6+
7+
metrics_reporter_plugin_name = "access_metrics_reporter"
8+
hookspec = pluggy.HookspecMarker(metrics_reporter_plugin_name)
9+
hookimpl = pluggy.HookimplMarker(metrics_reporter_plugin_name)
10+
11+
_cached_metrics_reporter_hook: Optional[pluggy.HookRelay] = None
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class MetricsReporterPluginSpec:
17+
@hookspec
18+
def record_counter(
19+
self,
20+
metric_name: str,
21+
value: float = 1.0,
22+
tags: Optional[Dict[str, str]] = None,
23+
monotonic: bool = True,
24+
) -> None:
25+
"""
26+
Record a counter metric value.
27+
28+
Args:
29+
metric_name: The metric name
30+
value: The value to add (default 1.0)
31+
tags: Optional tags
32+
monotonic: If True, counter only increases. If False, can decrease.
33+
"""
34+
35+
@hookspec
36+
def record_gauge(
37+
self,
38+
metric_name: str,
39+
value: float,
40+
tags: Optional[Dict[str, str]] = None,
41+
) -> None:
42+
"""Record a gauge metric value (snapshot/current value)."""
43+
44+
@hookspec
45+
def record_histogram(
46+
self,
47+
metric_name: str,
48+
value: float,
49+
tags: Optional[Dict[str, str]] = None,
50+
buckets: Optional[List[float]] = None,
51+
) -> None:
52+
"""
53+
Record a value in a histogram/distribution.
54+
55+
Args:
56+
metric_name: The metric name
57+
value: The value to record
58+
tags: Optional tags
59+
buckets: Optional bucket boundaries for the histogram
60+
"""
61+
62+
@hookspec
63+
def record_summary(
64+
self,
65+
metric_name: str,
66+
value: float,
67+
tags: Optional[Dict[str, str]] = None,
68+
) -> None:
69+
"""
70+
Record a value for summary statistics (percentiles, min, max, etc).
71+
This is similar to histogram but may be implemented differently by backends.
72+
"""
73+
74+
@hookspec
75+
def batch_metrics(self) -> ContextManager[None]:
76+
"""
77+
Context manager for batching multiple metric operations.
78+
79+
Returns a context manager that batches metric operations for efficiency.
80+
Particularly useful for HTTP-based backends to reduce network calls.
81+
82+
Example:
83+
with metrics.batch_metrics():
84+
metrics.record_counter("requests", 1)
85+
metrics.record_gauge("queue_size", 42)
86+
metrics.record_histogram("response_time", 0.123)
87+
# All metrics sent in one batch here
88+
"""
89+
return NotImplemented
90+
91+
@hookspec
92+
def set_global_tags(
93+
self,
94+
tags: Dict[str, str],
95+
) -> None:
96+
"""Set global tags to be included with all metrics."""
97+
98+
@hookspec
99+
def flush(self) -> None:
100+
"""Force flush any buffered metrics to the backend."""
101+
102+
103+
def get_metrics_reporter_hook() -> pluggy.HookRelay:
104+
global _cached_metrics_reporter_hook
105+
106+
if _cached_metrics_reporter_hook is not None:
107+
return _cached_metrics_reporter_hook
108+
109+
pm = pluggy.PluginManager(metrics_reporter_plugin_name)
110+
pm.add_hookspecs(MetricsReporterPluginSpec)
111+
112+
# Register the hook wrappers
113+
pm.register(sys.modules[__name__])
114+
115+
count = pm.load_setuptools_entrypoints(metrics_reporter_plugin_name)
116+
logger.debug(f"Count of loaded metrics reporter plugins: {count}")
117+
_cached_metrics_reporter_hook = pm.hook
118+
119+
return _cached_metrics_reporter_hook

api/plugins/notifications.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
hookspec = pluggy.HookspecMarker(notification_plugin_name)
1212
hookimpl = pluggy.HookimplMarker(notification_plugin_name)
1313

14-
_cached_notification_hook = None
14+
_cached_notification_hook: pluggy.HookRelay | None = None
1515

1616
logger = logging.getLogger(__name__)
1717

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Discord Access Datadog Metrics Plugin
2+
3+
This plugin integrates Discord access metrics with Datadog, allowing users to track and monitor access request patterns, approval rates, and system health metrics.
4+
5+
## Installation
6+
7+
Update the Dockerfile used to build the App container includes the following section for installing the metrics plugin before starting gunicorn:
8+
9+
```dockerfile
10+
# Add the specific plugins and install metrics
11+
WORKDIR /app/plugins
12+
ADD ./examples/plugins/metrics_reporter ./metrics_reporter
13+
RUN pip install -r ./metrics_reporter/requirements.txt && pip install ./metrics_reporter
14+
15+
# Reset working directory
16+
WORKDIR /app
17+
18+
ENV FLASK_ENV production
19+
ENV FLASK_APP api.app:create_app
20+
ENV SENTRY_RELEASE $SENTRY_RELEASE
21+
22+
EXPOSE 3000
23+
24+
CMD ["gunicorn", "-w", "4", "-t", "600", "-b", ":3000", "--access-logfile", "-", "api.wsgi:app"]
25+
```
26+
27+
## Build the Docker image, run and test
28+
29+
You may use the original Discord Access container build processes from the primary README.md:
30+
```bash
31+
docker compose up --build
32+
```
33+
34+
Verify metrics collection is working as designed.
35+
36+
## Configuration
37+
38+
The following environment variables need to be configured for the Datadog metrics plugin to work properly:
39+
40+
### Required Environment Variables
41+
42+
- `FLASK_ENV`: Application environment (e.g., `production`, `staging`, `development`)
43+
- Used to determine the environment tag for metrics
44+
- Maps to: `production``prd`, `staging``stg`, other → `dev`
45+
46+
### Optional Environment Variables
47+
48+
- `STATSD_HOST_IP`: IP address of the StatsD/DogStatsD server
49+
- If not set, falls back to `DD_AGENT_HOST`
50+
- Default: `127.0.0.1`
51+
52+
- `DD_AGENT_HOST`: Datadog Agent host address
53+
- Used when `STATSD_HOST_IP` is not provided
54+
- Default: `127.0.0.1`
55+
56+
- `DD_DOGSTATSD_PORT`: Port for DogStatsD communication
57+
- Default: `8125`
58+
59+
## Development
60+
61+
To contribute to the development of this plugin, please follow the standard Git workflow:
62+
63+
1. Fork the repository.
64+
2. Create a new branch for your feature or bug fix.
65+
3. Make your changes and commit them.
66+
4. Push your branch and create a pull request.
67+
68+
## License
69+
70+
This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details.
71+

examples/plugins/datadog_metrics_reporter/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)