mockforge-observability 0.1.6

Observability features for MockForge including Prometheus metrics, OpenTelemetry tracing, and recording
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# MockForge Observability

Comprehensive observability features for MockForge including Prometheus metrics, OpenTelemetry tracing, structured logging, and system monitoring.

This crate provides enterprise-grade observability capabilities to monitor MockForge performance, track system health, and debug issues in production environments. Perfect for understanding how your mock servers behave under load and ensuring reliable testing infrastructure.

## Features

- **Prometheus Metrics**: Comprehensive metrics collection with automatic export
- **OpenTelemetry Tracing**: Distributed tracing with Jaeger and OTLP support
- **Structured Logging**: JSON-formatted logs with configurable levels and outputs
- **System Metrics**: CPU, memory, and thread monitoring
- **Flight Recorder**: Request/response recording for debugging
- **Multi-Protocol Support**: Metrics for HTTP, gRPC, WebSocket, and GraphQL
- **Performance Monitoring**: Response times, throughput, and error rates
- **Health Checks**: Built-in health endpoints and status monitoring

## Quick Start

### Basic Metrics Collection

```rust,no_run
use mockforge_observability::prometheus::MetricsRegistry;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize global metrics registry
    let registry = MetricsRegistry::new();

    // Record HTTP request metrics
    registry.record_http_request("GET", "/api/users", 200, 0.045);

    // Record gRPC call metrics
    registry.record_grpc_request("GetUser", 0, 0.032);

    // Export metrics in Prometheus format
    let metrics = registry.export_prometheus().await?;
    println!("{}", metrics);

    Ok(())
}
```

### Structured Logging

```rust,no_run
use mockforge_observability::{init_logging, LoggingConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize structured logging
    let logging_config = LoggingConfig {
        level: "info".to_string(),
        json_format: true,
        file_path: Some("./logs/mockforge.log".to_string()),
        max_file_size_mb: 10,
        max_files: 5,
    };

    init_logging(logging_config)?;

    // Logs will now be structured JSON
    tracing::info!("MockForge server started");
    tracing::error!("Failed to connect to database");

    Ok(())
}
```

### OpenTelemetry Tracing

```rust,no_run
use mockforge_observability::{init_with_otel, OtelTracingConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize OpenTelemetry tracing
    let tracing_config = OtelTracingConfig {
        service_name: "mockforge-server".to_string(),
        jaeger_endpoint: Some("http://localhost:14268/api/traces".to_string()),
        sampling_rate: 1.0,
    };

    init_with_otel(tracing_config).await?;

    // Create spans for request tracing
    let span = tracing::info_span!("http_request", method = "GET", path = "/api/users");
    let _enter = span.enter();

    // Your request handling code here...

    Ok(())
}
```

## Core Components

### Prometheus Metrics

Comprehensive metrics collection with automatic Prometheus export:

```rust,no_run
use mockforge_observability::prometheus::MetricsRegistry;

let registry = MetricsRegistry::new();

// HTTP metrics
registry.record_http_request("GET", "/api/users", 200, 0.045);
registry.record_http_response_size(2048); // bytes

// gRPC metrics
registry.record_grpc_request("GetUser", 0, 0.032); // method, status, duration

// WebSocket metrics
registry.record_websocket_connection();
registry.record_websocket_message(512); // message size

// GraphQL metrics
registry.record_graphql_request("GetUser", true, 0.028); // operation, success, duration

// Connection metrics
registry.record_active_connection();
registry.record_connection_closed();
```

### Available Metrics

#### HTTP Metrics
- `mockforge_http_requests_total{method, path, status}` - Total HTTP requests
- `mockforge_http_request_duration_seconds{method, path}` - Request duration histogram
- `mockforge_http_response_size_bytes` - Response size distribution
- `mockforge_http_active_connections` - Current active connections

#### gRPC Metrics
- `mockforge_grpc_requests_total{method, status}` - Total gRPC requests
- `mockforge_grpc_request_duration_seconds{method}` - gRPC request duration
- `mockforge_grpc_active_streams` - Active gRPC streams

#### WebSocket Metrics
- `mockforge_websocket_connections_total` - Total WebSocket connections
- `mockforge_websocket_active_connections` - Current active WebSocket connections
- `mockforge_websocket_messages_total{direction}` - WebSocket messages sent/received
- `mockforge_websocket_message_size_bytes` - WebSocket message size distribution

#### GraphQL Metrics
- `mockforge_graphql_requests_total{operation, success}` - Total GraphQL requests
- `mockforge_graphql_request_duration_seconds{operation}` - GraphQL request duration
- `mockforge_graphql_errors_total{type}` - GraphQL error count

#### System Metrics
- `mockforge_system_cpu_usage_percent` - CPU usage percentage
- `mockforge_system_memory_usage_bytes` - Memory usage in bytes
- `mockforge_system_threads_total` - Total thread count

### Structured Logging

JSON-formatted logging with configurable outputs:

```rust,no_run
use mockforge_observability::LoggingConfig;

let config = LoggingConfig {
    level: "debug".to_string(),        // error, warn, info, debug, trace
    json_format: true,                 // JSON or human-readable
    file_path: Some("./logs/app.log".to_string()),
    max_file_size_mb: 10,
    max_files: 5,                      // Log rotation
};

// Initialize logging
init_logging(config)?;

// Structured logs with context
tracing::info!(
    method = "GET",
    path = "/api/users",
    status = 200,
    duration_ms = 45,
    "HTTP request completed"
);
```

### OpenTelemetry Tracing

Distributed tracing with multiple backends:

```rust,no_run
use mockforge_observability::OtelTracingConfig;

// Jaeger tracing
let jaeger_config = OtelTracingConfig {
    service_name: "mockforge-api".to_string(),
    jaeger_endpoint: Some("http://localhost:14268/api/traces".to_string()),
    sampling_rate: 0.1, // 10% sampling
};

// OTLP tracing (generic OpenTelemetry protocol)
let otlp_config = OtelTracingConfig {
    service_name: "mockforge-api".to_string(),
    otlp_endpoint: Some("http://otel-collector:4317".to_string()),
    sampling_rate: 1.0,
};

init_with_otel(jaeger_config).await?;
```

### System Metrics Collection

Automatic system resource monitoring:

```rust,no_run
use mockforge_observability::system_metrics::{start_system_metrics_collector, SystemMetricsConfig};

let config = SystemMetricsConfig {
    collection_interval_seconds: 30,  // Collect every 30 seconds
    enabled: true,
};

start_system_metrics_collector(config).await?;
```

## Configuration

### Logging Configuration

```rust,no_run
use mockforge_observability::LoggingConfig;

let logging_config = LoggingConfig {
    level: "info".to_string(),
    json_format: true,
    file_path: Some("/var/log/mockforge.log".to_string()),
    max_file_size_mb: 100,
    max_files: 10,
};
```

### Tracing Configuration

```rust,no_run
use mockforge_observability::OtelTracingConfig;

let tracing_config = OtelTracingConfig {
    service_name: "mockforge-server".to_string(),
    environment: "production".to_string(),
    jaeger_endpoint: Some("http://jaeger:14268/api/traces".to_string()),
    otlp_endpoint: None,
    sampling_rate: 0.5, // 50% sampling
};
```

### Metrics Configuration

Metrics are automatically configured with sensible defaults. Customize via environment variables:

```bash
# Metrics collection
export MOCKFORGE_METRICS_ENABLED=true
export MOCKFORGE_METRICS_PATH=/metrics

# System metrics
export MOCKFORGE_SYSTEM_METRICS_ENABLED=true
export MOCKFORGE_SYSTEM_METRICS_INTERVAL=30
```

## Integration Examples

### HTTP Server with Full Observability

```rust,no_run
use axum::{routing::get, Router, extract::State};
use mockforge_observability::{
    prometheus::MetricsRegistry,
    init_logging,
    init_with_otel,
    LoggingConfig,
    OtelTracingConfig,
};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize observability
    init_logging(LoggingConfig {
        level: "info".to_string(),
        json_format: true,
        ..Default::default()
    })?;

    init_with_otel(OtelTracingConfig {
        service_name: "mockforge-http".to_string(),
        jaeger_endpoint: Some("http://localhost:14268/api/traces".to_string()),
        sampling_rate: 1.0,
    }).await?;

    // Create metrics registry
    let metrics = Arc::new(MetricsRegistry::new());

    // Build application with metrics middleware
    let app = Router::new()
        .route("/api/users", get(get_users))
        .route("/metrics", get(metrics_endpoint))
        .with_state(metrics);

    // Start server
    let addr = "0.0.0.0:3000".parse()?;
    println!("🚀 Server with full observability running at http://{}", addr);
    axum::serve(tokio::net::TcpListener::bind(addr).await?, app).await?;

    Ok(())
}

async fn get_users(State(metrics): State<Arc<MetricsRegistry>>) -> &'static str {
    let start = std::time::Instant::now();

    // Your business logic here...
    let response = "{\"users\": [{\"id\": 1, \"name\": \"Alice\"}]}";

    // Record metrics
    let duration = start.elapsed().as_secs_f64();
    metrics.record_http_request("GET", "/api/users", 200, duration);

    response
}

async fn metrics_endpoint(State(metrics): State<Arc<MetricsRegistry>>) -> String {
    metrics.export_prometheus().await.unwrap_or_default()
}
```

