Skip to content

Prometheus monitoring #190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

// monitoring
implementation 'io.micronaut.micrometer:micronaut-micrometer-registry-prometheus:5.8.0'
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.3.4'

// springdoc
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${springDocVersion}"
implementation "org.springdoc:springdoc-openapi-starter-common:${springDocVersion}"
Expand Down
18 changes: 14 additions & 4 deletions settings/application-default.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,23 @@ spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
# Logging settings
logging.level.root=ERROR
logging.level.edu.kit.datamanager=INFO
logging.level.edu.kit.datamanager=DEBUG
springdoc.swagger-ui.disable-swagger-default-url=true
# Actuator settings
info.app.name=Mapping-Service
info.app.description=Generic mapping service supporting different mapping implementations.
info.app.group=edu.kit.datamanager
info.app.version=1.0.4
management.endpoint.health.probes.enabled=true
management.endpoints.web.exposure.include=*
management.health.rabbit.enabled:false
management.health.elasticsearch.enabled:false
management.endpoint.health.enabled: true
management.endpoint.health.show-details: when-authorized
management.endpoint.health.sensitive: true
management.endpoints.web.exposure.include: health,info

#spring.security.user.name=admin
#spring.security.user.password=secret
#spring.security.user.roles=ADMIN

###############################################################################
# Spring Cloud
###############################################################################
Expand All @@ -56,6 +62,10 @@ mapping-service.pluginLocation=file://INSTALLATION_DIR/plugins
mapping-service.mappingSchemasLocation=file://INSTALLATION_DIR/mappingSchemas
# Folder where job output files for async mapping executions are stored
mapping-service.jobOutput=file://INSTALLATION_DIR/jobOutput

management.metrics.export.prometheus.enabled=true
management.endpoint.metrics.enabled=true

