From 93680e50d5374c24dadcb02205f8cc64c83b45cf Mon Sep 17 00:00:00 2001 From: Swordsteel Date: Fri, 20 Dec 2024 17:09:22 +0100 Subject: [PATCH] Add MeterRegistry monitoring - update and cleanup in README.md - extract webclient call from registerDevice and add metrics in DeviceRegistryService - add WebClientCalls.kt - update kotlin logging dependency - add MeterRegistry dependency --- README.md | 71 +++++++++---------- build.gradle.kts | 18 ++++- gradle.properties | 2 +- gradlew | 0 release.sh | 0 .../hlaeja/service/DeviceRegistryService.kt | 38 +++++++--- src/main/kotlin/ltd/hlaeja/util/Helper.kt | 6 +- .../kotlin/ltd/hlaeja/util/WebClientCalls.kt | 17 +++++ src/main/resources/application.yml | 38 ++++++++++ 9 files changed, 138 insertions(+), 52 deletions(-) mode change 100644 => 100755 gradlew mode change 100644 => 100755 release.sh create mode 100644 src/main/kotlin/ltd/hlaeja/util/WebClientCalls.kt diff --git a/README.md b/README.md index 81b9406..bba12aa 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,24 @@ Classes and endpoints, to shape and to steer, Devices and sensors, their purpose ## Properties for deployment -| name | required | info | -|-------------------------------|----------|-------------------------| -| spring.profiles.active | * | Spring Boot environment | -| server.port | * | HTTP port | -| server.ssl.enabled | * | HTTP Enable SSL | -| server.ssl.key-store | * | HTTP Keystore | -| server.ssl.key-store-type | * | HTTP Cert Type | -| server.ssl.key-store-password | ** | HTTP Cert Pass | -| device-registry.url | * | Device Register URL | +| name | required | info | +|----------------------------------------------|:--------:|----------------------------------------------| +| spring.profiles.active | ✓ | Spring Boot environment | +| server.port | ✓ | HTTP port | +| server.ssl.enabled | ✓ | HTTP Enable SSL | +| server.ssl.key-store | ✓ | HTTP Keystore | +| server.ssl.key-store-type | ✓ | HTTP Cert Type | +| server.ssl.key-store-password | ✗ | HTTP Cert Pass | +| device-registry.url | ✓ | Device Register URL | +| management.influx.metrics.export.api-version | | InfluxDB API version | +| management.influx.metrics.export.enabled | | Enable/Disable exporting metrics to InfluxDB | +| management.influx.metrics.export.bucket | ✓ | InfluxDB bucket name | +| management.influx.metrics.export.org | ✓ | InfluxDB organization | +| management.influx.metrics.export.token | ✗ | InfluxDB token | +| management.influx.metrics.export.uri | ✓ | InfluxDB URL | +| management.metrics.tags.application | ✓ | Application instance tag for metrics | -Required: * can be stored as text, and ** need to be stored as secret. +*Required: ✓ can be stored as text, and ✗ need to be stored as secret.* ## Releasing Service @@ -22,39 +29,29 @@ Run `release.sh` script from `master` branch. ## Development Configuration + ### Developer Keystore -1. Open `hosts` file: - * On Unix-like systems (Linux, macOS), this directory is typically `/etc/hosts`. - * On Windows, this directory is typically `%SystemRoot%\System32\drivers\etc\hosts`. +We use a keystore to enable HTTPS for our API. To set up your developer environment for local development, please refer to [generate keystore](https://github.com/swordsteel/hlaeja-development/blob/master/doc/keystore.md) documentation. When generating and exporting the certificate for local development, please store it in the `./cert/keystore.p12` folder at the project root. -2. Add the following lines to the `hosts` file: - ```text - 127.0.0.1 registryapi # Hlæja Registry API - ``` +### Global Settings -3. Generate Keystores - ```shell - keytool -genkeypair -alias registry-api -keyalg RSA -keysize 2048 -validity 3650 -dname "CN=registryapi" -keypass password -keystore ./cert/keystore.p12 -storetype PKCS12 -storepass password - ``` +This services rely on a set of global settings to configure development environments. These settings, managed through Gradle properties or environment variables. -4. Export the public certificate - ```shell - keytool -export -alias registry-api -keystore ./cert/keystore.p12 -storepass password -file ./cert/registry-api.cer -rfc - ``` +*Note: For more information on global properties, please refer to our [global settings](https://github.com/swordsteel/hlaeja-development/blob/master/doc/global_settings.md) documentation.* -### Global gradle properties +#### Gradle Properties -To authenticate with Gradle to access repositories that require authentication, you can set your user and token in the `gradle.properties` file. +```properties +repository.user=your_user +repository.token=your_token_value +influxdb.token=your_token_value +``` -Here's how you can do it: +#### Environment Variables -1. Open or create the `gradle.properties` file in your Gradle user home directory: - - On Unix-like systems (Linux, macOS), this directory is typically `~/.gradle/`. - - On Windows, this directory is typically `C:\Users\\.gradle\`. -2. Add the following lines to the `gradle.properties` file: - ```properties - repository.user=your_user - repository.token=your_token_value - ``` - or use environment variables `REPOSITORY_USER` and `REPOSITORY_TOKEN` +```properties +REPOSITORY_USER=your_user +REPOSITORY_TOKEN=your_token_value +INFLUXDB_TOKEN=your_token_value +``` diff --git a/build.gradle.kts b/build.gradle.kts index b0ee77e..a0f3ec0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer + plugins { alias(hlaeja.plugins.kotlin.jvm) alias(hlaeja.plugins.kotlin.spring) @@ -13,6 +15,7 @@ dependencies { implementation(hlaeja.kotlin.reflect) implementation(hlaeja.kotlinx.coroutines) implementation(hlaeja.library.hlaeja.common.messages) + implementation(hlaeja.micrometer.registry.influx) implementation(hlaeja.springboot.starter.actuator) implementation(hlaeja.springboot.starter.webflux) @@ -27,6 +30,17 @@ dependencies { group = "ltd.hlaeja" -tasks.named("processResources") { - dependsOn("copyCertificates") +fun influxDbToken(): String = config.findOrDefault("influxdb.token", "INFLUXDB_TOKEN", "") + +tasks { + named("containerCreate", DockerCreateContainer::class) { + withEnvVar("MANAGEMENT_INFLUX_METRICS_EXPORT_TOKEN", influxDbToken()) + } + withType { + filesMatching("**/application.yml") { filter { it.replace("%INFLUXDB_TOKEN%", influxDbToken()) } } + onlyIf { file("src/main/resources/application.yml").exists() } + } + named("processResources") { + dependsOn("copyCertificates") + } } diff --git a/gradle.properties b/gradle.properties index 9cf5922..32de35c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ kotlin.code.style=official version=0.1.0-SNAPSHOT -catalog=0.6.0 +catalog=0.7.0-SNAPSHOT docker.port.expose=8443 container.port.expose=8443 container.port.host=9040 diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/release.sh b/release.sh old mode 100644 new mode 100755 diff --git a/src/main/kotlin/ltd/hlaeja/service/DeviceRegistryService.kt b/src/main/kotlin/ltd/hlaeja/service/DeviceRegistryService.kt index 806b5d5..381c7a0 100644 --- a/src/main/kotlin/ltd/hlaeja/service/DeviceRegistryService.kt +++ b/src/main/kotlin/ltd/hlaeja/service/DeviceRegistryService.kt @@ -1,24 +1,46 @@ package ltd.hlaeja.service +import io.github.oshai.kotlinlogging.KotlinLogging +import io.micrometer.core.instrument.Counter +import io.micrometer.core.instrument.MeterRegistry import ltd.hlaeja.library.deviceRegistry.Device -import ltd.hlaeja.util.logCall import ltd.hlaeja.property.DeviceRegistryProperty -import org.springframework.http.HttpStatus.REQUEST_TIMEOUT +import ltd.hlaeja.util.deviceRegistryCreateDevice +import org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE import org.springframework.stereotype.Service +import org.springframework.web.ErrorResponseException import org.springframework.web.reactive.function.client.WebClient -import org.springframework.web.reactive.function.client.awaitBodyOrNull +import org.springframework.web.reactive.function.client.WebClientRequestException import org.springframework.web.server.ResponseStatusException +private val log = KotlinLogging.logger {} + @Service class DeviceRegistryService( + meterRegistry: MeterRegistry, private val webClient: WebClient, private val deviceRegistryProperty: DeviceRegistryProperty, ) { + + private val registerDeviceSuccess = Counter.builder("device.registry.success") + .description("Number of successful device registrations") + .register(meterRegistry) + + private val registerDeviceFailure = Counter.builder("device.registry.failure") + .description("Number of failed device registrations") + .register(meterRegistry) + suspend fun registerDevice( request: Device.Request, - ): Device.Response = webClient.post() - .uri("${deviceRegistryProperty.url}/device".also(::logCall)) - .bodyValue(request) - .retrieve() - .awaitBodyOrNull() ?: throw ResponseStatusException(REQUEST_TIMEOUT) + ): Device.Response = try { + webClient.deviceRegistryCreateDevice(request, deviceRegistryProperty) + .also { registerDeviceSuccess.increment() } + } catch (e: ErrorResponseException) { + registerDeviceFailure.increment() + throw e + } catch (e: WebClientRequestException) { + registerDeviceFailure.increment() + log.error(e) { "Error device registry" } + throw ResponseStatusException(SERVICE_UNAVAILABLE) + } } diff --git a/src/main/kotlin/ltd/hlaeja/util/Helper.kt b/src/main/kotlin/ltd/hlaeja/util/Helper.kt index f77be8e..64b7734 100644 --- a/src/main/kotlin/ltd/hlaeja/util/Helper.kt +++ b/src/main/kotlin/ltd/hlaeja/util/Helper.kt @@ -1,9 +1,7 @@ package ltd.hlaeja.util -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging private val log = KotlinLogging.logger {} -fun logCall(url: String) { - log.debug("calling: {}", url) -} +fun logCall(url: String) = log.debug { "calling: $url" } diff --git a/src/main/kotlin/ltd/hlaeja/util/WebClientCalls.kt b/src/main/kotlin/ltd/hlaeja/util/WebClientCalls.kt new file mode 100644 index 0000000..312f795 --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/util/WebClientCalls.kt @@ -0,0 +1,17 @@ +package ltd.hlaeja.util + +import ltd.hlaeja.library.deviceRegistry.Device +import ltd.hlaeja.property.DeviceRegistryProperty +import org.springframework.http.HttpStatus.REQUEST_TIMEOUT +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.awaitBodyOrNull +import org.springframework.web.server.ResponseStatusException + +suspend fun WebClient.deviceRegistryCreateDevice( + request: Device.Request, + property: DeviceRegistryProperty, +): Device.Response = post() + .uri("${property.url}/device".also(::logCall)) + .bodyValue(request) + .retrieve() + .awaitBodyOrNull() ?: throw ResponseStatusException(REQUEST_TIMEOUT) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ae2d964..48f9921 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -10,6 +10,25 @@ spring: name: "%APP_BUILD_OS_NAME%" version: "%APP_BUILD_OS_VERSION%" +management: + endpoints: + enabled-by-default: false + web: + exposure: + include: "health,info" + endpoint: + health: + enabled: true + show-details: always + info: + enabled: true + influx: + metrics: + export: + api-version: v2 + bucket: hlaeja + org: hlaeja_ltd + --- ############################### ### Development environment ### @@ -30,6 +49,16 @@ server: device-registry: url: http://localhost:9010 +management: + metrics: + tags: + application: register-api + influx: + metrics: + export: + enabled: false + token: %INFLUXDB_TOKEN% + --- ########################## ### Docker environment ### @@ -39,6 +68,15 @@ spring: activate: on-profile: docker +management: + metrics: + tags: + application: register-api + influx: + metrics: + export: + uri: http://InfluxDB:8086 + server: port: 8443 ssl: