Skip to content

Commit 1998c2f

Browse files
authored
expose metrics exporter settings (#187)
* expose metrics exporter settings * delegate read-only prop's setting to AppConfig in AppGlobals * docs: be more precise about memory requirements & limits * docs: complete the configuration examples * docs: add info about the live demo instance * docs: add changelog entry for 2.2.0 * fix config fallbacks in application.yml
1 parent 26d5919 commit 1998c2f

File tree

30 files changed

+483
-58
lines changed

30 files changed

+483
-58
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
## [📖 Documentation](https://kuvasz-uptime.dev)
1111

12+
## [🛝 Live demo](https://kuvasz-uptime.dev/demo/)
13+
1214
## ℹ️ What is Kuvasz?
1315

1416
**Kuvasz** [ˈkuvɒs], an open-source, self-hosted uptime & SSL monitoring service, designed to help you keep track of your websites and services. It provides a modern, user-friendly interface, a powerful REST API, and supports multiple notification channels like email, Slack, Telegram, and PagerDuty.

app/src/main/kotlin/com/kuvaszuptime/kuvasz/controllers/ui/WebUIController.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.kuvaszuptime.kuvasz.controllers.ui
22

33
import com.kuvaszuptime.kuvasz.AppGlobals
4-
import com.kuvaszuptime.kuvasz.config.AppConfig
54
import com.kuvaszuptime.kuvasz.i18n.Messages
65
import com.kuvaszuptime.kuvasz.jooq.enums.SslStatus
76
import com.kuvaszuptime.kuvasz.jooq.enums.UptimeStatus
@@ -37,7 +36,6 @@ import java.time.Duration
3736
@Hidden
3837
class WebUIController(
3938
private val monitorCrudService: MonitorCrudService,
40-
private val appConfig: AppConfig,
4139
private val appGlobals: AppGlobals,
4240
private val statCalculator: StatCalculator,
4341
private val settingsRepository: SettingsRepository,
@@ -105,10 +103,9 @@ class WebUIController(
105103
@ExecuteOn(TaskExecutors.IO)
106104
@Produces(MediaType.TEXT_HTML)
107105
fun monitorTable(): String {
108-
val isReadOnlyMode = appConfig.isExternalWriteDisabled()
109106
val monitors = monitorCrudService.getMonitorsWithDetails(sortedBy = MONITOR.NAME.asc())
110107

111-
return renderMonitorList(monitors, isReadOnlyMode)
108+
return renderMonitorList(monitors, appGlobals.isReadOnlyMode())
112109
}
113110

114111
@Get("/fragments/monitors/{monitorId}/details-heading")

app/src/main/kotlin/com/kuvaszuptime/kuvasz/repositories/SettingsRepository.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.kuvaszuptime.kuvasz.repositories
33
import com.kuvaszuptime.kuvasz.AppGlobals
44
import com.kuvaszuptime.kuvasz.config.AppConfig
55
import com.kuvaszuptime.kuvasz.config.SMTPMailerConfig
6+
import com.kuvaszuptime.kuvasz.metrics.MetricsExportConfig
67
import com.kuvaszuptime.kuvasz.models.dto.SettingsDto
78
import com.kuvaszuptime.kuvasz.models.handlers.EmailNotificationConfig
89
import com.kuvaszuptime.kuvasz.models.handlers.IntegrationConfig
@@ -21,11 +22,17 @@ class SettingsRepository(
2122
private val appGlobals: AppGlobals,
2223
private val appConfig: AppConfig,
2324
private val smtpMailerConfig: SMTPMailerConfig?,
25+
private val exportConfig: MetricsExportConfig,
26+
private val prometheusSettings: PrometheusSettingsRepository,
27+
private val otlpSettings: OTLPSettingsRepository,
2428
) {
2529

2630
@field:Property(name = "micronaut.security.token.generator.access-token.expiration")
2731
protected var accessTokenMaxAge: Long = 0L
2832

33+
@field:Property(name = "micronaut.metrics.enabled")
34+
protected var metricsExportEnabled: Boolean = false
35+
2936
fun getSettings(): SettingsDto =
3037
SettingsDto(
3138
authentication = SettingsDto.AuthenticationSettingsDto(
@@ -64,6 +71,26 @@ class SettingsRepository(
6471
{ id, config ->
6572
SettingsDto.PagerdutyConfigDto(id, config)
6673
}
74+
),
75+
metricsExport = SettingsDto.MetricsExportSettingsDto(
76+
exportEnabled = metricsExportEnabled,
77+
meters = SettingsDto.MetricsExportSettingsDto.MeterSettingsDto(
78+
sslExpiry = exportConfig.sslExpiry,
79+
latestLatency = exportConfig.latestLatency,
80+
uptimeStatus = exportConfig.uptimeStatus,
81+
sslStatus = exportConfig.sslStatus,
82+
),
83+
exporters = SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto(
84+
prometheus = SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto.PrometheusSettingsDto(
85+
enabled = prometheusSettings.exportEnabled,
86+
descriptions = prometheusSettings.descriptionsEnabled,
87+
),
88+
openTelemetry = SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto.OTLPSettingsDto(
89+
enabled = otlpSettings.exportEnabled,
90+
url = otlpSettings.url,
91+
step = otlpSettings.step,
92+
)
93+
)
6794
)
6895
)
6996

@@ -78,3 +105,26 @@ class SettingsRepository(
78105
}
79106
}
80107
}
108+
109+
@Singleton
110+
class PrometheusSettingsRepository {
111+
112+
@field:Property(name = "micronaut.metrics.export.prometheus.enabled")
113+
var exportEnabled: Boolean = false
114+
115+
@field:Property(name = "micronaut.metrics.export.prometheus.descriptions")
116+
var descriptionsEnabled: Boolean = false
117+
}
118+
119+
@Singleton
120+
class OTLPSettingsRepository {
121+
122+
@field:Property(name = "micronaut.metrics.export.otlp.enabled")
123+
var exportEnabled: Boolean = false
124+
125+
@field:Property(name = "micronaut.metrics.export.otlp.url")
126+
var url: String = ""
127+
128+
@field:Property(name = "micronaut.metrics.export.otlp.step")
129+
var step: String = ""
130+
}

app/src/main/kotlin/com/kuvaszuptime/kuvasz/services/ui/AppGlobalsFactory.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class AppGlobalsFactory {
1919
appConfig: AppConfig,
2020
integrationRepository: IntegrationRepository,
2121
) = AppGlobals(
22-
isReadOnlyMode = appConfig.isExternalWriteDisabled(),
22+
isReadOnlyMode = { appConfig.isExternalWriteDisabled() },
2323
isAuthenticated = { securityService?.isAuthenticated ?: true },
2424
isAuthEnabled = securityService != null,
2525
appVersion = BuildConfig.APP_VERSION,

app/src/main/resources/application.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ micronaut:
88
descriptions: ${ENABLE_PROMETHEUS_DESCRIPTIONS:`true`}
99
otlp:
1010
enabled: ${ENABLE_OTLP_EXPORT:`false`}
11-
url: ${OTLP_EXPORT_URL:``}
12-
headers: ${OTLP_EXPORT_HEADERS:``}
11+
url: ${OTLP_EXPORT_URL:}
12+
headers: ${OTLP_EXPORT_HEADERS:}
1313
step: ${OTLP_EXPORT_STEP:`PT1M`}
1414
binders:
1515
# Disable the built-in binders to reduce the number of metrics

app/src/test/kotlin/com/kuvaszuptime/kuvasz/controllers/SettingsControllerTest.kt

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ import io.kotest.matchers.string.shouldNotBeEmpty
1414
import io.micronaut.context.annotation.Property
1515
import io.micronaut.test.extensions.kotest5.annotation.MicronautTest
1616

17-
@MicronautTest(environments = ["full-integrations-setup", "yaml-monitors"])
17+
@MicronautTest(
18+
environments = [
19+
"full-integrations-setup",
20+
"yaml-monitors",
21+
"enabled-metrics-otlp",
22+
"enabled-metrics-prometheus",
23+
]
24+
)
1825
@SMTPTest
1926
@Property(name = "micronaut.security.token.generator.access-token.expiration", value = "3600")
2027
@Property(name = "app-config.event-data-retention-days", value = "5")
@@ -150,6 +157,24 @@ class SettingsControllerTest(settingsClient: SettingsClient, appGlobals: AppGlob
150157
transportStrategy shouldBe "SMTP"
151158
}
152159
}
160+
161+
with(result.metricsExport) {
162+
exportEnabled shouldBe true
163+
meters.sslExpiry shouldBe true
164+
meters.latestLatency shouldBe true
165+
meters.uptimeStatus shouldBe true
166+
meters.sslStatus shouldBe true
167+
168+
with(exporters.prometheus) {
169+
enabled shouldBe true
170+
descriptions shouldBe true
171+
}
172+
with(exporters.openTelemetry) {
173+
enabled shouldBe true
174+
url shouldBe "http://otel-collector.example:4317"
175+
step shouldBe "PT30M"
176+
}
177+
}
153178
}
154179
}
155180
}

