Setup Guide
This guide walks you through enabling and configuring observability in your Viaduct instance. Metrics collection is automatic once you provide a MeterRegistry implementation.
Prerequisites
Add the Micrometer dependency for your chosen monitoring system. Viaduct works with any Micrometer-supported registry.
For Prometheus:
dependencies {
implementation("io.micrometer:micrometer-registry-prometheus:1.11.0")
}
For other registries, see the Micrometer documentation.
Basic Setup
Step 1: Create a MeterRegistry
Choose and configure a MeterRegistry implementation based on your monitoring system:
In-Memory Registry (for testing)
import io.micrometer.core.instrument.simple.SimpleMeterRegistry
val meterRegistry = SimpleMeterRegistry()
Prometheus
import io.micrometer.prometheus.PrometheusConfig
import io.micrometer.prometheus.PrometheusMeterRegistry
val prometheusMeterRegistry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)
// Expose metrics endpoint
app.get("/metrics") { req, res ->
res.contentType("text/plain; version=0.0.4")
prometheusMeterRegistry.scrape()
}
Other Monitoring Systems
Viaduct supports all Micrometer registries including Datadog, CloudWatch, StatsD, Graphite, and more. See the Micrometer documentation for configuration examples for your monitoring system.
Step 2: Configure Viaduct with MeterRegistry
Pass your MeterRegistry to the ViaductBuilder:
import viaduct.service.ViaductBuilder
val viaduct = ViaductBuilder()
.withMeterRegistry(meterRegistry)
.withTenantAPIBootstrapperBuilder(myBootstrapper)
.build()
That’s it! Viaduct will now automatically emit metrics for all GraphQL operations.
Advanced Configuration
Custom Percentiles
By default, Viaduct configures percentiles at p50, p75, p90, and p95. To customize percentiles, configure your MeterRegistry before passing it to Viaduct:
import io.micrometer.core.instrument.config.MeterFilter
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig
meterRegistry.config().meterFilter(object : MeterFilter {
override fun configure(id: Meter.Id, config: DistributionStatisticConfig): DistributionStatisticConfig? {
if (id.name.startsWith("viaduct.")) {
return config.merge(
DistributionStatisticConfig.builder()
.percentiles(0.5, 0.75, 0.9, 0.95, 0.99, 0.999)
.build()
)
}
return config
}
})
Adding Common Tags
Add tags to all Viaduct metrics for filtering and aggregation:
import io.micrometer.core.instrument.config.MeterFilter
import io.micrometer.core.instrument.Tag
meterRegistry.config().commonTags(
Tag.of("service", "my-viaduct-service"),
Tag.of("environment", "production"),
Tag.of("region", "us-east-1")
)
Filtering Metrics
Disable specific metrics or add custom logic:
import io.micrometer.core.instrument.config.MeterFilter
// Disable field-level metrics (reduce cardinality)
meterRegistry.config().meterFilter(
MeterFilter.deny { id -> id.name == "viaduct.field" }
)
// Only track metrics for specific operations
meterRegistry.config().meterFilter(
MeterFilter.deny { id ->
id.name.startsWith("viaduct.") &&
!id.tags.any { tag ->
tag.key == "operation_name" &&
tag.value in setOf("GetUser", "SearchProducts")
}
}
)
Histogram Configuration
Configure histogram buckets for better distribution analysis:
meterRegistry.config().meterFilter(object : MeterFilter {
override fun configure(id: Meter.Id, config: DistributionStatisticConfig): DistributionStatisticConfig? {
if (id.name.startsWith("viaduct.")) {
return config.merge(
DistributionStatisticConfig.builder()
.percentilesHistogram(true)
.minimumExpectedValue(Duration.ofMillis(1).toNanos().toDouble())
.maximumExpectedValue(Duration.ofSeconds(10).toNanos().toDouble())
.build()
)
}
return config
}
})
Composite MeterRegistry
Use CompositeMeterRegistry to send metrics to multiple backends:
import io.micrometer.core.instrument.composite.CompositeMeterRegistry
val compositeMeterRegistry = CompositeMeterRegistry()
// Add Prometheus for real-time querying
val prometheusMeterRegistry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)
compositeMeterRegistry.add(prometheusMeterRegistry)
// Add Datadog for long-term storage and alerting
val datadogMeterRegistry = DatadogMeterRegistry(datadogConfig, Clock.SYSTEM)
compositeMeterRegistry.add(datadogMeterRegistry)
// Use composite registry with Viaduct
val viaduct = ViaductBuilder()
.withMeterRegistry(compositeMeterRegistry)
.withTenantAPIBootstrapperBuilder(myBootstrapper)
.build()
Verification
Verify Metrics are Being Collected
For SimpleMeterRegistry or testing:
// Execute a query
viaduct.execute(executionInput)
// Check metrics
meterRegistry.meters.forEach { meter ->
if (meter.id.name.startsWith("viaduct.")) {
println("${meter.id.name} - ${meter.measure()}")
}
}
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.