### gRPC Server with Tracing

```rust,no_run
use mockforge_observability::{init_with_otel, OtelTracingConfig};
use tonic::{transport::Server, Request, Response, Status};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize tracing
    init_with_otel(OtelTracingConfig {
        service_name: "mockforge-grpc".to_string(),
        jaeger_endpoint: Some("http://localhost:14268/api/traces".to_string()),
        sampling_rate: 1.0,
    }).await?;

    // Create gRPC server with tracing
    let addr = "0.0.0.0:50051".parse()?;
    let user_service = UserService::default();

    println!("🚀 gRPC server with tracing running at http://{}", addr);

    Server::builder()
        .add_service(UserServiceServer::new(user_service))
        .serve(addr)
        .await?;

    Ok(())
}
```

## Performance Considerations

- **Metrics Overhead**: Minimal performance impact with efficient metric collection
- **Logging Performance**: JSON formatting adds small overhead, file I/O can be async
- **Tracing Sampling**: Use sampling rates to control tracing volume in production
- **System Metrics**: Collection interval can be adjusted based on monitoring needs
- **Memory Usage**: Metrics registries use bounded memory with cleanup mechanisms

## Troubleshooting

### Common Issues

**Metrics not appearing:**
- Check Prometheus scrape configuration
- Verify metrics endpoint is accessible
- Ensure metrics are being recorded before scraping

**Logs not structured:**
- Verify JSON format is enabled in LoggingConfig
- Check log level settings
- Ensure tracing subscriber is properly initialized

**Tracing not working:**
- Verify Jaeger/OTLP endpoint is accessible
- Check service name configuration
- Ensure sampling rate allows traces through

**High memory usage:**
- Adjust log file rotation settings
- Reduce system metrics collection frequency
- Check for metric registry leaks

## Development

### Testing Observability Features

```rust,no_run
#[cfg(test)]
mod tests {
    use super::*;
    use mockforge_observability::prometheus::MetricsRegistry;

    #[tokio::test]
    async fn test_metrics_collection() {
        let registry = MetricsRegistry::new();

        // Record some metrics
        registry.record_http_request("GET", "/test", 200, 0.1);
        registry.record_http_request("POST", "/test", 201, 0.05);

        // Export and verify
        let metrics = registry.export_prometheus().await.unwrap();
        assert!(metrics.contains("mockforge_http_requests_total"));
        assert!(metrics.contains("mockforge_http_request_duration_seconds"));
    }
}
```

### Custom Metrics

```rust,no_run
use prometheus::{register_counter, register_histogram, Counter, Histogram};

// Register custom metrics
lazy_static::lazy_static! {
    static ref CUSTOM_COUNTER: Counter = register_counter!(
        "mockforge_custom_operations_total",
        "Total number of custom operations"
    ).unwrap();

    static ref CUSTOM_HISTOGRAM: Histogram = register_histogram!(
        "mockforge_custom_operation_duration_seconds",
        "Duration of custom operations"
    ).unwrap();
}

// Use custom metrics
CUSTOM_COUNTER.inc();
let _timer = CUSTOM_HISTOGRAM.start_timer(); // Measures until dropped
```

## Examples

See the [examples directory](https://github.com/SaaSy-Solutions/mockforge/tree/main/examples) for complete working examples including:

- Full observability stack setup
- Custom metrics implementation
- Distributed tracing configuration
- Log aggregation patterns
- Performance monitoring dashboards

## Related Crates

- [`mockforge-core`]https://docs.rs/mockforge-core: Core mocking functionality
- [`prometheus`]https://docs.rs/prometheus: Metrics collection library
- [`tracing`]https://docs.rs/tracing: Logging and tracing framework
- [`opentelemetry`]https://docs.rs/opentelemetry: Observability standards

## License

Licensed under MIT OR Apache-2.0