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
This commit is contained in:
2024-12-20 17:09:22 +01:00
parent 10f6486e37
commit 93680e50d5
9 changed files with 138 additions and 52 deletions

View File

@@ -5,16 +5,23 @@ Classes and endpoints, to shape and to steer, Devices and sensors, their purpose
## Properties for deployment ## Properties for deployment
| name | required | info | | name | required | info |
|-------------------------------|----------|-------------------------| |----------------------------------------------|:--------:|----------------------------------------------|
| spring.profiles.active | * | Spring Boot environment | | spring.profiles.active | ✓ | Spring Boot environment |
| server.port | * | HTTP port | | server.port | ✓ | HTTP port |
| server.ssl.enabled | * | HTTP Enable SSL | | server.ssl.enabled | ✓ | HTTP Enable SSL |
| server.ssl.key-store | * | HTTP Keystore | | server.ssl.key-store | ✓ | HTTP Keystore |
| server.ssl.key-store-type | * | HTTP Cert Type | | server.ssl.key-store-type | ✓ | HTTP Cert Type |
| server.ssl.key-store-password | ** | HTTP Cert Pass | | server.ssl.key-store-password | ✗ | HTTP Cert Pass |
| device-registry.url | * | Device Register URL | | 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 ## Releasing Service
@@ -22,39 +29,29 @@ Run `release.sh` script from `master` branch.
## Development Configuration ## Development Configuration
### Developer Keystore ### Developer Keystore
1. Open `hosts` file: 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.
* On Unix-like systems (Linux, macOS), this directory is typically `/etc/hosts`.
* On Windows, this directory is typically `%SystemRoot%\System32\drivers\etc\hosts`.
2. Add the following lines to the `hosts` file: ### Global Settings
```text
127.0.0.1 registryapi # Hlæja Registry API
```
3. Generate Keystores This services rely on a set of global settings to configure development environments. These settings, managed through Gradle properties or environment variables.
```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
```
4. Export the public certificate *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.*
```shell
keytool -export -alias registry-api -keystore ./cert/keystore.p12 -storepass password -file ./cert/registry-api.cer -rfc
```
### 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.
Here's how you can do it:
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\<YourUsername>\.gradle\`.
2. Add the following lines to the `gradle.properties` file:
```properties ```properties
repository.user=your_user repository.user=your_user
repository.token=your_token_value repository.token=your_token_value
influxdb.token=your_token_value
```
#### Environment Variables
```properties
REPOSITORY_USER=your_user
REPOSITORY_TOKEN=your_token_value
INFLUXDB_TOKEN=your_token_value
``` ```
or use environment variables `REPOSITORY_USER` and `REPOSITORY_TOKEN`

View File

@@ -1,3 +1,5 @@
import com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer
plugins { plugins {
alias(hlaeja.plugins.kotlin.jvm) alias(hlaeja.plugins.kotlin.jvm)
alias(hlaeja.plugins.kotlin.spring) alias(hlaeja.plugins.kotlin.spring)
@@ -13,6 +15,7 @@ dependencies {
implementation(hlaeja.kotlin.reflect) implementation(hlaeja.kotlin.reflect)
implementation(hlaeja.kotlinx.coroutines) implementation(hlaeja.kotlinx.coroutines)
implementation(hlaeja.library.hlaeja.common.messages) implementation(hlaeja.library.hlaeja.common.messages)
implementation(hlaeja.micrometer.registry.influx)
implementation(hlaeja.springboot.starter.actuator) implementation(hlaeja.springboot.starter.actuator)
implementation(hlaeja.springboot.starter.webflux) implementation(hlaeja.springboot.starter.webflux)
@@ -27,6 +30,17 @@ dependencies {
group = "ltd.hlaeja" group = "ltd.hlaeja"
tasks.named("processResources") { fun influxDbToken(): String = config.findOrDefault("influxdb.token", "INFLUXDB_TOKEN", "")
tasks {
named("containerCreate", DockerCreateContainer::class) {
withEnvVar("MANAGEMENT_INFLUX_METRICS_EXPORT_TOKEN", influxDbToken())
}
withType<ProcessResources> {
filesMatching("**/application.yml") { filter { it.replace("%INFLUXDB_TOKEN%", influxDbToken()) } }
onlyIf { file("src/main/resources/application.yml").exists() }
}
named("processResources") {
dependsOn("copyCertificates") dependsOn("copyCertificates")
} }
}

View File

@@ -1,6 +1,6 @@
kotlin.code.style=official kotlin.code.style=official
version=0.1.0-SNAPSHOT version=0.1.0-SNAPSHOT
catalog=0.6.0 catalog=0.7.0-SNAPSHOT
docker.port.expose=8443 docker.port.expose=8443
container.port.expose=8443 container.port.expose=8443
container.port.host=9040 container.port.host=9040

0
gradlew vendored Normal file → Executable file
View File

0
release.sh Normal file → Executable file
View File

View File

@@ -1,24 +1,46 @@
package ltd.hlaeja.service 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.library.deviceRegistry.Device
import ltd.hlaeja.util.logCall
import ltd.hlaeja.property.DeviceRegistryProperty 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.stereotype.Service
import org.springframework.web.ErrorResponseException
import org.springframework.web.reactive.function.client.WebClient 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 import org.springframework.web.server.ResponseStatusException
private val log = KotlinLogging.logger {}
@Service @Service
class DeviceRegistryService( class DeviceRegistryService(
meterRegistry: MeterRegistry,
private val webClient: WebClient, private val webClient: WebClient,
private val deviceRegistryProperty: DeviceRegistryProperty, 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( suspend fun registerDevice(
request: Device.Request, request: Device.Request,
): Device.Response = webClient.post() ): Device.Response = try {
.uri("${deviceRegistryProperty.url}/device".also(::logCall)) webClient.deviceRegistryCreateDevice(request, deviceRegistryProperty)
.bodyValue(request) .also { registerDeviceSuccess.increment() }
.retrieve() } catch (e: ErrorResponseException) {
.awaitBodyOrNull<Device.Response>() ?: throw ResponseStatusException(REQUEST_TIMEOUT) registerDeviceFailure.increment()
throw e
} catch (e: WebClientRequestException) {
registerDeviceFailure.increment()
log.error(e) { "Error device registry" }
throw ResponseStatusException(SERVICE_UNAVAILABLE)
}
} }

View File

@@ -1,9 +1,7 @@
package ltd.hlaeja.util package ltd.hlaeja.util
import mu.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
private val log = KotlinLogging.logger {} private val log = KotlinLogging.logger {}
fun logCall(url: String) { fun logCall(url: String) = log.debug { "calling: $url" }
log.debug("calling: {}", url)
}

View File

@@ -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<Device.Response>() ?: throw ResponseStatusException(REQUEST_TIMEOUT)

View File

@@ -10,6 +10,25 @@ spring:
name: "%APP_BUILD_OS_NAME%" name: "%APP_BUILD_OS_NAME%"
version: "%APP_BUILD_OS_VERSION%" 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 ### ### Development environment ###
@@ -30,6 +49,16 @@ server:
device-registry: device-registry:
url: http://localhost:9010 url: http://localhost:9010
management:
metrics:
tags:
application: register-api
influx:
metrics:
export:
enabled: false
token: %INFLUXDB_TOKEN%
--- ---
########################## ##########################
### Docker environment ### ### Docker environment ###
@@ -39,6 +68,15 @@ spring:
activate: activate:
on-profile: docker on-profile: docker
management:
metrics:
tags:
application: register-api
influx:
metrics:
export:
uri: http://InfluxDB:8086
server: server:
port: 8443 port: 8443
ssl: ssl: