> Markdown version of [Migrating to Version 5](https://vaadin.com/docs/next/tools/observability/migrating-to-v5). Section index: [llms.txt](https://vaadin.com/docs/next/tools/llms.txt)

# Migrating to Version 5

Observability Kit 5 is a ground-up rewrite. Earlier versions shipped as a standalone **OpenTelemetry Java agent** that you attached to the JVM and configured through an `agent.properties` file. Version 5 is a plain **[Micrometer](https://micrometer.io)** library: you add it as a dependency, and it records into your application’s `MeterRegistry` and emits tracing spans through the Micrometer Observation API.

The practical upshot is that, with Spring Boot, the kit becomes a true drop-in — add one dependency and you’re done. There’s no agent to download, no `-javaagent` flag, and no separate configuration file. In exchange, the telemetry now flows through Spring Boot Actuator and the Micrometer registry backends you already use, rather than through the kit’s own OpenTelemetry exporters.

This page describes what changed and how to move an existing version 4 setup to version 5.

> **Important: Breaking Change**
>
> Version 5 is not a drop-in replacement for version 4. Meter names, the configuration model, the tracing data, and the custom-instrumentation API have all changed. Plan to update your dashboards, alerts, and any custom instrumentation as part of the upgrade.

## <a id="what-changed-at-a-glance"></a>What Changed at a Glance

|                        | Version 4 (OpenTelemetry agent)                                                    | Version 5 (Micrometer)                                                             |
| ---------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Packaging              | A separate agent JAR, downloaded manually, plus a starter dependency.              | A single Maven dependency. No agent, no manual download.                           |
| Startup                | `-javaagent:observability-kit-agent.jar` on the JVM command line.                  | Nothing — the kit auto-configures on the classpath.                                |
| Telemetry pipeline     | OpenTelemetry SDK, exported directly to a backend or collector over OTLP.          | Micrometer `MeterRegistry` for metrics, Observation API for traces.                |
| Export                 | Built into the agent and configured in `agent.properties`.                         | Spring Boot Actuator plus a Micrometer registry of your choice.                    |
| Configuration          | `agent.properties`, `OTEL_*` environment variables, or `otel.*` system properties. | `vaadin.observability.*` Spring properties, or an `ObservabilitySettings` builder. |
| Frontend               | `@hilla/observability-kit-client` npm package, initialized with `init()`.          | Built in and server-side. No npm package, no client code.                          |
| Custom instrumentation | OpenTelemetry API — `@WithSpan`, `GlobalOpenTelemetry`, `Tracer`.                  | Micrometer — `@Observed`/`@Timed`, `MeterRegistry`, Observation API.               |
| Requirements           | Java 21+.                                                                          | Java 21+, Vaadin 25.3+, and Spring Boot 4 for the starter.                         |

## <a id="step-1-update-dependencies"></a>Step 1: Update Dependencies

Remove the agent and switch the dependency to the module that matches your deployment.

### <a id="spring-boot"></a>Spring Boot

For a Spring Boot application, keep the starter but update its version. Boot’s Micrometer auto-configuration does the rest.

`pom.xml`

```xml
<dependency>
    <groupId>com.vaadin</groupId>
    <artifactId>observability-kit-starter</artifactId>
</dependency>
```

To actually **export** the metrics, add Spring Boot Actuator and the registry backend you want. The kit only records into the registry; it no longer exports anything itself. For example, for Prometheus:

`pom.xml`

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
```

`application.properties`

```properties
management.endpoints.web.exposure.include=prometheus
```

The metrics are then served at `GET /actuator/prometheus`.

### <a id="plain-spring"></a>Plain Spring

Use the `observability-kit-spring` module, import the configuration, and supply a `MeterRegistry` bean. See the [Getting Started](https://vaadin.com/docs/next/tools/observability/getting-started.md) page for details.

### <a id="standalone-without-spring"></a>Standalone (Without Spring)

Use the `observability-kit-micrometer` module and install the kit at servlet-context startup, before `VaadinService` initializes:

```java
ObservabilityKit.install(meterRegistry,
        ObservabilitySettings.builder().build());
```

## <a id="step-2-remove-the-agent"></a>Step 2: Remove the Agent

Delete everything related to the version 4 agent:

- The downloaded `observability-kit-agent-*.jar` file.

- The `-javaagent:…​/observability-kit-agent.jar` argument from every launch command, container image, and run configuration.

- The `agent.properties` file (its settings are migrated in [Step 4](#step-4-migrate-configuration)).

- Any `OTEL_*` environment variables or `-Dotel.*` system properties used to configure the agent.

With the new version there is no longer anything to download or attach. The kit activates by being on the classpath.

## <a id="step-3-remove-the-frontend-package"></a>Step 3: Remove the Frontend Package

Version 4 required a separate client package for frontend observability. In version 5, browser-side timing is built in and collected through a server-side channel, so the npm package and its initialization code are gone.

Remove the dependency and the `init()` call:

- Uninstall `@hilla/observability-kit-client`.

- Delete the `import { init } from '@hilla/observability-kit-client'` block and the `init(…​)` call from `frontend/index.ts`.

Client-side instrumentation is now enabled by default and toggled with a single property, `vaadin.observability.client`.

## <a id="step-4-migrate-configuration"></a>Step 4: Migrate Configuration

Move settings out of `agent.properties` and into `vaadin.observability.*` Spring properties (or, for standalone, the `ObservabilitySettings` builder). The configuration model is much smaller: instead of OpenTelemetry’s fine-grained instrumentation switches, version 5 exposes a handful of feature toggles.

| Version 4                                                     | Version 5                                                                                                                                                    |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `otel.instrumentation.vaadin.frontend.enabled`                | `vaadin.observability.client`                                                                                                                                |
| `otel.instrumentation.vaadin.frontend.*` (per-module toggles) | No equivalent — client instrumentation is a single on/off toggle.                                                                                            |
| Tracing on/off (agent-level)                                  | `vaadin.observability.traces`                                                                                                                                |
| (no equivalent)                                               | `vaadin.observability.sessions`, `.uis`, `.ui-state`, `.navigation`, `.requests`, `.data`, `.errors`, `.resync`, `.database`, `.insights`                    |
| `otel.service.name`, `otel.resource.attributes`               | Configured through Spring/Micrometer (for example `spring.application.name` and your registry’s common tags or OTLP resource settings), not through the kit. |

A typical version 5 configuration:

`application.properties`

```properties
# Turn the whole kit off
vaadin.observability.enabled=false

# Or toggle individual feature groups
vaadin.observability.client=false
vaadin.observability.traces=false
```

Everything that instruments ordinary request handling is enabled by default, so most applications need no configuration at all. The opt-in features are UI state size, database monitoring, and the two detail toggles that would add sensitive or high-cardinality data to what leaves the application. For the complete list of properties, see the [Configuration](https://vaadin.com/docs/next/tools/observability/configuration.md) page.

> **Note: Service Identity Moves to Micrometer**
>
> The `service.name`, `service.namespace`, and related OpenTelemetry resource attributes are no longer set on the kit. Service identity is now part of your Micrometer/Actuator setup — for example, common registry tags or the OTLP exporter’s resource configuration. Set them there so every meter and span is tagged consistently.

## <a id="step-5-update-custom-instrumentation"></a>Step 5: Update Custom Instrumentation

If you wrote custom traces or metrics with the OpenTelemetry API, port them to Micrometer. The concepts map closely.

| Version 4 (OpenTelemetry)                    | Version 5 (Micrometer)                                                               |
| -------------------------------------------- | ------------------------------------------------------------------------------------ |
| `@WithSpan`                                  | `@Observed` (timer + span) or `@Timed` (timer only)                                  |
| `@SpanAttribute("key")`                      | `lowCardinalityKeyValues = { "key", "value" }` on `@Observed`, or a tag on the meter |
| `GlobalOpenTelemetry.getTracer(…​)` + `Span` | `Observation` from an injected `ObservationRegistry`                                 |
| `Meter` / `LongCounter` / `gaugeBuilder`     | `Counter` / `Gauge` / `Timer` builders, or `MeterRegistry` shortcuts                 |

For example, a manually traced method:

Version 4 — OpenTelemetry

```java
Tracer tracer = GlobalOpenTelemetry.getTracer("app-instrumentation", "1.0");
Span span = tracer.spanBuilder("My Task").startSpan();
try {
    doWork();
} finally {
    span.end();
}
```

Version 5 — Micrometer Observation

```java
Observation.createNotStarted("app.task", observationRegistry)
        .contextualName("my-task")
        .observe(() -> doWork());
```

Because the kit records into the same registry and Observation API, your custom meters and spans sit alongside the built-in ones and export through the same backend. For the full set of options — annotations, fluent builders, and raw registry beans — see the [Customization](https://vaadin.com/docs/next/tools/observability/customization.md) page.

> **Note: AOP Required for Annotations**
>
> The `@Observed`, `@Timed`, and `@Counted` annotations are backed by Spring AOP aspects. Add `spring-boot-starter-aop` (or register the aspects yourself in plain Spring) for them to take effect. The version 4 `@WithSpan` annotation worked through the agent and needed no AOP.

## <a id="step-6-update-dashboards-and-alerts"></a>Step 6: Update Dashboards and Alerts

Meter names changed, so any saved queries, dashboards, and alerts must be updated. The most common renames:

| Version 4                             | Version 5                                                                                                           | Notes                                                                                                                                           |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `vaadin.session.count` (Gauge)        | `vaadin.sessions.active` (Gauge)                                                                                    |                                                                                                                                                 |
| `vaadin.session.duration` (Histogram) | `vaadin.sessions.duration` (Timer)                                                                                  |                                                                                                                                                 |
| `vaadin.ui.count` (Gauge)             | `vaadin.ui.active` (Gauge)                                                                                          |                                                                                                                                                 |
| (none)                                | `vaadin.sessions.created`, `vaadin.ui.created`                                                                      | New counters.                                                                                                                                   |
| (none)                                | `vaadin.navigation`, `vaadin.request.duration`, `vaadin.rpc.duration`, `vaadin.errors`                              | New server-side timers and error counter.                                                                                                       |
| (none)                                | `vaadin.session.lock.wait`, `vaadin.session.lock.hold`                                                              | New session-lock timers.                                                                                                                        |
| (none)                                | `vaadin.data.count.duration`, `vaadin.data.fetch.duration`, `vaadin.data.fetch.requested`, `vaadin.data.fetch.rows` | New data provider query timers and page sizes for lazy-loading components.                                                                      |
| (none)                                | `vaadin.ui.state.*`, `vaadin.session.state.nodes.max`, `vaadin.session.uis.max`                                     | New, opt-in gauges of retained UI state, for capacity planning.                                                                                 |
| (none)                                | `vaadin.client.*`                                                                                                   | Browser-observed timing (bootstrap, navigation, UIDL requests and rendering, Web Vitals), plus browser errors and connection-state transitions. |
| (none)                                | `vaadin.resync`                                                                                                     | New counter of UIDL message resends and client-requested resynchronizations.                                                                    |
| (none)                                | `vaadin.db.fetch.rows`, `vaadin.db.query`                                                                           | New, opt-in per-route database metrics. Spring Boot starter only.                                                                               |
| `db.client.connections.*`             | Use Micrometer’s own pool metrics                                                                                   | No longer emitted by the kit. Spring Boot/Micrometer instrument the connection pool directly.                                                   |
| `process.runtime.jvm.*`               | Use Micrometer’s JVM binders                                                                                        | No longer emitted by the kit. Actuator registers JVM and process metrics out of the box.                                                        |

For the complete, current list of meters, see the [Reference](https://vaadin.com/docs/next/tools/observability/reference.md) page.

> **Important: Queries Break Structurally, Not Only by Name**
>
> Three export-level changes affect existing queries and alerts beyond the renames above:
>
> - The version 4 agent exported OpenTelemetry histograms with buckets. Version 5 timers publish count, sum, and max only, so a `histogram_quantile()` query or a latency alert finds no `_bucket` series until you enable them per meter — and it fails silently, returning no data rather than an error. See [Percentiles and Histogram Buckets](https://vaadin.com/docs/next/tools/observability/integrations.md#percentiles).
>
> - Prometheus strips the reserved `_created` suffix, so `vaadin.sessions.created` and `vaadin.ui.created` appear as `vaadin_sessions_total` and `vaadin_ui_total`.
>
> - With tracing on, each observed timer also publishes a parallel long-task timer (`_active` series in Prometheus), which roughly doubles the series count for those meters. Account for it when estimating metric volume.

> **Note: Tracing Data Is Shaped Differently**
>
> Version 4 produced a rich span tree with many Vaadin-specific attributes, some of them per element synchronization. Version 5 emits spans for the request lifecycle, navigation, RPC, service-executor tasks, data provider count and fetch queries, and — when database monitoring is on — JDBC queries, through the Observation API. Spans that would have been unbounded as timer tags, such as the invoked component and event, are attached as span-only attributes. Add your own spans with `@Observed` or the Observation API where you need finer-grained tracing; see [Customization](https://vaadin.com/docs/next/tools/observability/customization.md).

> **Note: New in Version 5: Interaction Insights**
>
> Version 5 also retains the interactions that failed or ran over the UX budget — and the data provider queries behind a slow component — and serves them, grouped and ready to act on, from an Actuator endpoint. This has no version 4 equivalent, and it’s the fastest way from a user report to a line of code. See the [Interaction Insights](https://vaadin.com/docs/next/tools/observability/insights.md) page.

## <a id="step-7-update-backend-integrations"></a>Step 7: Update Backend Integrations

Because the kit no longer exports telemetry itself, integration with backends now goes through standard Micrometer mechanisms rather than the OpenTelemetry agent’s exporters:

- **Metrics** are exported by adding the corresponding `micrometer-registry-*` dependency (Prometheus, OTLP, Graphite, and others) and exposing it through Actuator.

- **Traces** are exported by adding a Micrometer tracing bridge — for example OpenTelemetry (`micrometer-tracing-bridge-otel`) or Zipkin (`micrometer-tracing-bridge-brave`) — as you would for any Micrometer-instrumented application.

- **Logs** are no longer exported by the kit at all. The version 4 agent could ship them over OTLP; in version 5, route them through your logging pipeline instead — see the vendor pages under [Integrations](https://vaadin.com/docs/next/tools/observability/integrations.md) for the options.

Vendors such as Prometheus, Grafana, Datadog, and New Relic are all reachable this way, either through a native Micrometer registry or through an OTLP bridge to an OpenTelemetry collector. See the [Integrations](https://vaadin.com/docs/next/tools/observability/integrations.md) page for vendor-specific setup.

## <a id="migration-checklist"></a>Migration Checklist

- ❏ Remove the `observability-kit-agent` JAR and the `-javaagent` flag.

- ❏ Update or switch the kit dependency to the version 5 module for your deployment.

- ❏ Add Spring Boot Actuator and a `micrometer-registry-*` backend to export metrics.

- ❏ Add a Micrometer tracing bridge if you export traces.

- ❏ Delete `agent.properties` and any `OTEL_*` / `otel.*` settings.

- ❏ Re-create any needed settings as `vaadin.observability.*` properties.

- ❏ Remove the `@hilla/observability-kit-client` package and its `init()` call.

- ❏ Port custom OpenTelemetry instrumentation to the Micrometer API (add `spring-boot-starter-aop` if you use the annotations).

- ❏ Update dashboards, queries, and alerts to the new meter names.

- ❏ Expose and secure the `vaadin` Actuator endpoint if you want interaction insights.

- ❏ Re-verify backend integrations end to end.

`A82D8720-A226-41EF-9CC5-3391AE5EF262`
