This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch 4_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/4_0_X by this push:
new 677b99f752 [SYNCOPE-1916] Metrics (#1195)
677b99f752 is described below
commit 677b99f75205667f7650f7c9c5f766a3950dab37
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Thu Sep 25 13:55:51 2025 +0200
[SYNCOPE-1916] Metrics (#1195)
---
core/metrics-starter/pom.xml | 68 ++++++++
...nstrumentedPriorityPropagationTaskExecutor.java | 85 ++++++++++
.../security/InstrumentedAuthDataAccessor.java | 171 +++++++++++++++++++++
.../syncope/core/starter/JPAMetricsContext.java | 83 ++++++++++
.../syncope/core/starter/MetricsContext.java | 137 +++++++++++++++++
.../metrics/cache/CacheMetricsContext.java | 27 ++++
...rk.boot.autoconfigure.AutoConfiguration.imports | 19 +++
core/pom.xml | 1 +
core/self-keymaster-starter/pom.xml | 17 ++
.../src/test/resources/core-debug.properties | 2 +-
.../core/spring/security/AuthDataAccessor.java | 2 +-
fit/core-reference/pom.xml | 5 +
.../src/main/resources/core-embedded.properties | 2 +-
.../usage/{usage.adoc => loggers.adoc} | 27 +---
.../asciidoc/reference-guide/usage/metrics.adoc | 55 +++++++
src/main/asciidoc/reference-guide/usage/usage.adoc | 4 +
16 files changed, 681 insertions(+), 24 deletions(-)
diff --git a/core/metrics-starter/pom.xml b/core/metrics-starter/pom.xml
new file mode 100644
index 0000000000..f72bbd153b
--- /dev/null
+++ b/core/metrics-starter/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.syncope</groupId>
+ <artifactId>syncope-core</artifactId>
+ <version>4.0.2-SNAPSHOT</version>
+ </parent>
+
+ <name>Apache Syncope Core Spring Boot Metrics</name>
+ <description>Apache Syncope Core Spring Boot Metrics</description>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-metrics-starter</artifactId>
+ <packaging>jar</packaging>
+
+ <properties>
+ <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-starter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+</project>
diff --git
a/core/metrics-starter/src/main/java/org/apache/syncope/core/provisioning/java/propagation/InstrumentedPriorityPropagationTaskExecutor.java
b/core/metrics-starter/src/main/java/org/apache/syncope/core/provisioning/java/propagation/InstrumentedPriorityPropagationTaskExecutor.java
new file mode 100644
index 0000000000..0ba8fba38f
--- /dev/null
+++
b/core/metrics-starter/src/main/java/org/apache/syncope/core/provisioning/java/propagation/InstrumentedPriorityPropagationTaskExecutor.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.propagation;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import java.util.Collection;
+import
org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.ConnectorManager;
+import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
+import
org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import
org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import
org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
+import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
+import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.core.task.AsyncTaskExecutor;
+
+public class InstrumentedPriorityPropagationTaskExecutor extends
PriorityPropagationTaskExecutor {
+
+ protected final MeterRegistry meterRegistry;
+
+ public InstrumentedPriorityPropagationTaskExecutor(
+ final ConnectorManager connectorManager,
+ final ConnObjectUtils connObjectUtils,
+ final TaskDAO taskDAO,
+ final ExternalResourceDAO resourceDAO,
+ final PlainSchemaDAO plainSchemaDAO,
+ final NotificationManager notificationManager,
+ final AuditManager auditManager,
+ final TaskDataBinder taskDataBinder,
+ final AnyUtilsFactory anyUtilsFactory,
+ final TaskUtilsFactory taskUtilsFactory,
+ final OutboundMatcher outboundMatcher,
+ final PlainAttrValidationManager validator,
+ final ApplicationEventPublisher publisher,
+ final AsyncTaskExecutor taskExecutor,
+ final MeterRegistry meterRegistry) {
+
+ super(connectorManager, connObjectUtils, taskDAO, resourceDAO,
plainSchemaDAO, notificationManager,
+ auditManager, taskDataBinder, anyUtilsFactory,
taskUtilsFactory, outboundMatcher, validator, publisher,
+ taskExecutor);
+ this.meterRegistry = meterRegistry;
+ }
+
+ @Override
+ public PropagationReporter execute(
+ final Collection<PropagationTaskInfo> taskInfos,
+ final boolean nullPriorityAsync,
+ final String executor) {
+
+ PropagationReporter reporter = super.execute(taskInfos,
nullPriorityAsync, executor);
+
+ reporter.getStatuses().forEach(status -> Counter.builder(
+ "syncope.propagation." +
status.getStatus().name().toLowerCase() + ".count").
+ description("The total number of propagations with status " +
status.getStatus().name()).
+ tag("resource", status.getResource()).
+ register(meterRegistry).
+ increment());
+
+ return reporter;
+ }
+}
diff --git
a/core/metrics-starter/src/main/java/org/apache/syncope/core/spring/security/InstrumentedAuthDataAccessor.java
b/core/metrics-starter/src/main/java/org/apache/syncope/core/spring/security/InstrumentedAuthDataAccessor.java
new file mode 100644
index 0000000000..daafe28af8
--- /dev/null
+++
b/core/metrics-starter/src/main/java/org/apache/syncope/core/spring/security/InstrumentedAuthDataAccessor.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.spring.security;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
+import org.apache.syncope.core.persistence.api.EncryptorManager;
+import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.ConnectorManager;
+import org.apache.syncope.core.provisioning.api.MappingManager;
+import
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.core.Authentication;
+
+public class InstrumentedAuthDataAccessor extends AuthDataAccessor {
+
+ protected static final String SUCCESS_TYPE = ".success";
+
+ protected static final String FAILURE_TYPE = ".failure";
+
+ protected static final String MUST_CHANGE_PASSWORD_TYPE = ".failure";
+
+ protected static final String NOT_FOUND_TYPE = ".notfound";
+
+ protected static final String DISABLED_TYPE = ".disabled";
+
+ protected static final Function<String, String> USERNAME = suffix ->
"syncope.auth.username" + suffix + ".count";
+
+ protected static final Function<String, String> JWT = suffix ->
"syncope.auth.jwt" + suffix + ".count";
+
+ protected static final Function<String, String> SUCCESS_DESC =
+ type -> "The total number of succeeded " + type + " logins";
+
+ protected static final Function<String, String> FAILURE_DESC =
+ type -> "The total number of failed " + type + " logins";
+
+ protected static final Function<String, String> MUST_CHANGE_PASSWORD_DESC =
+ type -> "The total number of mustChangePassword users attempting
to perform " + type + " login";
+
+ protected static final Function<String, String> NOT_FOUND_DESC =
+ type -> "The total number of not found users attempting to perform
" + type + " login";
+
+ protected static final Function<String, String> DISABLED_DESC =
+ type -> "The total number of disabled users attempting to perform
" + type + " login";
+
+ protected final MeterRegistry meterRegistry;
+
+ public InstrumentedAuthDataAccessor(
+ final SecurityProperties securityProperties,
+ final EncryptorManager encryptorManager,
+ final RealmSearchDAO realmSearchDAO,
+ final UserDAO userDAO,
+ final GroupDAO groupDAO,
+ final AnySearchDAO anySearchDAO,
+ final AccessTokenDAO accessTokenDAO,
+ final ConfParamOps confParamOps,
+ final RoleDAO roleDAO,
+ final DelegationDAO delegationDAO,
+ final ExternalResourceDAO resourceDAO,
+ final ConnectorManager connectorManager,
+ final AuditManager auditManager,
+ final MappingManager mappingManager,
+ final List<JWTSSOProvider> jwtSSOProviders,
+ final MeterRegistry meterRegistry) {
+
+ super(securityProperties, encryptorManager, realmSearchDAO, userDAO,
groupDAO, anySearchDAO, accessTokenDAO,
+ confParamOps, roleDAO, delegationDAO, resourceDAO,
connectorManager, auditManager, mappingManager,
+ jwtSSOProviders);
+ this.meterRegistry = meterRegistry;
+ }
+
+ @Override
+ public Triple<User, Boolean, String> authenticate(final String domain,
final Authentication authentication) {
+ try {
+ Triple<User, Boolean, String> auth = super.authenticate(domain,
authentication);
+
+ Optional.ofNullable(auth.getLeft()).ifPresentOrElse(
+ user -> {
+ Counter.builder(auth.getMiddle() ?
USERNAME.apply(SUCCESS_TYPE) : USERNAME.apply(FAILURE_TYPE)).
+ description(auth.getMiddle()
+ ? SUCCESS_DESC.apply("username") :
FAILURE_DESC.apply("username")).
+ tag("realm", user.getRealm().getFullPath()).
+ register(meterRegistry).
+ increment();
+ if (user.isMustChangePassword()) {
+
Counter.builder(USERNAME.apply(MUST_CHANGE_PASSWORD_TYPE)).
+
description(MUST_CHANGE_PASSWORD_DESC.apply("username")).
+ register(meterRegistry).
+ increment();
+ }
+ },
+ () -> Counter.builder(USERNAME.apply(NOT_FOUND_TYPE)).
+ description(NOT_FOUND_DESC.apply("username")).
+ register(meterRegistry).
+ increment());
+
+ return auth;
+ } catch (DisabledException e) {
+ Counter.builder(USERNAME.apply(DISABLED_TYPE)).
+ description(DISABLED_DESC.apply("JWT")).
+ register(meterRegistry).
+ increment();
+ throw e;
+ }
+ }
+
+ @Override
+ public Pair<String, Set<SyncopeGrantedAuthority>> authenticate(final
JWTAuthentication authentication) {
+ try {
+ Pair<String, Set<SyncopeGrantedAuthority>> auth =
super.authenticate(authentication);
+
+ if (MUST_CHANGE_PASSWORD_AUTHORITIES.equals(auth.getRight())) {
+ Counter.builder(JWT.apply(MUST_CHANGE_PASSWORD_TYPE)).
+ description(MUST_CHANGE_PASSWORD_DESC.apply("JWT")).
+ register(meterRegistry).
+ increment();
+ } else {
+ Counter.builder(JWT.apply(SUCCESS_TYPE)).
+ description(SUCCESS_DESC.apply("JWT")).
+ register(meterRegistry).
+ increment();
+ }
+
+ return auth;
+ } catch (AuthenticationCredentialsNotFoundException e) {
+ Counter.builder(JWT.apply(NOT_FOUND_TYPE)).
+ description(NOT_FOUND_DESC.apply("JWT")).
+ register(meterRegistry).
+ increment();
+ throw e;
+ } catch (DisabledException e) {
+ Counter.builder(JWT.apply(DISABLED_TYPE)).
+ description(DISABLED_DESC.apply("JWT")).
+ register(meterRegistry).
+ increment();
+ throw e;
+ }
+ }
+}
diff --git
a/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.java
b/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.java
new file mode 100644
index 0000000000..4e455b7e2f
--- /dev/null
+++
b/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.starter;
+
+import com.zaxxer.hikari.HikariDataSource;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.binder.MeterBinder;
+import java.util.List;
+import javax.sql.DataSource;
+import org.apache.syncope.core.persistence.api.DomainHolder;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import
org.springframework.boot.actuate.metrics.data.MetricsRepositoryMethodInvocationListener;
+import org.springframework.boot.actuate.metrics.jdbc.DataSourcePoolMetrics;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import
org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
+import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
+import org.springframework.util.Assert;
+import org.springframework.util.function.SingletonSupplier;
+
+@ConditionalOnClass(HikariDataSource.class)
+@Import(DataSourcePoolMetadataProvidersConfiguration.class)
+@Configuration(proxyBeanMethods = false)
+public class JPAMetricsContext {
+
+ @Bean
+ public MeterBinder dataSourcePoolMetadataMeterBinder(
+ final ObjectProvider<DataSourcePoolMetadataProvider>
metadataProviders,
+ final DomainHolder<DataSource> domainHolder) {
+
+ return new MeterBinder() {
+
+ @Override
+ public void bindTo(final MeterRegistry registry) {
+ List<DataSourcePoolMetadataProvider> metadataProvidersList =
metadataProviders.stream().toList();
+ domainHolder.getDomains().forEach((name, dataSource) -> new
DataSourcePoolMetrics(
+ dataSource, metadataProvidersList, name,
List.of()).bindTo(registry));
+ }
+ };
+ }
+
+ @Bean
+ public static BeanPostProcessor jpaRepositoryFactoryBeanPostProcessor(
+ final ObjectProvider<MetricsRepositoryMethodInvocationListener>
metricsRepositoryMethodInvocationListener) {
+
+ return new BeanPostProcessor() {
+
+ @Override
+ public Object postProcessBeforeInitialization(final Object bean,
final String beanName)
+ throws BeansException {
+
+ if (bean instanceof JpaRepositoryFactory jpaRepositoryFactory)
{
+ MetricsRepositoryMethodInvocationListener listener =
+
SingletonSupplier.of(metricsRepositoryMethodInvocationListener::getObject).get();
+ Assert.state(listener != null, "'listener' must not be
null");
+ jpaRepositoryFactory.addInvocationListener(listener);
+ }
+ return bean;
+ }
+ };
+ }
+}
diff --git
a/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/MetricsContext.java
b/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/MetricsContext.java
new file mode 100644
index 0000000000..f7720ada02
--- /dev/null
+++
b/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/MetricsContext.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.starter;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import java.util.List;
+import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
+import org.apache.syncope.core.persistence.api.EncryptorManager;
+import
org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
+import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.ConnectorManager;
+import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
+import
org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import
org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import
org.apache.syncope.core.provisioning.java.propagation.InstrumentedPriorityPropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
+import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
+import org.apache.syncope.core.spring.security.AuthDataAccessor;
+import org.apache.syncope.core.spring.security.InstrumentedAuthDataAccessor;
+import org.apache.syncope.core.spring.security.JWTSSOProvider;
+import org.apache.syncope.core.spring.security.SecurityProperties;
+import org.springframework.beans.factory.annotation.Qualifier;
+import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.task.AsyncTaskExecutor;
+
+@Configuration(proxyBeanMethods = false)
+public class MetricsContext {
+
+ @ConditionalOnMissingBean(name = "instrumentedAuthDataAccessor")
+ @Bean(name = { "authDataAccessor", "instrumentedAuthDataAccessor" })
+ public AuthDataAccessor instrumentedAuthDataAccessor(
+ final SecurityProperties securityProperties,
+ final EncryptorManager encryptorManager,
+ final RealmSearchDAO realmSearchDAO,
+ final UserDAO userDAO,
+ final GroupDAO groupDAO,
+ final AnySearchDAO anySearchDAO,
+ final AccessTokenDAO accessTokenDAO,
+ final ConfParamOps confParamOps,
+ final RoleDAO roleDAO,
+ final DelegationDAO delegationDAO,
+ final ExternalResourceDAO resourceDAO,
+ final ConnectorManager connectorManager,
+ final AuditManager auditManager,
+ final MappingManager mappingManager,
+ final List<JWTSSOProvider> jwtSSOProviders,
+ final MeterRegistry meterRegistry) {
+
+ return new InstrumentedAuthDataAccessor(
+ securityProperties,
+ encryptorManager,
+ realmSearchDAO,
+ userDAO,
+ groupDAO,
+ anySearchDAO,
+ accessTokenDAO,
+ confParamOps,
+ roleDAO,
+ delegationDAO,
+ resourceDAO,
+ connectorManager,
+ auditManager,
+ mappingManager,
+ jwtSSOProviders,
+ meterRegistry);
+ }
+
+ @ConditionalOnMissingBean(name = "instrumentedPropagationTaskExecutor")
+ @Bean(name = { "propagationTaskExecutor",
"instrumentedPropagationTaskExecutor" })
+ public PropagationTaskExecutor propagationTaskExecutor(
+ @Qualifier("propagationTaskExecutorAsyncExecutor")
+ final AsyncTaskExecutor propagationTaskExecutorAsyncExecutor,
+ final TaskUtilsFactory taskUtilsFactory,
+ final AnyUtilsFactory anyUtilsFactory,
+ final ConnectorManager connectorManager,
+ final ConnObjectUtils connObjectUtils,
+ final TaskDAO taskDAO,
+ final ExternalResourceDAO resourceDAO,
+ final PlainSchemaDAO plainSchemaDAO,
+ final NotificationManager notificationManager,
+ final AuditManager auditManager,
+ final TaskDataBinder taskDataBinder,
+ final OutboundMatcher outboundMatcher,
+ final PlainAttrValidationManager validator,
+ final ApplicationEventPublisher publisher,
+ final MeterRegistry meterRegistry) {
+
+ return new InstrumentedPriorityPropagationTaskExecutor(
+ connectorManager,
+ connObjectUtils,
+ taskDAO,
+ resourceDAO,
+ plainSchemaDAO,
+ notificationManager,
+ auditManager,
+ taskDataBinder,
+ anyUtilsFactory,
+ taskUtilsFactory,
+ outboundMatcher,
+ validator,
+ publisher,
+ propagationTaskExecutorAsyncExecutor,
+ meterRegistry);
+ }
+}
diff --git
a/core/metrics-starter/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsContext.java
b/core/metrics-starter/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsContext.java
new file mode 100644
index 0000000000..da04a795a8
--- /dev/null
+++
b/core/metrics-starter/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsContext.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.springframework.boot.actuate.autoconfigure.metrics.cache;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Import({ CacheMeterBinderProvidersConfiguration.class,
CacheMetricsRegistrarConfiguration.class })
+@Configuration(proxyBeanMethods = false)
+public class CacheMetricsContext {
+}
diff --git
a/core/metrics-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
b/core/metrics-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000000..4ceb3ba1af
--- /dev/null
+++
b/core/metrics-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+org.apache.syncope.core.starter.MetricsContext
+org.apache.syncope.core.starter.JPAMetricsContext
+org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsContext
diff --git a/core/pom.xml b/core/pom.xml
index 6f7912ca73..571c25a44a 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -91,6 +91,7 @@ under the License.
<module>workflow-java</module>
<module>starter</module>
<module>self-keymaster-starter</module>
+ <module>metrics-starter</module>
<module>persistence-jpa-upgrader</module>
</modules>
</project>
diff --git a/core/self-keymaster-starter/pom.xml
b/core/self-keymaster-starter/pom.xml
index 0d0b615fcc..ea55b47b6e 100644
--- a/core/self-keymaster-starter/pom.xml
+++ b/core/self-keymaster-starter/pom.xml
@@ -102,6 +102,23 @@ under the License.
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-metrics-starter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.syncope.core.idm</groupId>
+ <artifactId>syncope-core-idm-rest-cxf</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.syncope.core.am</groupId>
+ <artifactId>syncope-core-am-rest-cxf</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
diff --git
a/core/self-keymaster-starter/src/test/resources/core-debug.properties
b/core/self-keymaster-starter/src/test/resources/core-debug.properties
index 6328313cfd..05e963d862 100644
--- a/core/self-keymaster-starter/src/test/resources/core-debug.properties
+++ b/core/self-keymaster-starter/src/test/resources/core-debug.properties
@@ -20,7 +20,7 @@ service.discovery.address=http://localhost:9080/syncope/rest/
logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
-management.endpoints.web.exposure.include=health,info,beans,env,loggers,entityCache
+management.endpoints.web.exposure.include=health,info,beans,env,loggers,entityCache,metrics
keymaster.address=http://localhost:9080/syncope/rest/keymaster
keymaster.username=${anonymousUser}
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
index 3f03c8a3e0..f251b9b946 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
@@ -251,7 +251,7 @@ public class AuthDataAccessor {
}
if (userModified) {
- userDAO.save(user);
+ user = userDAO.save(user);
}
}
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 50d4770937..8502930dc9 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -69,6 +69,11 @@ under the License.
<artifactId>syncope-core-self-keymaster-starter</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-metrics-starter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.syncope.common.keymaster</groupId>
diff --git a/fit/core-reference/src/main/resources/core-embedded.properties
b/fit/core-reference/src/main/resources/core-embedded.properties
index ae52611280..d44c2cc484 100644
--- a/fit/core-reference/src/main/resources/core-embedded.properties
+++ b/fit/core-reference/src/main/resources/core-embedded.properties
@@ -16,7 +16,7 @@
# under the License.
embedded.databases=syncope,syncopetwo,syncopetest
-management.endpoints.web.exposure.include=health,info,beans,env,loggers,entityCache
+management.endpoints.web.exposure.include=health,info,beans,env,loggers,entityCache,metrics
keymaster.address=http://localhost:9080/syncope/rest/keymaster
keymaster.username=${anonymousUser}
diff --git a/src/main/asciidoc/reference-guide/usage/usage.adoc
b/src/main/asciidoc/reference-guide/usage/loggers.adoc
similarity index 54%
copy from src/main/asciidoc/reference-guide/usage/usage.adoc
copy to src/main/asciidoc/reference-guide/usage/loggers.adoc
index cc5e240be5..fd96f23c90 100644
--- a/src/main/asciidoc/reference-guide/usage/usage.adoc
+++ b/src/main/asciidoc/reference-guide/usage/loggers.adoc
@@ -16,26 +16,11 @@
// specific language governing permissions and limitations
// under the License.
//
-== Usage
+=== Loggers
-Before proceeding, please ensure that you have access to a running Apache
Syncope deployment.
-You can take a look at the
-ifeval::["{backend}" == "html5"]
-https://syncope.apache.org/docs/4.0/getting-started.html[Apache Syncope
Getting Started Guide]
-endif::[]
-ifeval::["{backend}" == "pdf"]
-https://syncope.apache.org/docs/4.0/getting-started.pdf[Apache Syncope Getting
Started Guide]
-endif::[]
-to check system requirements and to choose among the various options for
obtaining Apache Syncope.
+Spring Boot actuator includes the ability to
+https://docs.spring.io/spring-boot/3.4/reference/actuator/loggers.html[view
and configure the log levels^] of all
+Syncope modules at runtime.
-include::adminconsole.adoc[]
-
-include::enduser.adoc[]
-
-include::core.adoc[]
-
-include::clientlibrary.adoc[]
-
-include::customization.adoc[]
-
-include::actuator.adoc[]
+In addition, Console provides a UI to view the list of logger’s configuration
and set their level, for all
+Syncope modules.
diff --git a/src/main/asciidoc/reference-guide/usage/metrics.adoc
b/src/main/asciidoc/reference-guide/usage/metrics.adoc
new file mode 100644
index 0000000000..98a697acbe
--- /dev/null
+++ b/src/main/asciidoc/reference-guide/usage/metrics.adoc
@@ -0,0 +1,55 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+=== Metrics
+
+Monitoring performance to ensure reliability and efficiency can be achieved by
leveraging
+https://docs.spring.io/spring-boot/3.4/reference/actuator/metrics.html[Spring
Boot's metrics^].
+
+[[metrics-core]]
+==== Core
+
+This can be enabled by adding the following dependency to `core/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-metrics-starter</artifactId>
+ <version>${syncope.version}</version>
+</dependency>
+----
+
+Additional dependencies might be required, depending on the
+https://docs.spring.io/spring-boot/3.4/reference/actuator/metrics.html#actuator.metrics.export[actual
monitoring system in use^].
+
+[[metrics-wa]]
+==== WA
+
+This can be enabled by adding the following dependency to `wa/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+ <groupId>org.apereo.cas</groupId>
+ <artifactId>cas-server-support-metrics</artifactId>
+ <version>${cas.version}</version>
+</dependency>
+----
+
+For further options and configuration, refer to
https://apereo.github.io/cas/7.2.x/monitoring/Configuring-Metrics.html[CAS
documentation^].
diff --git a/src/main/asciidoc/reference-guide/usage/usage.adoc
b/src/main/asciidoc/reference-guide/usage/usage.adoc
index cc5e240be5..778903020b 100644
--- a/src/main/asciidoc/reference-guide/usage/usage.adoc
+++ b/src/main/asciidoc/reference-guide/usage/usage.adoc
@@ -39,3 +39,7 @@ include::clientlibrary.adoc[]
include::customization.adoc[]
include::actuator.adoc[]
+
+include::loggers.adoc[]
+
+include::metrics.adoc[]