Setup
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:
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: