Recently, we revamped the way we manage dependencies for our internal Kotlin library. We migrated observability dependency management to Gradle Catalog to simplify and streamline the process.
What is the Kotlin Gradle Catalog?
The Kotlin Gradle Catalog is a Gradle feature that allows you to manage dependencies and plugins in a structured and reusable way using a central libs.versions.toml file. It helps organize and simplify the management of library versions, plugins, and other dependencies in Kotlin or Java projects, making the build script cleaner and reducing duplication.
Key Features
Centralized Dependency Management:
- Define all dependencies and their versions in a single
libs.versions.tomlfile. - Reuse these definitions across your project or in multiple subprojects in a multi-module setup.
- Define all dependencies and their versions in a single
Readability: Replace hardcoded dependency strings in
build.gradle.ktsfiles with human-readable aliases.Version Control: Manage all library versions in one place, making upgrades and maintenance easier.
Standardization: Ensure consistent dependency versions across all modules in a project.
Structure of libs.versions.toml
The libs.versions.toml file resides in the gradle folder by convention and organizes dependencies into:
Version Definitions:
[versions] kotlin = "1.9.0" coroutines = "1.7.2"Library Aliases:
[libraries] kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }Plugins:
[plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
How We Integrated Prometheus and OpenTelemetry with Gradle Catalog
1. Defining Dependencies in libs.versions.toml
Hereβs how we structured the dependencies for Prometheus and OpenTelemetry:
[versions]
kotlin = "1.9.0"
prometheus = "0.16.0"
otel = "1.30.0"
[libraries]
micrometer-core = { module = "io.micrometer:micrometer-core", version = "1.11.4" }
micrometer-registry-prometheus = { module = "io.micrometer:micrometer-registry-prometheus", version = "1.11.4" }
otel-sdk = { module = "io.opentelemetry:opentelemetry-sdk", version.ref = "otel" }
otel-exporter-otlp = { module = "io.opentelemetry:opentelemetry-exporter-otlp", version.ref = "otel" }
2. Configuring build.gradle.kts
With the dependencies defined in the catalog, integrating them into the build script was much simpler:
plugins {
id("org.jetbrains.kotlin.jvm") version libs.versions.kotlin
application
}
dependencies {
implementation(libs.micrometer.core)
implementation(libs.micrometer.registry.prometheus)
implementation(libs.otel.sdk)
implementation(libs.otel.exporter.otlp)
}
application {
mainClass.set("com.example.DropwizardApp")
}
3. Using Prometheus and OpenTelemetry in the Application
We integrated Micrometer to expose metrics for Prometheus and OpenTelemetry for distributed tracing. The setup involved:
- Initializing Prometheus metrics with Micrometer:
val prometheusRegistry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)
prometheusRegistry.bindTo(JvmMemoryMetrics())
prometheusRegistry.bindTo(JvmGcMetrics())
// Register Prometheus servlet
environment.admin().addServlet("metrics", MetricsServlet(prometheusRegistry.prometheusRegistry)).addMapping("/metrics")
- Enabling OpenTelemetry auto-instrumentation:
AutoConfiguredOpenTelemetrySdk.initialize()
Why We Switched to Gradle Catalog
Reduced Boilerplate: Eliminating verbose dependency declarations from
build.gradle.ktsmade the build script cleaner and more maintainable.Centralized Updates: Upgrading library versions became easier since all versioning now resides in
libs.versions.toml.Consistency Across Projects: The catalog ensured that multiple subprojects in our ecosystem used the same dependency versions.
Conclusion
By switching to Gradle Catalog, we simplified dependency management and ensured consistency across our Kotlin projects. Integrating Prometheus and OpenTelemetry was seamless, allowing us to instrument our services with minimal effort.