diff --git a/hawkbit-core/pom.xml b/hawkbit-core/pom.xml
index 8fb24906c..a1f473420 100644
--- a/hawkbit-core/pom.xml
+++ b/hawkbit-core/pom.xml
@@ -30,11 +30,36 @@
org.springframework.security
spring-security-config
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ compile
+
+
+ org.springframework
+ spring-web
+ compile
+
+
+ org.springframework.data
+ spring-data-commons
+ compile
+
jakarta.validation
jakarta.validation-api
+
+ jakarta.servlet
+ jakarta.servlet-api
+ compile
+
+
+ io.micrometer
+ micrometer-core
+ compile
+
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/TenantMetricsConfiguration.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/TenantMetricsConfiguration.java
new file mode 100644
index 000000000..c000b26e6
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/TenantMetricsConfiguration.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.hawkbit.tenancy;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+import io.micrometer.common.KeyValue;
+import io.micrometer.common.KeyValues;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.observation.ObservationRegistry;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.eclipse.hawkbit.tenancy.TenantAware.TenantResolver;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
+import org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration;
+import org.springframework.boot.actuate.metrics.data.DefaultRepositoryTagsProvider;
+import org.springframework.boot.actuate.metrics.data.RepositoryTagsProvider;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.security.SecurityProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocation;
+import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
+import org.springframework.http.server.observation.ServerRequestObservationContext;
+import org.springframework.http.server.observation.ServerRequestObservationConvention;
+import org.springframework.web.filter.ServerHttpObservationFilter;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class TenantMetricsConfiguration {
+
+ public static final String TENANT_TAG = "tenant";
+
+ @AutoConfiguration(after = ObservationAutoConfiguration.class)
+ @ConditionalOnProperty(name = "hawkbit.metrics.tenancy.web.enabled", havingValue = "true", matchIfMissing = true)
+ @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
+ @ConditionalOnClass(name = { "org.springframework.web.servlet.DispatcherServlet", "io.micrometer.observation.Observation" })
+ @ConditionalOnBean(ObservationRegistry.class)
+ public static class WebConfig {
+
+ @Bean
+ @Primary
+ public DefaultServerRequestObservationConvention serverRequestObservationConvention(final TenantResolver tenantResolver) {
+ return new DefaultServerRequestObservationConvention() {
+
+ @Override
+ public KeyValues getLowCardinalityKeyValues(final ServerRequestObservationContext context) {
+ // Make sure that KeyValues entries are already sorted by name for better performance
+ return KeyValues.of(exception(context), method(context), outcome(context), status(context), tenant(), uri(context));
+ }
+
+ private KeyValue tenant() {
+ return KeyValue.of(TENANT_TAG, Optional.ofNullable(tenantResolver.resolveTenant()).orElse("n/a"));
+ }
+ };
+ }
+
+ @Bean
+ @Primary
+ public FilterRegistrationBean webMvcObservationFilter(
+ final ObservationRegistry registry,
+ // should be serverRequestObservationConvention (registered above)
+ final ObjectProvider customConvention,
+ final ObservationProperties observationProperties,
+ final SecurityProperties securityProperties) {
+ final FilterRegistrationBean filterRegistrationBean = new WebMvcObservationAutoConfiguration()
+ .webMvcObservationFilter(registry, customConvention, observationProperties);
+ // after security filter, so to be able to log tenant
+ filterRegistrationBean.setOrder(securityProperties.getFilter().getOrder() + 1);
+ return filterRegistrationBean;
+ }
+ }
+
+ @Configuration
+ @ConditionalOnProperty(name = "hawkbit.metrics.tenancy.repository.enabled", havingValue = "true", matchIfMissing = true)
+ @ConditionalOnClass(name = {
+ "io.micrometer.core.instrument.Tag",
+ "org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocation" })
+ public static class RepositoryConfig {
+
+ @Bean
+ public RepositoryTagsProvider repositoryTagsProvider(final TenantResolver tenantResolver) {
+ return new DefaultRepositoryTagsProvider() {
+
+ @Override
+ public Iterable repositoryTags(final RepositoryMethodInvocation invocation) {
+ final Iterable defaultTags = super.repositoryTags(invocation);
+ final String tenant = Optional.ofNullable(tenantResolver.resolveTenant()).orElse("n/a");
+ return () -> {
+ final Iterator defaultTagsIterator = defaultTags.iterator();
+ return new Iterator<>() {
+
+ private boolean tenantReturned;
+
+ @Override
+ public boolean hasNext() {
+ return defaultTagsIterator.hasNext() || !tenantReturned;
+ }
+
+ @Override
+ public Tag next() {
+ if (defaultTagsIterator.hasNext()) {
+ return defaultTagsIterator.next();
+ } else {
+ if (tenantReturned) {
+ throw new NoSuchElementException();
+ } else {
+ tenantReturned = true;
+ return Tag.of(TENANT_TAG, tenant);
+ }
+ }
+ }
+ };
+ };
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/hawkbit-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/hawkbit-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..b1f410d93
--- /dev/null
+++ b/hawkbit-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,2 @@
+org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration.WebConfig
+org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration.RepositoryConfig