1mod metrics;
75pub mod tracing;
76pub mod propagation;
77#[cfg(feature = "observability")]
78pub mod prometheus;
79#[cfg(feature = "system_info")]
80mod metrics_collector;
81pub mod grafana;
82
83use std::sync::Arc;
84use serde::{Serialize, Deserialize};
85
86pub use tracing::{DMSCTracer, DMSCTraceId, DMSCSpanId, DMSCSpan, DMSCSpanKind, DMSCSpanStatus, DMSCTracingContext, DMSCSamplingStrategy};
87pub use metrics::{DMSCMetricsRegistry, DMSCMetric, DMSCMetricConfig, DMSCMetricType, DMSCWindowStats, DMSCMetricSample};
88pub use propagation::{DMSCTraceContext, DMSCBaggage, DMSCContextCarrier, W3CTracePropagator};
89#[cfg(feature = "system_info")]
90pub use metrics_collector::{DMSCSystemMetricsCollector, DMSCSystemMetrics, DMSCCPUMetrics, DMSCMemoryMetrics, DMSCDiskMetrics, DMSCNetworkMetrics};
91
92use crate::core::{DMSCResult, DMSCServiceContext};
93
94
95#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
101pub struct DMSCObservabilityModule {
102 tracer: Option<Arc<DMSCTracer>>,
104 metrics_registry: Option<Arc<DMSCMetricsRegistry>>,
106 config: DMSCObservabilityConfig,
108}
109
110#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct DMSCObservabilityConfig {
116 pub tracing_enabled: bool,
118 pub metrics_enabled: bool,
120 pub tracing_sampling_rate: f64,
122 pub tracing_sampling_strategy: String,
124 pub metrics_window_size_secs: u64,
126 pub metrics_bucket_size_secs: u64,
128}
129
130impl Default for DMSCObservabilityConfig {
131 fn default() -> Self {
141 Self {
142 tracing_enabled: true,
143 metrics_enabled: true,
144 tracing_sampling_rate: 0.1, tracing_sampling_strategy: "rate".to_string(), metrics_window_size_secs: 300, metrics_bucket_size_secs: 10, }
149 }
150}
151
152#[cfg(feature = "pyo3")]
153#[pyo3::prelude::pymethods]
155impl DMSCObservabilityConfig {
156 #[new]
157 fn py_new() -> Self {
158 Self::default()
159 }
160
161 fn set_tracing_enabled(&mut self, tracing_enabled: bool) {
163 self.tracing_enabled = tracing_enabled;
164 }
165
166 fn get_tracing_enabled(&self) -> bool {
168 self.tracing_enabled
169 }
170
171 fn set_metrics_enabled(&mut self, metrics_enabled: bool) {
173 self.metrics_enabled = metrics_enabled;
174 }
175
176 fn get_metrics_enabled(&self) -> bool {
178 self.metrics_enabled
179 }
180
181 fn set_tracing_sampling_rate(&mut self, tracing_sampling_rate: f64) -> pyo3::PyResult<()>
183 {
184 if tracing_sampling_rate < 0.0 || tracing_sampling_rate > 1.0 {
185 return Err(pyo3::exceptions::PyValueError::new_err("Tracing sampling rate must be between 0.0 and 1.0"));
186 }
187 self.tracing_sampling_rate = tracing_sampling_rate;
188 Ok(())
189 }
190
191 fn get_tracing_sampling_rate(&self) -> f64 {
193 self.tracing_sampling_rate
194 }
195
196 fn set_tracing_sampling_strategy(&mut self, tracing_sampling_strategy: String) {
198 self.tracing_sampling_strategy = tracing_sampling_strategy;
199 }
200
201 fn get_tracing_sampling_strategy(&self) -> String {
203 self.tracing_sampling_strategy.clone()
204 }
205
206 fn set_metrics_window_size_secs(&mut self, metrics_window_size_secs: u64) {
208 self.metrics_window_size_secs = metrics_window_size_secs;
209 }
210
211 fn get_metrics_window_size_secs(&self) -> u64 {
213 self.metrics_window_size_secs
214 }
215
216 fn set_metrics_bucket_size_secs(&mut self, metrics_bucket_size_secs: u64) {
218 self.metrics_bucket_size_secs = metrics_bucket_size_secs;
219 }
220
221 fn get_metrics_bucket_size_secs(&self) -> u64 {
223 self.metrics_bucket_size_secs
224 }
225}
226
227impl Default for DMSCObservabilityModule {
228 fn default() -> Self {
229 Self::new()
230 }
231}
232
233impl DMSCObservabilityModule {
234 pub fn new() -> Self {
240 Self {
241 tracer: None,
242 metrics_registry: None,
243 config: DMSCObservabilityConfig::default(),
244 }
245 }
246
247 pub fn with_config(mut self, config: DMSCObservabilityConfig) -> Self {
257 self.config = config;
258 self
259 }
260
261 fn init_tracing(&mut self) {
265 if self.config.tracing_enabled {
266 tracing::init_tracer(self.config.tracing_sampling_rate);
269 }
270 }
271
272 fn init_metrics(&mut self) {
276 if self.config.metrics_enabled {
277 let registry = Arc::new(DMSCMetricsRegistry::new());
278 self.metrics_registry = Some(registry);
279 }
280 }
281
282 fn create_service_metrics(&self) -> DMSCResult<()> {
300 if let Some(registry) = &self.metrics_registry {
301 let request_duration_config = DMSCMetricConfig {
303 metric_type: DMSCMetricType::Histogram,
304 name: "dms_request_duration_seconds".to_string(),
305 help: "Request duration in seconds".to_string(),
306 buckets: vec![0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0], quantiles: vec![0.5, 0.9, 0.95, 0.99],
308 max_age: std::time::Duration::from_secs(300),
309 age_buckets: 5,
310 };
311
312 let request_duration_metric = Arc::new(DMSCMetric::new(request_duration_config));
313 registry.register(request_duration_metric)?;
314
315 let request_counter_config = DMSCMetricConfig {
317 metric_type: DMSCMetricType::Counter,
318 name: "dms_requests_total".to_string(),
319 help: "Total number of requests".to_string(),
320 buckets: vec![],
321 quantiles: vec![],
322 max_age: std::time::Duration::from_secs(0),
323 age_buckets: 0,
324 };
325
326 let request_counter_metric = Arc::new(DMSCMetric::new(request_counter_config));
327 registry.register(request_counter_metric)?;
328
329 let error_counter_config = DMSCMetricConfig {
331 metric_type: DMSCMetricType::Counter,
332 name: "dms_errors_total".to_string(),
333 help: "Total number of errors".to_string(),
334 buckets: vec![],
335 quantiles: vec![],
336 max_age: std::time::Duration::from_secs(0),
337 age_buckets: 0,
338 };
339
340 let error_counter_metric = Arc::new(DMSCMetric::new(error_counter_config));
341 registry.register(error_counter_metric)?;
342
343 let connections_gauge_config = DMSCMetricConfig {
345 metric_type: DMSCMetricType::Gauge,
346 name: "dms_active_connections".to_string(),
347 help: "Number of active connections".to_string(),
348 buckets: vec![],
349 quantiles: vec![],
350 max_age: std::time::Duration::from_secs(0),
351 age_buckets: 0,
352 };
353
354 let connections_gauge_metric = Arc::new(DMSCMetric::new(connections_gauge_config));
355 registry.register(connections_gauge_metric)?;
356
357 let startup_time_config = DMSCMetricConfig {
359 metric_type: DMSCMetricType::Gauge,
360 name: "dms_service_startup_time_seconds".to_string(),
361 help: "Service startup time in seconds".to_string(),
362 buckets: vec![],
363 quantiles: vec![],
364 max_age: std::time::Duration::from_secs(0),
365 age_buckets: 0,
366 };
367
368 let startup_time_metric = Arc::new(DMSCMetric::new(startup_time_config));
369 registry.register(startup_time_metric)?;
370
371 let module_init_config = DMSCMetricConfig {
373 metric_type: DMSCMetricType::Histogram,
374 name: "dms_module_init_time_seconds".to_string(),
375 help: "Module initialization time in seconds".to_string(),
376 buckets: vec![0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0],
377 quantiles: vec![0.5, 0.9, 0.95, 0.99],
378 max_age: std::time::Duration::from_secs(300),
379 age_buckets: 5,
380 };
381
382 let module_init_metric = Arc::new(DMSCMetric::new(module_init_config));
383 registry.register(module_init_metric)?;
384
385 let queue_length_config = DMSCMetricConfig {
387 metric_type: DMSCMetricType::Gauge,
388 name: "dms_request_queue_length".to_string(),
389 help: "Request queue length".to_string(),
390 buckets: vec![],
391 quantiles: vec![],
392 max_age: std::time::Duration::from_secs(0),
393 age_buckets: 0,
394 };
395
396 let queue_length_metric = Arc::new(DMSCMetric::new(queue_length_config));
397 registry.register(queue_length_metric)?;
398
399 let middleware_time_config = DMSCMetricConfig {
401 metric_type: DMSCMetricType::Histogram,
402 name: "dms_middleware_duration_seconds".to_string(),
403 help: "Middleware execution time in seconds".to_string(),
404 buckets: vec![0.001, 0.005, 0.01, 0.05, 0.1, 0.5],
405 quantiles: vec![0.5, 0.9, 0.95, 0.99],
406 max_age: std::time::Duration::from_secs(300),
407 age_buckets: 5,
408 };
409
410 let middleware_time_metric = Arc::new(DMSCMetric::new(middleware_time_config));
411 registry.register(middleware_time_metric)?;
412
413 let cache_hit_config = DMSCMetricConfig {
416 metric_type: DMSCMetricType::Counter,
417 name: "dms_cache_hits_total".to_string(),
418 help: "Total number of cache hits".to_string(),
419 buckets: vec![],
420 quantiles: vec![],
421 max_age: std::time::Duration::from_secs(0),
422 age_buckets: 0,
423 };
424
425 let cache_hit_metric = Arc::new(DMSCMetric::new(cache_hit_config));
426 registry.register(cache_hit_metric)?;
427
428 let cache_miss_config = DMSCMetricConfig {
430 metric_type: DMSCMetricType::Counter,
431 name: "dms_cache_misses_total".to_string(),
432 help: "Total number of cache misses".to_string(),
433 buckets: vec![],
434 quantiles: vec![],
435 max_age: std::time::Duration::from_secs(0),
436 age_buckets: 0,
437 };
438
439 let cache_miss_metric = Arc::new(DMSCMetric::new(cache_miss_config));
440 registry.register(cache_miss_metric)?;
441
442 let cache_entries_config = DMSCMetricConfig {
444 metric_type: DMSCMetricType::Gauge,
445 name: "dms_cache_entries".to_string(),
446 help: "Number of cache entries".to_string(),
447 buckets: vec![],
448 quantiles: vec![],
449 max_age: std::time::Duration::from_secs(0),
450 age_buckets: 0,
451 };
452
453 let cache_entries_metric = Arc::new(DMSCMetric::new(cache_entries_config));
454 registry.register(cache_entries_metric)?;
455
456 let cache_memory_config = DMSCMetricConfig {
458 metric_type: DMSCMetricType::Gauge,
459 name: "dms_cache_memory_usage_bytes".to_string(),
460 help: "Cache memory usage in bytes".to_string(),
461 buckets: vec![],
462 quantiles: vec![],
463 max_age: std::time::Duration::from_secs(0),
464 age_buckets: 0,
465 };
466
467 let cache_memory_metric = Arc::new(DMSCMetric::new(cache_memory_config));
468 registry.register(cache_memory_metric)?;
469
470 let cache_eviction_config = DMSCMetricConfig {
472 metric_type: DMSCMetricType::Counter,
473 name: "dms_cache_evictions_total".to_string(),
474 help: "Total number of cache evictions".to_string(),
475 buckets: vec![],
476 quantiles: vec![],
477 max_age: std::time::Duration::from_secs(0),
478 age_buckets: 0,
479 };
480
481 let cache_eviction_metric = Arc::new(DMSCMetric::new(cache_eviction_config));
482 registry.register(cache_eviction_metric)?;
483
484 let db_query_config = DMSCMetricConfig {
487 metric_type: DMSCMetricType::Histogram,
488 name: "dms_db_query_duration_seconds".to_string(),
489 help: "Database query execution time in seconds".to_string(),
490 buckets: vec![0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0],
491 quantiles: vec![0.5, 0.9, 0.95, 0.99],
492 max_age: std::time::Duration::from_secs(300),
493 age_buckets: 5,
494 };
495
496 let db_query_metric = Arc::new(DMSCMetric::new(db_query_config));
497 registry.register(db_query_metric)?;
498
499 let db_active_connections_config = DMSCMetricConfig {
501 metric_type: DMSCMetricType::Gauge,
502 name: "dms_db_active_connections".to_string(),
503 help: "Number of active database connections".to_string(),
504 buckets: vec![],
505 quantiles: vec![],
506 max_age: std::time::Duration::from_secs(0),
507 age_buckets: 0,
508 };
509
510 let db_active_connections_metric = Arc::new(DMSCMetric::new(db_active_connections_config));
511 registry.register(db_active_connections_metric)?;
512
513 let db_idle_connections_config = DMSCMetricConfig {
515 metric_type: DMSCMetricType::Gauge,
516 name: "dms_db_idle_connections".to_string(),
517 help: "Number of idle database connections".to_string(),
518 buckets: vec![],
519 quantiles: vec![],
520 max_age: std::time::Duration::from_secs(0),
521 age_buckets: 0,
522 };
523
524 let db_idle_connections_metric = Arc::new(DMSCMetric::new(db_idle_connections_config));
525 registry.register(db_idle_connections_metric)?;
526
527 let db_timeout_config = DMSCMetricConfig {
529 metric_type: DMSCMetricType::Counter,
530 name: "dms_db_connection_timeouts_total".to_string(),
531 help: "Total number of database connection timeouts".to_string(),
532 buckets: vec![],
533 quantiles: vec![],
534 max_age: std::time::Duration::from_secs(0),
535 age_buckets: 0,
536 };
537
538 let db_timeout_metric = Arc::new(DMSCMetric::new(db_timeout_config));
539 registry.register(db_timeout_metric)?;
540
541 let db_errors_config = DMSCMetricConfig {
543 metric_type: DMSCMetricType::Counter,
544 name: "dms_db_errors_total".to_string(),
545 help: "Total number of database errors".to_string(),
546 buckets: vec![],
547 quantiles: vec![],
548 max_age: std::time::Duration::from_secs(0),
549 age_buckets: 0,
550 };
551
552 let db_errors_metric = Arc::new(DMSCMetric::new(db_errors_config));
553 registry.register(db_errors_metric)?;
554
555 let db_transactions_config = DMSCMetricConfig {
557 metric_type: DMSCMetricType::Counter,
558 name: "dms_db_transactions_total".to_string(),
559 help: "Total number of database transactions".to_string(),
560 buckets: vec![],
561 quantiles: vec![],
562 max_age: std::time::Duration::from_secs(0),
563 age_buckets: 0,
564 };
565
566 let db_transactions_metric = Arc::new(DMSCMetric::new(db_transactions_config));
567 registry.register(db_transactions_metric)?;
568
569 let db_commits_config = DMSCMetricConfig {
571 metric_type: DMSCMetricType::Counter,
572 name: "dms_db_transaction_commits_total".to_string(),
573 help: "Total number of database transaction commits".to_string(),
574 buckets: vec![],
575 quantiles: vec![],
576 max_age: std::time::Duration::from_secs(0),
577 age_buckets: 0,
578 };
579
580 let db_commits_metric = Arc::new(DMSCMetric::new(db_commits_config));
581 registry.register(db_commits_metric)?;
582
583 let db_rollbacks_config = DMSCMetricConfig {
585 metric_type: DMSCMetricType::Counter,
586 name: "dms_db_transaction_rollbacks_total".to_string(),
587 help: "Total number of database transaction rollbacks".to_string(),
588 buckets: vec![],
589 quantiles: vec![],
590 max_age: std::time::Duration::from_secs(0),
591 age_buckets: 0,
592 };
593
594 let db_rollbacks_metric = Arc::new(DMSCMetric::new(db_rollbacks_config));
595 registry.register(db_rollbacks_metric)?;
596 }
597
598 Ok(())
599 }
600
601 pub fn export_data(&self) -> DMSCObservabilityData {
610 DMSCObservabilityData {
611 metrics: {
612 #[cfg(feature = "observability")]
613 {
614 self.metrics_registry.as_ref().map(|r| r.export_prometheus()).unwrap_or_default()
615 }
616 #[cfg(not(feature = "observability"))]
617 {
618 String::default()
619 }
620 },
621 active_traces: self.tracer.as_ref().map(|_| tracing::tracer().map(|t| t.active_trace_count()).unwrap_or(0)).unwrap_or(0),
622 active_spans: self.tracer.as_ref().map(|_| tracing::tracer().map(|t| t.active_span_count()).unwrap_or(0)).unwrap_or(0),
623 }
624 }
625}
626
627#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
632#[derive(Debug, Clone, Serialize, Deserialize)]
633pub struct DMSCObservabilityData {
634 pub metrics: String,
636 pub active_traces: usize,
638 pub active_spans: usize,
640}
641
642#[cfg(feature = "pyo3")]
643#[pyo3::prelude::pymethods]
645impl DMSCObservabilityData {
646 #[new]
648 fn py_new(metrics: String, active_traces: usize, active_spans: usize) -> Self {
649 Self {
650 metrics,
651 active_traces,
652 active_spans,
653 }
654 }
655
656 #[pyo3(name = "get_metrics")]
658 fn get_metrics_impl(&self) -> String {
659 self.metrics.clone()
660 }
661
662 #[pyo3(name = "get_active_traces")]
664 fn get_active_traces_impl(&self) -> usize {
665 self.active_traces
666 }
667
668 #[pyo3(name = "get_active_spans")]
670 fn get_active_spans_impl(&self) -> usize {
671 self.active_spans
672 }
673}
674
675#[cfg(feature = "pyo3")]
676#[pyo3::prelude::pymethods]
677impl DMSCObservabilityModule {
678 fn get_metrics(&self) -> String {
679 format!("ObservabilityModule with config: {:?}", self.config)
680 }
681}
682
683#[async_trait::async_trait]
684impl crate::core::DMSCModule for DMSCObservabilityModule {
685 fn name(&self) -> &str {
691 "DMSC.Observability"
692 }
693
694 fn is_critical(&self) -> bool {
704 false }
706
707 async fn init(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
725 let binding = ctx.config();
727 let cfg = binding.config();
728
729 self.config = DMSCObservabilityConfig {
730 tracing_enabled: cfg.get_bool("observability.tracing_enabled").unwrap_or(true),
731 metrics_enabled: cfg.get_bool("observability.metrics_enabled").unwrap_or(true),
732 tracing_sampling_rate: cfg.get_f32("observability.tracing_sampling_rate")
733 .unwrap_or(0.1)
734 .max(0.0)
735 .min(1.0) as f64,
736 tracing_sampling_strategy: cfg.get_str("observability.tracing_sampling_strategy")
737 .unwrap_or("rate")
738 .to_string(),
739 metrics_window_size_secs: cfg.get_u64("observability.metrics_window_size_secs")
740 .unwrap_or(300)
741 .max(1),
742 metrics_bucket_size_secs: cfg.get_u64("observability.metrics_bucket_size_secs")
743 .unwrap_or(10)
744 .max(1),
745 };
746
747 self.init_tracing();
749 self.init_metrics();
750 self.create_service_metrics()?;
751
752 let hooks: &mut crate::hooks::DMSCHookBus = ctx.hooks_mut();
754
755 hooks.register(
757 crate::hooks::DMSCHookKind::Startup,
758 "dms.observability.lifecycle".to_string(),
759 |_ctx, _event: &crate::hooks::DMSCHookEvent| {
760 Ok(())
762 },
763 );
764
765 let logger = ctx.logger();
766 logger.info("DMSC.Observability", "Observability module initialized")?;
767
768 Ok(())
769 }
770
771 async fn after_shutdown(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
784 let data = self.export_data();
786
787 let logger = ctx.logger();
788 logger.info("DMSC.Observability", format!("Final observability data: {} active traces, {} active spans",
789 data.active_traces, data.active_spans))?;
790
791 Ok(())
792 }
793}