Skip to content

Commit 3de4822

Browse files
swcollardDaleSeo
authored andcommitted
Add more unit test coverage
Fix links to external vendors OTLP docs pr feedback: LazyLock meter, debug and todo removal, comments
1 parent 859ff6a commit 3de4822

File tree

10 files changed

+64
-32
lines changed

10 files changed

+64
-32
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ url = { version = "2.4", features = ["serde"] }
5454
[workspace.metadata]
5555
crane.name = "apollo-mcp"
5656

57+
# This allows usage of coverage(off) attribute without causing a linting error.
58+
# This attribute doesn't work in stable Rust yet and can be removed whenever it does.
59+
# See https://github.com/apollographql/apollo-mcp-server/pull/372
5760
[workspace.lints.rust]
5861
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
5962

crates/apollo-mcp-server/build.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,6 @@ fn main() {
100100
// Generate the keys
101101
let telemetry_attribute_data = flatten(telemetry.attributes);
102102
let telemetry_metrics_data = flatten(telemetry.metrics);
103-
println!(
104-
"a {:?} | m {:?}",
105-
telemetry_attribute_data, telemetry_metrics_data
106-
);
107103

108104
// Write out the generated keys
109105
let out_dir = std::env::var_os("OUT_DIR").expect("could not retrieve output directory");

crates/apollo-mcp-server/src/graphql.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Execute GraphQL operations from an MCP tool
22
3+
use crate::errors::McpError;
34
use crate::generated::telemetry::{TelemetryAttribute, TelemetryMetric};
4-
use crate::{errors::McpError, meter::get_meter};
5+
use crate::meter;
56
use opentelemetry::KeyValue;
67
use reqwest::header::{HeaderMap, HeaderValue};
78
use reqwest_middleware::{ClientBuilder, Extension};
@@ -40,7 +41,7 @@ pub trait Executable {
4041
/// Execute as a GraphQL operation using the endpoint and headers
4142
#[tracing::instrument(skip(self, request))]
4243
async fn execute(&self, request: Request<'_>) -> Result<CallToolResult, McpError> {
43-
let meter = get_meter();
44+
let meter = &meter::METER;
4445
let start = std::time::Instant::now();
4546
let mut op_id: Option<String> = None;
4647
let client_metadata = serde_json::json!({
@@ -135,14 +136,13 @@ pub trait Executable {
135136
),
136137
KeyValue::new(
137138
TelemetryAttribute::OperationId.to_key(),
138-
op_id.unwrap_or("unknown".to_string()),
139+
op_id.unwrap_or("".to_string()),
139140
),
140141
KeyValue::new(
141142
TelemetryAttribute::OperationSource.to_key(),
142-
if self.persisted_query_id().is_some() {
143-
"persisted_query"
144-
} else {
145-
"operation"
143+
match self.persisted_query_id() {
144+
Some(_) => "persisted_query",
145+
None => "operation",
146146
},
147147
),
148148
];
@@ -162,6 +162,7 @@ pub trait Executable {
162162
#[cfg(test)]
163163
mod test {
164164
use crate::errors::McpError;
165+
use crate::generated::telemetry::TelemetryMetric;
165166
use crate::graphql::{Executable, OperationDetails, Request};
166167
use http::{HeaderMap, HeaderValue};
167168
use opentelemetry::global;
@@ -453,7 +454,7 @@ mod test {
453454
.find(|scope_metrics| scope_metrics.scope().name() == "apollo.mcp")
454455
{
455456
for metric in scope_metrics.metrics() {
456-
if metric.name() == "apollo.mcp.operation.count"
457+
if metric.name() == TelemetryMetric::OperationCount.as_str()
457458
&& let AggregatedMetrics::U64(MetricData::Sum(data)) = metric.data()
458459
{
459460
for point in data.data_points() {

crates/apollo-mcp-server/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub(crate) mod schema_tree_shake;
1717
pub mod server;
1818
pub mod telemetry_attributes;
1919

20+
/// These values are generated at build time by build.rs using telemetry.toml as input.
2021
pub mod generated {
2122
pub mod telemetry {
2223
include!(concat!(env!("OUT_DIR"), "/telemetry_attributes.rs"));
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
use opentelemetry::{global, metrics::Meter};
2-
use std::sync::OnceLock;
2+
use std::sync::LazyLock;
33

4-
static METER: OnceLock<Meter> = OnceLock::new();
5-
6-
pub fn get_meter() -> &'static Meter {
7-
METER.get_or_init(|| global::meter(env!("CARGO_PKG_NAME")))
8-
}
4+
pub static METER: LazyLock<Meter> = LazyLock::new(|| global::meter(env!("CARGO_PKG_NAME")));

crates/apollo-mcp-server/src/runtime/filtering_exporter.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@ where
2828
for span in &mut batch {
2929
span.attributes
3030
.retain(|kv| filter_omitted_apollo_attributes(kv, &self.omitted));
31-
32-
// TODO: while not strictly necessary for dealing with high-cardinality, do we want to
33-
// filter out from span.events.events as well?
34-
// for ev in &mut span.events.events {
35-
// ev.attributes.retain(|kv| filter_omitted_apollo_attributes(kv, &self.allow));
36-
// }
3731
}
3832

3933
self.inner.export(batch)

crates/apollo-mcp-server/src/runtime/telemetry.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,24 @@ mod tests {
345345
);
346346
}
347347

348+
#[tokio::test]
349+
async fn http_protocol_returns_valid_meter_provider() {
350+
let config = test_config(
351+
None,
352+
None,
353+
Some(MetricsExporters {
354+
otlp: Some(OTLPMetricExporter {
355+
protocol: "http/protobuf".to_string(),
356+
endpoint: "http://localhost:4318/v1/metrics".to_string(),
357+
}),
358+
omitted_attributes: None,
359+
}),
360+
None,
361+
);
362+
let result = init_meter_provider(&config.telemetry);
363+
assert!(result.is_ok());
364+
}
365+
348366
#[tokio::test]
349367
async fn unknown_protocol_raises_tracer_provider_error() {
350368
let config = test_config(
@@ -368,4 +386,23 @@ mod tests {
368386
.unwrap_or(false)
369387
);
370388
}
389+
390+
#[tokio::test]
391+
async fn http_protocol_returns_valid_tracer_provider() {
392+
let config = test_config(
393+
None,
394+
None,
395+
None,
396+
Some(TracingExporters {
397+
otlp: Some(OTLPTracingExporter {
398+
protocol: "http/protobuf".to_string(),
399+
endpoint: "http://localhost:4318/v1/traces".to_string(),
400+
}),
401+
sampler: Default::default(),
402+
omitted_attributes: None,
403+
}),
404+
);
405+
let result = init_tracer_provider(&config.telemetry);
406+
assert!(result.is_ok());
407+
}
371408
}

crates/apollo-mcp-server/src/server/states/running.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use tracing::{debug, error};
2222
use url::Url;
2323

2424
use crate::generated::telemetry::{TelemetryAttribute, TelemetryMetric};
25+
use crate::meter;
2526
use crate::{
2627
auth::ValidToken,
2728
custom_scalar_map::CustomScalarMap,
@@ -35,7 +36,6 @@ use crate::{
3536
search::{SEARCH_TOOL_NAME, Search},
3637
validate::{VALIDATE_TOOL_NAME, Validate},
3738
},
38-
meter::get_meter,
3939
operations::{MutationMode, Operation, RawOperation},
4040
};
4141

@@ -182,7 +182,7 @@ impl ServerHandler for Running {
182182
_request: InitializeRequestParam,
183183
context: RequestContext<RoleServer>,
184184
) -> Result<InitializeResult, McpError> {
185-
let meter = get_meter();
185+
let meter = &meter::METER;
186186
meter
187187
.u64_counter(TelemetryMetric::InitializeCount.as_str())
188188
.build()
@@ -199,7 +199,7 @@ impl ServerHandler for Running {
199199
request: CallToolRequestParam,
200200
context: RequestContext<RoleServer>,
201201
) -> Result<CallToolResult, McpError> {
202-
let meter = get_meter();
202+
let meter = &meter::METER;
203203
let start = std::time::Instant::now();
204204
let tool_name = request.name.clone();
205205
let result = match tool_name.as_ref() {
@@ -321,7 +321,7 @@ impl ServerHandler for Running {
321321
_request: Option<PaginatedRequestParam>,
322322
_context: RequestContext<RoleServer>,
323323
) -> Result<ListToolsResult, McpError> {
324-
let meter = get_meter();
324+
let meter = &meter::METER;
325325
meter
326326
.u64_counter(TelemetryMetric::ListToolsCount.as_str())
327327
.build()
@@ -344,7 +344,7 @@ impl ServerHandler for Running {
344344
}
345345

346346
fn get_info(&self) -> ServerInfo {
347-
let meter = get_meter();
347+
let meter = &meter::METER;
348348
meter
349349
.u64_counter(TelemetryMetric::GetInfoCount.as_str())
350350
.build()

crates/apollo-mcp-server/src/server/states/starting.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,11 @@ mod tests {
367367
disable_auth_token_passthrough: false,
368368
search_leaf_depth: 5,
369369
index_memory_bytes: 1024 * 1024 * 1024,
370-
health_check: HealthCheckConfig::default(),
370+
health_check: HealthCheckConfig {
371+
enabled: true,
372+
..Default::default()
373+
},
374+
cors: Default::default(),
371375
},
372376
schema: Schema::parse_and_validate("type Query { hello: String }", "test.graphql")
373377
.expect("Valid schema"),

docs/source/telemetry.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ telemetry:
6666

6767
The MCP server works with any OTLP-compatible backend. Consult your provider's documentation for specific endpoint URLs and authentication:
6868

69-
- [Datadog OTLP Integration](https://docs.datadoghq.com/tracing/setup_overview/open_standards/otlp_ingest_in_datadog/) - Native OTLP support
70-
- [New Relic OpenTelemetry](https://docs.newrelic.com/docs/more-integrations/open-source-telemetry-integrations/opentelemetry/) - Direct OTLP ingestion
69+
- [Datadog OTLP Integration](https://docs.datadoghq.com/opentelemetry/setup/otlp_ingest_in_the_agent/) - Native OTLP support
70+
- [New Relic OpenTelemetry](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/) - Direct OTLP ingestion
7171
- [AWS Observability](https://aws-otel.github.io/docs/introduction) - Via AWS Distro for OpenTelemetry
7272
- [Grafana Cloud](https://grafana.com/docs/grafana-cloud/send-data/otlp/) - Hosted Grafana with OTLP
7373
- [Honeycomb](https://docs.honeycomb.io/getting-data-in/opentelemetry/) - OpenTelemetry-native platform

0 commit comments

Comments
 (0)