app/src/test/kotlin/com/kuvaszuptime/kuvasz/services/ui/AppGlobalsFactoryTest.kt

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class AppGlobalsFactoryTest : BehaviorSpec({
3030
globals.appVersion shouldBe BuildConfig.APP_VERSION
3131
globals.isAuthEnabled shouldBe false
3232
globals.isAuthenticated() shouldBe true
33-
globals.isReadOnlyMode shouldBe false
33+
globals.isReadOnlyMode() shouldBe false
3434
}
3535
}
3636

@@ -44,7 +44,7 @@ class AppGlobalsFactoryTest : BehaviorSpec({
4444
globals.appVersion shouldBe BuildConfig.APP_VERSION
4545
globals.isAuthEnabled shouldBe true
4646
globals.isAuthenticated() shouldBe true
47-
globals.isReadOnlyMode shouldBe false
47+
globals.isReadOnlyMode() shouldBe false
4848
}
4949
}
5050

@@ -58,7 +58,7 @@ class AppGlobalsFactoryTest : BehaviorSpec({
5858
globals.appVersion shouldBe BuildConfig.APP_VERSION
5959
globals.isAuthEnabled shouldBe true
6060
globals.isAuthenticated() shouldBe false
61-
globals.isReadOnlyMode shouldBe false
61+
globals.isReadOnlyMode() shouldBe false
6262
}
6363
}
6464

@@ -68,7 +68,20 @@ class AppGlobalsFactoryTest : BehaviorSpec({
6868
val globals = AppGlobalsFactory().appGlobals(null, appConfig, emptyIntegrationRepository)
6969

7070
then("it should return the correctly hydrated view model") {
71-
globals.isReadOnlyMode shouldBe true
71+
globals.isReadOnlyMode() shouldBe true
72+
}
73+
}
74+
75+
`when`("when the app is in read-only mode but it's only set later") {
76+
val appConfig = AppConfig()
77+
val globals = AppGlobalsFactory().appGlobals(null, appConfig, emptyIntegrationRepository)
78+
globals.isReadOnlyMode() shouldBe false
79+
80+
appConfig.disableExternalWrite()
81+
val globalsAfterUpdate = AppGlobalsFactory().appGlobals(null, appConfig, emptyIntegrationRepository)
82+
83+
then("it should return the correctly hydrated view model") {
84+
globalsAfterUpdate.isReadOnlyMode() shouldBe true
7285
}
7386
}
7487

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
micronaut.metrics.enabled: true
2+
micronaut.metrics.export.otlp.enabled: true
3+
micronaut.metrics.export.otlp.url: http://otel-collector.example:4317
4+
micronaut.metrics.export.otlp.step: PT30M
5+
---
6+
metrics-exports:
7+
uptime-status: true
8+
latest-latency: true
9+
ssl-status: true
10+
ssl-expiry: true

docs/docs/api-doc/kuvasz-latest.yml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,7 @@ components:
10151015
- app
10161016
- authentication
10171017
- integrations
1018+
- metricsExport
10181019
type: object
10191020
properties:
10201021
authentication:
@@ -1029,6 +1030,10 @@ components:
10291030
description: Integration settings
10301031
allOf:
10311032
- $ref: "#/components/schemas/SettingsDto.IntegrationSettingsDto"
1033+
metricsExport:
1034+
description: Metrics exporter settings
1035+
allOf:
1036+
- $ref: "#/components/schemas/SettingsDto.MetricsExportSettingsDto"
10321037
SettingsDto.AppSettingsDto:
10331038
required:
10341039
- eventDataRetentionDays
@@ -1142,6 +1147,87 @@ components:
11421147
description: List of Telegram notification configurations
11431148
items:
11441149
$ref: "#/components/schemas/SettingsDto.TelegramNotificationConfigDto"
1150+
SettingsDto.MetricsExportSettingsDto:
1151+
required:
1152+
- exportEnabled
1153+
- exporters
1154+
- meters
1155+
type: object
1156+
properties:
1157+
exportEnabled:
1158+
type: boolean
1159+
description: Whether the metrics exporting is generally enabled
1160+
meters:
1161+
description: Settings for individual meters
1162+
allOf:
1163+
- $ref: "#/components/schemas/SettingsDto.MetricsExportSettingsDto.MeterSettingsDto"
1164+
exporters:
1165+
description: Settings for individual exporters
1166+
allOf:
1167+
- $ref: "#/components/schemas/SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto"
1168+
SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto:
1169+
required:
1170+
- openTelemetry
1171+
- prometheus
1172+
type: object
1173+
properties:
1174+
prometheus:
1175+
description: Prometheus exporter settings
1176+
allOf:
1177+
- $ref: "#/components/schemas/SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto.PrometheusSettingsDto"
1178+
openTelemetry:
1179+
description: OpenTelemetry exporter settings
1180+
allOf:
1181+
- $ref: "#/components/schemas/SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto.OTLPSettingsDto"
1182+
SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto.OTLPSettingsDto:
1183+
required:
1184+
- enabled
1185+
- step
1186+
- url
1187+
type: object
1188+
properties:
1189+
enabled:
1190+
type: boolean
1191+
description: Whether the exporter is enabled
1192+
url:
1193+
type: string
1194+
description: The endpoint where the metrics will be published
1195+
step:
1196+
type: string
1197+
description: The step for the metrics reporting as an ISO 8601 duration
1198+
string
1199+
SettingsDto.MetricsExportSettingsDto.ExporterSettingsDto.PrometheusSettingsDto:
1200+
required:
1201+
- descriptions
1202+
- enabled
1203+
type: object
1204+
properties:
1205+
enabled:
1206+
type: boolean
1207+
description: Whether the exporter is enabled
1208+
descriptions:
1209+
type: boolean
1210+
description: Whether descriptions are included in the export
1211+
SettingsDto.MetricsExportSettingsDto.MeterSettingsDto:
1212+
required:
1213+
- latestLatency
1214+
- sslExpiry
1215+
- sslStatus
1216+
- uptimeStatus
1217+
type: object
1218+
properties:
1219+
sslExpiry:
1220+
type: boolean
1221+
description: Whether SSL certificate expiry exporter is enabled
1222+
latestLatency:
1223+
type: boolean
1224+
description: Whether latest latency exporter is enabled
1225+
uptimeStatus:
1226+
type: boolean
1227+
description: Whether monitor status exporter is enabled
1228+
sslStatus:
1229+
type: boolean
1230+
description: Whether SSL status exporter is enabled
11451231
SettingsDto.PagerdutyConfigDto:
11461232
required:
11471233
- enabled

docs/docs/changelog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
Prior to version 2.0.0, the changelogs were maintained **only in the** [**GitHub repository**](https://github.com/kuvasz-uptime/kuvasz/releases){ target="_blank" }.
44

5+
## 2.2.0 <small>2025-07-17</small> { id="2.2.0" data-toc-label="2.2.0" }
6+
7+
### Features
8+
9+
- **Metrics exporter settings** are exposed both on the API (under `GET /api/v1/settings`) and on the UI, so you can easily get an overview of the effective configuration.
10+
- A **live demo** is available at [**demo.kuvasz-uptime.dev**](https://demo.kuvasz-uptime.dev){ target="blank" } where you can try out the latest features of _Kuvasz_ without setting up your own instance. Further details, credentials [**here**](demo.md).
11+
12+
### Fixes
13+
14+
- Fixed the glitch on the UI regarding read-only mode in case the underlying logic was initialized before YAML monitors were loaded. This caused the UI to not show the read-only mode correctly, even though the backend was working as expected.
15+
516
## 2.1.0 <small>2025-07-11</small> { id="2.1.0" data-toc-label="2.1.0" }
617

718
### Features

0 commit comments

Comments
 (0)