mas_config/sections/
telemetry.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use serde_with::skip_serializing_none;
10use url::Url;
11
12use super::ConfigurationSection;
13
14/// Propagation format for incoming and outgoing requests
15#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
16#[serde(rename_all = "lowercase")]
17pub enum Propagator {
18    /// Propagate according to the W3C Trace Context specification
19    TraceContext,
20
21    /// Propagate according to the W3C Baggage specification
22    Baggage,
23
24    /// Propagate trace context with Jaeger compatible headers
25    Jaeger,
26}
27
28#[allow(clippy::unnecessary_wraps)]
29fn otlp_endpoint_default() -> Option<String> {
30    Some("https://localhost:4318".to_owned())
31}
32
33/// Exporter to use when exporting traces
34#[skip_serializing_none]
35#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, Default)]
36#[serde(rename_all = "lowercase")]
37pub enum TracingExporterKind {
38    /// Don't export traces
39    #[default]
40    None,
41
42    /// Export traces to the standard output. Only useful for debugging
43    Stdout,
44
45    /// Export traces to an OpenTelemetry protocol compatible endpoint
46    Otlp,
47}
48
49/// Configuration related to exporting traces
50#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
51pub struct TracingConfig {
52    /// Exporter to use when exporting traces
53    #[serde(default)]
54    pub exporter: TracingExporterKind,
55
56    /// OTLP exporter: OTLP over HTTP compatible endpoint
57    #[serde(skip_serializing_if = "Option::is_none")]
58    #[schemars(url, default = "otlp_endpoint_default")]
59    pub endpoint: Option<Url>,
60
61    /// List of propagation formats to use for incoming and outgoing requests
62    #[serde(default)]
63    pub propagators: Vec<Propagator>,
64}
65
66impl TracingConfig {
67    /// Returns true if all fields are at their default values
68    fn is_default(&self) -> bool {
69        matches!(self.exporter, TracingExporterKind::None)
70            && self.endpoint.is_none()
71            && self.propagators.is_empty()
72    }
73}
74
75/// Exporter to use when exporting metrics
76#[skip_serializing_none]
77#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, Default)]
78#[serde(rename_all = "lowercase")]
79pub enum MetricsExporterKind {
80    /// Don't export metrics
81    #[default]
82    None,
83
84    /// Export metrics to stdout. Only useful for debugging
85    Stdout,
86
87    /// Export metrics to an OpenTelemetry protocol compatible endpoint
88    Otlp,
89
90    /// Export metrics via Prometheus. An HTTP listener with the `prometheus`
91    /// resource must be setup to expose the Promethes metrics.
92    Prometheus,
93}
94
95/// Configuration related to exporting metrics
96#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
97pub struct MetricsConfig {
98    /// Exporter to use when exporting metrics
99    #[serde(default)]
100    pub exporter: MetricsExporterKind,
101
102    /// OTLP exporter: OTLP over HTTP compatible endpoint
103    #[serde(skip_serializing_if = "Option::is_none")]
104    #[schemars(url, default = "otlp_endpoint_default")]
105    pub endpoint: Option<Url>,
106}
107
108impl MetricsConfig {
109    /// Returns true if all fields are at their default values
110    fn is_default(&self) -> bool {
111        matches!(self.exporter, MetricsExporterKind::None) && self.endpoint.is_none()
112    }
113}
114
115fn sentry_dsn_example() -> &'static str {
116    "https://public@host:port/1"
117}
118
119/// Configuration related to the Sentry integration
120#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
121pub struct SentryConfig {
122    /// Sentry DSN
123    #[schemars(url, example = "sentry_dsn_example")]
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub dsn: Option<String>,
126}
127
128impl SentryConfig {
129    /// Returns true if all fields are at their default values
130    fn is_default(&self) -> bool {
131        self.dsn.is_none()
132    }
133}
134
135/// Configuration related to sending monitoring data
136#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
137pub struct TelemetryConfig {
138    /// Configuration related to exporting traces
139    #[serde(default, skip_serializing_if = "TracingConfig::is_default")]
140    pub tracing: TracingConfig,
141
142    /// Configuration related to exporting metrics
143    #[serde(default, skip_serializing_if = "MetricsConfig::is_default")]
144    pub metrics: MetricsConfig,
145
146    /// Configuration related to the Sentry integration
147    #[serde(default, skip_serializing_if = "SentryConfig::is_default")]
148    pub sentry: SentryConfig,
149}
150
151impl TelemetryConfig {
152    /// Returns true if all fields are at their default values
153    pub(crate) fn is_default(&self) -> bool {
154        self.tracing.is_default() && self.metrics.is_default() && self.sentry.is_default()
155    }
156}
157
158impl ConfigurationSection for TelemetryConfig {
159    const PATH: Option<&'static str> = Some("telemetry");
160}