# Execution timeout for script calls
mapping-service.executionTimeout=30

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import edu.kit.datamanager.mappingservice.configuration.ApplicationProperties;
import edu.kit.datamanager.mappingservice.plugins.PluginLoader;
import edu.kit.datamanager.mappingservice.plugins.PluginManager;
import io.micrometer.core.instrument.MeterRegistry;
import edu.kit.datamanager.mappingservice.plugins.PluginLoader;
import edu.kit.datamanager.mappingservice.util.PythonRunnerUtil;
import edu.kit.datamanager.mappingservice.util.ShellRunnerUtil;
import edu.kit.datamanager.security.filter.KeycloakJwtProperties;
Expand All @@ -13,6 +14,7 @@
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
Expand All @@ -32,6 +34,13 @@ public class MappingServiceApplication {

private static final Logger LOG = LoggerFactory.getLogger(MappingServiceApplication.class);

@Autowired
private final MeterRegistry meterRegistry;

MappingServiceApplication(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}

@Bean
public ApplicationProperties applicationProperties() {
return new ApplicationProperties();
Expand All @@ -46,7 +55,7 @@ public PluginLoader pluginLoader() {
public PluginManager pluginManager() {
PythonRunnerUtil.init(applicationProperties());
ShellRunnerUtil.init(applicationProperties());
return new PluginManager(applicationProperties(), pluginLoader());
return new PluginManager(applicationProperties(), pluginLoader(), meterRegistry);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
*/
package edu.kit.datamanager.mappingservice.configuration;

import edu.kit.datamanager.mappingservice.rest.impl.PreHandleInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
Expand All @@ -28,10 +31,16 @@
*/
@Configuration
public class StaticResourcesConfiguration implements WebMvcConfigurer {
private final PreHandleInterceptor preHandleInterceptor;

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/static/"};

@Autowired
public StaticResourcesConfiguration(PreHandleInterceptor preHandleInterceptor) {
this.preHandleInterceptor = preHandleInterceptor;
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
Expand All @@ -43,4 +52,9 @@ public void configurePathMatch(PathMatchConfigurer configurer) {
urlPathHelper.setUrlDecode(false);
configurer.setUrlPathHelper(urlPathHelper);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(preHandleInterceptor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.config.Customizer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
Expand Down Expand Up @@ -105,6 +106,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
requestMatchers(AUTH_WHITELIST_SWAGGER_UI).permitAll().
anyRequest().authenticated()
).
httpBasic(Customizer.withDefaults()).
cors(cors -> cors.configurationSource(corsConfigurationSource())).
sessionManagement(
session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import edu.kit.datamanager.mappingservice.plugins.MappingPluginState;
import edu.kit.datamanager.mappingservice.plugins.PluginManager;
import edu.kit.datamanager.mappingservice.util.FileUtil;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
Expand Down Expand Up @@ -89,16 +91,18 @@ public class MappingService {
*/
private Path jobsOutputDirectory;

private final ApplicationProperties applicationProperties;
private ApplicationProperties applicationProperties;
private final MeterRegistry meterRegistry;

/**
* Logger for this class.
*/
private final static Logger LOGGER = LoggerFactory.getLogger(MappingService.class);

@Autowired
public MappingService(ApplicationProperties applicationProperties) {
public MappingService(ApplicationProperties applicationProperties, MeterRegistry meterRegistry) {
this.applicationProperties = applicationProperties;
this.meterRegistry = meterRegistry;
init(this.applicationProperties);
}

Expand Down Expand Up @@ -212,6 +216,9 @@ public Optional<Path> executeMapping(URI contentUrl, String mappingId) throws Ma
if (optionalMappingRecord.isPresent()) {
LOGGER.trace("Mapping for id {} found.", mappingId);
mappingRecord = optionalMappingRecord.get();

Counter.builder("mapping_service.plugin_usage").tag("plugin", mappingRecord.getMappingType()).register(meterRegistry).increment();

Path mappingFile = Paths.get(mappingRecord.getMappingDocumentUri());
// execute mapping
Path resultFile;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package edu.kit.datamanager.mappingservice.plugins;

import edu.kit.datamanager.mappingservice.configuration.ApplicationProperties;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import edu.kit.datamanager.mappingservice.exception.MappingServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -62,10 +64,12 @@ public class PluginManager {
* instantiation time.
*/
@Autowired
public PluginManager(ApplicationProperties applicationProperties, PluginLoader pluginLoader) {
public PluginManager(ApplicationProperties applicationProperties, PluginLoader pluginLoader, MeterRegistry meterRegistry) {
this.applicationProperties = applicationProperties;
this.pluginLoader = pluginLoader;
reloadPlugins();

Gauge.builder("mapping_service.plugins_total", () -> plugins.size()).register(meterRegistry);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import edu.kit.datamanager.mappingservice.rest.PluginInformation;
import edu.kit.datamanager.util.AuthenticationHelper;
import edu.kit.datamanager.util.ControllerUtils;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.swagger.v3.core.util.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -86,10 +88,12 @@ public class MappingAdministrationController implements IMappingAdministrationCo
*/
private final MappingService mappingService;

public MappingAdministrationController(IMappingRecordDao mappingRecordDao, PluginManager pluginManager, MappingService mappingService) {
public MappingAdministrationController(IMappingRecordDao mappingRecordDao, PluginManager pluginManager, MappingService mappingService, MeterRegistry meterRegistry) {
this.mappingRecordDao = mappingRecordDao;
this.mappingService = mappingService;
this.pluginManager = pluginManager;

Gauge.builder("mapping_service.schemes_total", mappingRecordDao::count).register(meterRegistry);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
import edu.kit.datamanager.mappingservice.plugins.MappingPluginState;
import edu.kit.datamanager.mappingservice.rest.IMappingExecutionController;
import edu.kit.datamanager.mappingservice.util.FileUtil;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.MeterRegistry;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -72,11 +75,17 @@ public class MappingExecutionController implements IMappingExecutionController {
private final MappingService mappingService;
protected JobManager jobManager;
private final IMappingRecordDao mappingRecordDao;
private final MeterRegistry meterRegistry;
private final DistributionSummary documentsInSizeMetric;
private final DistributionSummary documentsOutSizeMetric;

public MappingExecutionController(MappingService mappingService, IMappingRecordDao mappingRecordDao, JobManager jobManager) {
public MappingExecutionController(MappingService mappingService, IMappingRecordDao mappingRecordDao, JobManager jobManager, MeterRegistry meterRegistry) {
this.mappingService = mappingService;
this.mappingRecordDao = mappingRecordDao;
this.jobManager = jobManager;
this.meterRegistry = meterRegistry;
this.documentsInSizeMetric = DistributionSummary.builder("mapping_service.documents.input_size").baseUnit("bytes").register(meterRegistry);
this.documentsOutSizeMetric = DistributionSummary.builder("mapping_service.documents.output_size").baseUnit("bytes").register(meterRegistry);
}

@Override
Expand Down Expand Up @@ -161,6 +170,10 @@ public void mapDocument(MultipartFile document, String mappingID, HttpServletReq
LOG.error(message, ex);
throw new MappingServiceException(message);
} finally {
Counter.builder("mapping_service.mapping_usage").tag("mappingID", mappingID).register(meterRegistry).increment();
this.documentsInSizeMetric.record(document.getSize());
this.documentsOutSizeMetric.record(result.toFile().length());

LOG.trace("Result file successfully transferred to client. Removing file {} from disk.", result);
try {
Files.delete(result);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package edu.kit.datamanager.mappingservice.rest.impl;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.HandlerInterceptor;

import java.security.MessageDigest;
import java.util.HashSet;

@Service
public class PreHandleInterceptor implements HandlerInterceptor {
private final HashSet<String> uniqueUsers = new HashSet<>();
private final Counter counter;

/**
* Logger for this class.
*/
private final static Logger LOGGER = LoggerFactory.getLogger(PreHandleInterceptor.class);

@Autowired
PreHandleInterceptor(MeterRegistry meterRegistry) {
Gauge.builder("mapping_service.unique_users", uniqueUsers::size).register(meterRegistry);
counter = Counter.builder("mapping_service.requests_served").register(meterRegistry);
}

@Override
public boolean preHandle(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler) throws Exception {
String forwardedFor = request.getHeader("X-Forwarded-For");
LOGGER.debug("X-Forwarded-For: {}", forwardedFor);
String clientIp = null;

if (forwardedFor != null) {
String[] ipList = forwardedFor.split(", ");
if (ipList.length > 0) clientIp = ipList[0];
LOGGER.debug("Client IP from X-Forwarded-For: {}", clientIp);
}

String remoteIp = request.getRemoteAddr();
LOGGER.debug("Client IP from getRemoteAddr: {}", remoteIp);
String ip = clientIp == null ? remoteIp : clientIp;
LOGGER.debug("Using {} for monitoring", ip);

MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(ip.getBytes());
uniqueUsers.add(new String(messageDigest.digest()));

counter.increment();

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import edu.kit.datamanager.mappingservice.configuration.ApplicationProperties;
import edu.kit.datamanager.mappingservice.plugins.PluginLoader;
import edu.kit.datamanager.mappingservice.plugins.PluginManager;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
Expand All @@ -18,6 +20,8 @@
@Configuration
@ComponentScan("edu.kit.datamanager.mappingservice")
public class TestConfig {
@Autowired
private MeterRegistry meterRegistry;

@Bean
public ApplicationProperties applicationProperties() {
Expand All @@ -31,6 +35,6 @@ public PluginLoader pluginLoader() {

@Bean
public PluginManager pluginManager() {
return new PluginManager(applicationProperties(), pluginLoader());
return new PluginManager(applicationProperties(), pluginLoader(), meterRegistry);
}
}
Loading
Loading