From 6fe4b3637f8d6fd8f26959695ab17aba218b6d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Thu, 25 Sep 2025 08:48:59 +0200 Subject: [PATCH 1/2] [SYNCOPE-1916] Metrics --- core/metrics-starter/pom.xml | 68 +++++++ ...mentedPriorityPropagationTaskExecutor.java | 85 +++++++++ .../InstrumentedAuthDataAccessor.java | 171 ++++++++++++++++++ .../core/starter/JPAMetricsContext.java | 85 +++++++++ .../syncope/core/starter/MetricsContext.java | 137 ++++++++++++++ .../metrics/cache/CacheMetricsContext.java | 27 +++ ...ot.autoconfigure.AutoConfiguration.imports | 19 ++ core/pom.xml | 1 + core/self-keymaster-starter/pom.xml | 17 ++ .../src/test/resources/core-debug.properties | 2 +- .../spring/security/AuthDataAccessor.java | 2 +- fit/core-reference/pom.xml | 5 + .../main/resources/core-embedded.properties | 2 +- .../reference-guide/usage/loggers.adoc | 26 +++ .../reference-guide/usage/metrics.adoc | 55 ++++++ .../asciidoc/reference-guide/usage/usage.adoc | 4 + 16 files changed, 703 insertions(+), 3 deletions(-) create mode 100644 core/metrics-starter/pom.xml create mode 100644 core/metrics-starter/src/main/java/org/apache/syncope/core/provisioning/java/propagation/InstrumentedPriorityPropagationTaskExecutor.java create mode 100644 core/metrics-starter/src/main/java/org/apache/syncope/core/spring/security/InstrumentedAuthDataAccessor.java create mode 100644 core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.java create mode 100644 core/metrics-starter/src/main/java/org/apache/syncope/core/starter/MetricsContext.java create mode 100644 core/metrics-starter/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsContext.java create mode 100644 core/metrics-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 src/main/asciidoc/reference-guide/usage/loggers.adoc create mode 100644 src/main/asciidoc/reference-guide/usage/metrics.adoc diff --git a/core/metrics-starter/pom.xml b/core/metrics-starter/pom.xml new file mode 100644 index 00000000000..f72bbd153b3 --- /dev/null +++ b/core/metrics-starter/pom.xml @@ -0,0 +1,68 @@ + + + + + 4.0.0 + + + org.apache.syncope + syncope-core + 4.0.2-SNAPSHOT + + + Apache Syncope Core Spring Boot Metrics + Apache Syncope Core Spring Boot Metrics + org.apache.syncope.core + syncope-core-metrics-starter + jar + + + ${basedir}/../.. + + + + + org.apache.syncope.core + syncope-core-starter + ${project.version} + + + + io.micrometer + micrometer-core + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + + + src/main/resources + true + + + + 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 00000000000..0ba8fba38f8 --- /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 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 00000000000..daafe28af89 --- /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 USERNAME = suffix -> "syncope.auth.username" + suffix + ".count"; + + protected static final Function JWT = suffix -> "syncope.auth.jwt" + suffix + ".count"; + + protected static final Function SUCCESS_DESC = + type -> "The total number of succeeded " + type + " logins"; + + protected static final Function FAILURE_DESC = + type -> "The total number of failed " + type + " logins"; + + protected static final Function MUST_CHANGE_PASSWORD_DESC = + type -> "The total number of mustChangePassword users attempting to perform " + type + " login"; + + protected static final Function NOT_FOUND_DESC = + type -> "The total number of not found users attempting to perform " + type + " login"; + + protected static final Function 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 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 authenticate(final String domain, final Authentication authentication) { + try { + Triple 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> authenticate(final JWTAuthentication authentication) { + try { + Pair> 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 00000000000..91b92236806 --- /dev/null +++ b/core/metrics-starter/src/main/java/org/apache/syncope/core/starter/JPAMetricsContext.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.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.beans.factory.config.ConfigurableListableBeanFactory; +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 ConfigurableListableBeanFactory beanFactory, + final ObjectProvider metadataProviders, + final DomainHolder domainHolder) { + + return new MeterBinder() { + + @Override + public void bindTo(final MeterRegistry registry) { + List 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) { + + 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 00000000000..f7720ada027 --- /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 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 00000000000..da04a795a89 --- /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 00000000000..4ceb3ba1afd --- /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 6f7912ca735..571c25a44ab 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -91,6 +91,7 @@ under the License. workflow-java starter self-keymaster-starter + metrics-starter persistence-jpa-upgrader diff --git a/core/self-keymaster-starter/pom.xml b/core/self-keymaster-starter/pom.xml index 0d0b615fccc..ea55b47b6e5 100644 --- a/core/self-keymaster-starter/pom.xml +++ b/core/self-keymaster-starter/pom.xml @@ -102,6 +102,23 @@ under the License. + + org.apache.syncope.core + syncope-core-metrics-starter + ${project.version} + + + + org.apache.syncope.core.idm + syncope-core-idm-rest-cxf + ${project.version} + + + org.apache.syncope.core.am + syncope-core-am-rest-cxf + ${project.version} + + org.postgresql postgresql 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 6328313cfd3..05e963d8628 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 3f03c8a3e08..f251b9b9461 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 Triple authenticate(final String domain, final Aut } if (userModified) { - userDAO.save(user); + user = userDAO.save(user); } } diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index 50d4770937d..8502930dc95 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -69,6 +69,11 @@ under the License. syncope-core-self-keymaster-starter ${project.version} + + org.apache.syncope.core + syncope-core-metrics-starter + ${project.version} + org.apache.syncope.common.keymaster diff --git a/fit/core-reference/src/main/resources/core-embedded.properties b/fit/core-reference/src/main/resources/core-embedded.properties index ae526112808..d44c2cc4846 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/loggers.adoc b/src/main/asciidoc/reference-guide/usage/loggers.adoc new file mode 100644 index 00000000000..fd96f23c90a --- /dev/null +++ b/src/main/asciidoc/reference-guide/usage/loggers.adoc @@ -0,0 +1,26 @@ +// +// 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. +// +=== Loggers + +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. + +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 00000000000..98a697acbe2 --- /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"] +---- + + org.apache.syncope.core + syncope-core-metrics-starter + ${syncope.version} + +---- + +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"] +---- + + org.apereo.cas + cas-server-support-metrics + ${cas.version} + +---- + +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 cc5e240be58..778903020b8 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[] From fd9648e79b72de7cfcedecb0eb1ae23898f37da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Thu, 25 Sep 2025 12:31:47 +0200 Subject: [PATCH 2/2] Useless parameter --- .../java/org/apache/syncope/core/starter/JPAMetricsContext.java | 2 -- 1 file changed, 2 deletions(-) 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 index 91b92236806..4e455b7e2f5 100644 --- 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 @@ -27,7 +27,6 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.actuate.metrics.data.MetricsRepositoryMethodInvocationListener; import org.springframework.boot.actuate.metrics.jdbc.DataSourcePoolMetrics; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -47,7 +46,6 @@ public class JPAMetricsContext { @Bean public MeterBinder dataSourcePoolMetadataMeterBinder( - final ConfigurableListableBeanFactory beanFactory, final ObjectProvider metadataProviders, final DomainHolder domainHolder) {