Metrics
Implementations of the `MetricsPort` interface for observability and monitoring.
Metrics Adapters
Implementations of the MetricsPort interface for observability and monitoring.
Purpose
Provides metrics adapters for recording application telemetry including API performance, cache effectiveness, and rate limit detection. Implements hexagonal architecture (FR-034) for swappable external dependencies.
Key Concepts
- Port Implementation: All metrics adapters implement
MetricsPortfrom domain/ports/metrics - Async Publishing: Non-blocking metric recording
- Rate Limit Detection: Special tracking for ArcGIS API rate limiting (FR-033)
- Batching: Efficient metric publishing in batches
- Error Handling: Failed metrics do not crash the application
Available Adapters
CloudWatchMetrics
AWS CloudWatch metrics adapter for production observability.
Features:
- Automatic batching (max 20 metrics per API call)
- Periodic flushing (every 60 seconds by default)
- Rate limit spike detection with CloudWatch alarms
- Error handling prevents application crashes if CloudWatch unavailable
- Configurable namespace and region
Example:
import { createCloudWatchMetrics } from "@/adapters/metrics/cloudwatch-metrics";
const metrics = createCloudWatchMetrics({
namespace: "NJGeocodingApp",
region: "us-east-1",
enabled: process.env.NODE_ENV === "production",
});
// Record API request
await metrics.recordCount("ApiRequests", 1, [
{ name: "Operation", value: "geocode" },
{ name: "StatusCode", value: "200" },
]);
// Record latency
await metrics.recordTiming("GeocodeDuration", 150, [
{ name: "CacheStatus", value: "miss" },
]);
// Record rate limiting (FR-033)
await metrics.recordRateLimit("arcgis-geocode", "429", {
retryAfter: "60",
endpoint: "/geocode",
});
NullMetrics
No-op metrics adapter for local development and testing.
All operations complete successfully but perform no actions. Allows code to use metrics without requiring external services.
Example:
import { createNullMetrics } from "@/adapters/metrics/null-metrics";
const metrics = createNullMetrics();
// Safe to call - does nothing
await metrics.recordCount("ApiRequests", 1);
await metrics.recordTiming("GeocodeDuration", 150);
Integration with Application
Metrics are injected into adapters and use cases through dependency injection:
import { createCloudWatchMetrics } from "@/adapters";
import { createFetchClient } from "@/adapters";
const metrics = createCloudWatchMetrics({
namespace: "NJGeocodingApp",
region: "us-east-1",
});
const httpClient = createFetchClient({
timeout: 10000,
metrics, // Inject metrics for HTTP tracking
});
Metric Types
Counter Metrics
Cumulative values that increase over time (e.g., request counts, error counts).
Timing Metrics
Duration measurements in milliseconds (e.g., API latency, geocode duration).
Gauge Metrics
Point-in-time values (e.g., cache size, active connections).
Rate Limit Metrics
Special metric for detecting API rate limiting (FR-033). Records both HTTP 429 status codes and redirect-based rate limiting from ArcGIS.
Adding New Metrics Adapters
To add a new metrics adapter (e.g., for Prometheus or Datadog):
- Create new file implementing
MetricsPort - Export factory function
- Add to
src/adapters/index.ts
Example:
import type { MetricsPort, MetricDimension } from "@/domain/ports/metrics";
export class PrometheusMetrics implements MetricsPort {
async recordCount(
name: string,
value: number,
dimensions?: MetricDimension[],
): Promise<void> {
// Push to Prometheus pushgateway
}
// ... implement other methods
}
export function createPrometheusMetrics(config: PrometheusConfig): MetricsPort {
return new PrometheusMetrics(config);
}
Observability Requirements (FR-033)
The metrics adapters support rate limiting observability:
- Track ArcGIS API rate limiting (HTTP 429 and redirects)
- Log to CloudWatch metrics for spike detection
- Enable CloudWatch alarms for operational awareness
- Include metadata (endpoint, retry count, redirect URL)