diff --git a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/App.kt b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/App.kt index a090237f2..188fa6983 100644 --- a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/App.kt +++ b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/App.kt @@ -21,13 +21,13 @@ import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisConnection import no.nav.helsearbeidsgiver.felles.rapidsrivers.registerShutdownLifecycle import no.nav.helsearbeidsgiver.inntektsmelding.api.aktiveorgnr.aktiveOrgnrRoute import no.nav.helsearbeidsgiver.inntektsmelding.api.auth.Tilgangskontroll -import no.nav.helsearbeidsgiver.inntektsmelding.api.hentforespoersel.hentForespoerselRoute -import no.nav.helsearbeidsgiver.inntektsmelding.api.hentforespoerselIdListe.hentForespoerselIdListeRoute +import no.nav.helsearbeidsgiver.inntektsmelding.api.hentforespoersel.hentForespoersel +import no.nav.helsearbeidsgiver.inntektsmelding.api.hentforespoerselIdListe.hentForespoerselIdListe import no.nav.helsearbeidsgiver.inntektsmelding.api.hentselvbestemtim.hentSelvbestemtImRoute -import no.nav.helsearbeidsgiver.inntektsmelding.api.innsending.innsendingRoute +import no.nav.helsearbeidsgiver.inntektsmelding.api.innsending.innsending import no.nav.helsearbeidsgiver.inntektsmelding.api.inntekt.inntektRoute import no.nav.helsearbeidsgiver.inntektsmelding.api.inntektselvbestemt.inntektSelvbestemtRoute -import no.nav.helsearbeidsgiver.inntektsmelding.api.kvittering.kvitteringRoute +import no.nav.helsearbeidsgiver.inntektsmelding.api.kvittering.kvittering import no.nav.helsearbeidsgiver.inntektsmelding.api.lagreselvbestemtim.lagreSelvbestemtImRoute import no.nav.helsearbeidsgiver.inntektsmelding.api.tilgang.TilgangProducer import no.nav.helsearbeidsgiver.inntektsmelding.api.tilgangorgnr.tilgangOrgnrRoute @@ -116,12 +116,12 @@ fun Application.apiModule( authenticate { route(Routes.PREFIX) { - hentForespoerselRoute(rapid, tilgangskontroll, redisConnection) - hentForespoerselIdListeRoute(rapid, tilgangskontroll, redisConnection) + hentForespoersel(rapid, tilgangskontroll, redisConnection) + hentForespoerselIdListe(rapid, tilgangskontroll, redisConnection) inntektRoute(rapid, tilgangskontroll, redisConnection) inntektSelvbestemtRoute(rapid, tilgangskontroll, redisConnection) - innsendingRoute(rapid, tilgangskontroll, redisConnection) - kvitteringRoute(rapid, tilgangskontroll, redisConnection) + innsending(rapid, tilgangskontroll, redisConnection) + kvittering(rapid, tilgangskontroll, redisConnection) lagreSelvbestemtImRoute(rapid, tilgangskontroll, redisConnection) hentSelvbestemtImRoute(rapid, tilgangskontroll, redisConnection) aktiveOrgnrRoute(rapid, redisConnection) diff --git a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/HelsesjekkerRouting.kt b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/HelsesjekkerRouting.kt index 3dbea652c..921e0fdea 100644 --- a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/HelsesjekkerRouting.kt +++ b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/HelsesjekkerRouting.kt @@ -1,14 +1,12 @@ package no.nav.helsearbeidsgiver.inntektsmelding.api -import io.ktor.http.ContentType import io.ktor.server.application.Application import io.ktor.server.application.call import io.ktor.server.response.respondText import io.ktor.server.response.respondTextWriter import io.ktor.server.routing.get import io.ktor.server.routing.routing -import io.prometheus.client.CollectorRegistry -import io.prometheus.client.exporter.common.TextFormat +import no.nav.helsearbeidsgiver.felles.metrics.Metrics fun Application.helsesjekkerRouting() { routing { @@ -25,8 +23,8 @@ fun Application.helsesjekkerRouting() { ?.toSet() .orEmpty() - call.respondTextWriter(ContentType.parse(TextFormat.CONTENT_TYPE_004)) { - TextFormat.write004(this, CollectorRegistry.defaultRegistry.filteredMetricFamilySamples(names)) + call.respondTextWriter(Metrics.Expose.contentType004) { + Metrics.Expose.filteredMetricsWrite004(this, names) } } } diff --git a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/hentforespoersel/HentForespoerselRoute.kt b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/hentforespoersel/HentForespoerselRoute.kt index 693c55157..19bd01c24 100644 --- a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/hentforespoersel/HentForespoerselRoute.kt +++ b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/hentforespoersel/HentForespoerselRoute.kt @@ -4,11 +4,12 @@ import io.ktor.http.HttpStatusCode import io.ktor.server.application.call import io.ktor.server.routing.Route import io.ktor.server.routing.post -import io.prometheus.client.Summary import kotlinx.serialization.builtins.serializer import no.nav.helse.rapids_rivers.RapidsConnection import no.nav.helsearbeidsgiver.felles.domene.HentForespoerselResultat import no.nav.helsearbeidsgiver.felles.domene.ResultJson +import no.nav.helsearbeidsgiver.felles.metrics.Metrics +import no.nav.helsearbeidsgiver.felles.metrics.recordTime import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisConnection import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisPrefix import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisStore @@ -30,7 +31,7 @@ import no.nav.helsearbeidsgiver.utils.json.fromJson import no.nav.helsearbeidsgiver.utils.json.toJson import java.util.UUID -fun Route.hentForespoerselRoute( +fun Route.hentForespoersel( rapid: RapidsConnection, tilgangskontroll: Tilgangskontroll, redisConnection: RedisConnection, @@ -38,66 +39,58 @@ fun Route.hentForespoerselRoute( val hentForespoerselProducer = HentForespoerselProducer(rapid) val redisPoller = RedisStore(redisConnection, RedisPrefix.HentForespoersel).let(::RedisPoller) - val requestLatency = - Summary - .build() - .name("simba_hent_forespoersel_latency_seconds") - .help("hent forespoersel endpoint latency in seconds") - .register() - post(Routes.HENT_FORESPOERSEL) { val transaksjonId = UUID.randomUUID() - val requestTimer = requestLatency.startTimer() - runCatching { - receive(HentForespoerselRequest.serializer()) - }.onSuccess { request -> - logger.info("Henter data for uuid: ${request.uuid}") - try { - tilgangskontroll.validerTilgangTilForespoersel(call.request, request.uuid) + Metrics.hentForespoerselEndpoint.recordTime(Route::hentForespoersel) { + runCatching { + receive(HentForespoerselRequest.serializer()) + }.onSuccess { request -> + logger.info("Henter data for uuid: ${request.uuid}") + try { + tilgangskontroll.validerTilgangTilForespoersel(call.request, request.uuid) - val arbeidsgiverFnr = call.request.lesFnrFraAuthToken() + val arbeidsgiverFnr = call.request.lesFnrFraAuthToken() - hentForespoerselProducer.publish(transaksjonId, request, arbeidsgiverFnr) + hentForespoerselProducer.publish(transaksjonId, request, arbeidsgiverFnr) - val resultatJson = redisPoller.hent(transaksjonId).fromJson(ResultJson.serializer()) + val resultatJson = redisPoller.hent(transaksjonId).fromJson(ResultJson.serializer()) - sikkerLogger.info("Hentet forespørsel: $resultatJson") + sikkerLogger.info("Hentet forespørsel: $resultatJson") - val resultat = resultatJson.success?.fromJson(HentForespoerselResultat.serializer()) - if (resultat != null) { - respond(HttpStatusCode.Created, resultat.toResponse(), HentForespoerselResponse.serializer()) - } else { - val feilmelding = resultatJson.failure?.fromJson(String.serializer()) ?: "Teknisk feil, prøv igjen senere." + val resultat = resultatJson.success?.fromJson(HentForespoerselResultat.serializer()) + if (resultat != null) { + respond(HttpStatusCode.Created, resultat.toResponse(), HentForespoerselResponse.serializer()) + } else { + val feilmelding = resultatJson.failure?.fromJson(String.serializer()) ?: "Teknisk feil, prøv igjen senere." + val response = + ResultJson( + failure = feilmelding.toJson(), + ) + respond(HttpStatusCode.ServiceUnavailable, response, ResultJson.serializer()) + } + } catch (e: ManglerAltinnRettigheterException) { + val response = + ResultJson( + failure = "Du har ikke rettigheter for organisasjon.".toJson(), + ) + respondForbidden(response, ResultJson.serializer()) + } catch (_: RedisPollerTimeoutException) { + logger.info("Fikk timeout for ${request.uuid}") val response = ResultJson( - failure = feilmelding.toJson(), + failure = RedisTimeoutResponse(request.uuid).toJson(RedisTimeoutResponse.serializer()), ) - respond(HttpStatusCode.ServiceUnavailable, response, ResultJson.serializer()) + respondInternalServerError(response, ResultJson.serializer()) } - } catch (e: ManglerAltinnRettigheterException) { - val response = - ResultJson( - failure = "Du har ikke rettigheter for organisasjon.".toJson(), - ) - respondForbidden(response, ResultJson.serializer()) - } catch (_: RedisPollerTimeoutException) { - logger.info("Fikk timeout for ${request.uuid}") + }.onFailure { + logger.error("Klarte ikke lese request.", it) val response = ResultJson( - failure = RedisTimeoutResponse(request.uuid).toJson(RedisTimeoutResponse.serializer()), + failure = "Mangler forespørsel-ID for å hente forespørsel.".toJson(), ) - respondInternalServerError(response, ResultJson.serializer()) + respondBadRequest(response, ResultJson.serializer()) } - }.also { - requestTimer.observeDuration() - }.onFailure { - logger.error("Klarte ikke lese request.", it) - val response = - ResultJson( - failure = "Mangler forespørsel-ID for å hente forespørsel.".toJson(), - ) - respondBadRequest(response, ResultJson.serializer()) } } } diff --git a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/hentforespoerselIdListe/HentForespoerselIdListeRoute.kt b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/hentforespoerselIdListe/HentForespoerselIdListeRoute.kt index 41a937cee..da88591e0 100644 --- a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/hentforespoerselIdListe/HentForespoerselIdListeRoute.kt +++ b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/hentforespoerselIdListe/HentForespoerselIdListeRoute.kt @@ -6,7 +6,6 @@ import io.ktor.server.application.call import io.ktor.server.routing.Route import io.ktor.server.routing.post import io.ktor.util.pipeline.PipelineContext -import io.prometheus.client.Summary import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import no.nav.helse.rapids_rivers.RapidsConnection @@ -15,6 +14,8 @@ import no.nav.helsearbeidsgiver.felles.Tekst.UGYLDIG_REQUEST import no.nav.helsearbeidsgiver.felles.domene.Forespoersel import no.nav.helsearbeidsgiver.felles.domene.ResultJson import no.nav.helsearbeidsgiver.felles.domene.VedtaksperiodeIdForespoerselIdPar +import no.nav.helsearbeidsgiver.felles.metrics.Metrics +import no.nav.helsearbeidsgiver.felles.metrics.recordTime import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisConnection import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisPrefix import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisStore @@ -38,7 +39,7 @@ import java.util.UUID const val MAKS_ANTALL_VEDTAKSPERIODE_IDER = 100 -fun Route.hentForespoerselIdListeRoute( +fun Route.hentForespoerselIdListe( rapid: RapidsConnection, tilgangskontroll: Tilgangskontroll, redisConnection: RedisConnection, @@ -46,43 +47,34 @@ fun Route.hentForespoerselIdListeRoute( val hentForespoerslerProducer = HentForespoerslerProducer(rapid) val redisPoller = RedisStore(redisConnection, RedisPrefix.HentForespoerslerForVedtaksperiodeIdListe).let(::RedisPoller) - val requestLatency = - Summary - .build() - .name("simba_hent_forespoersel_id_liste_latency_seconds") - .help("hent forespoersel id liste endpoint latency in seconds") - .register() - post(Routes.HENT_FORESPOERSEL_ID_LISTE) { - val requestTimer = requestLatency.startTimer() - - runCatching { - receive(HentForespoerslerRequest.serializer()) - }.onSuccess { request -> - if (request.vedtaksperiodeIdListe.size > MAKS_ANTALL_VEDTAKSPERIODE_IDER) { - loggErrorSikkerOgUsikker( - "Stopper forsøk på å hente forespørsler for mer enn $MAKS_ANTALL_VEDTAKSPERIODE_IDER vedtaksperiode-IDer på en gang.", - ) - respondBadRequest(UGYLDIG_REQUEST, String.serializer()) - } else { - try { - hentForespoersler(request, hentForespoerslerProducer, redisPoller, tilgangskontroll) - } catch (_: ManglerAltinnRettigheterException) { - respondForbidden("Mangler rettigheter for organisasjon.", String.serializer()) - } catch (e: RedisPollerTimeoutException) { - loggErrorSikkerOgUsikker("Fikk timeout ved henting av forespørselIDer for vedtaksperiodeIDene: ${request.vedtaksperiodeIdListe}", e) - respondInternalServerError(RedisTimeoutResponse(), RedisTimeoutResponse.serializer()) - } catch (e: Exception) { - loggErrorSikkerOgUsikker("Ukjent feil ved henting av forespørselIDer for vedtaksperiodeIDene: ${request.vedtaksperiodeIdListe}", e) - respondInternalServerError(TEKNISK_FEIL_FORBIGAAENDE, String.serializer()) + Metrics.hentForespoerselIdListeEndpoint.recordTime(Route::hentForespoerselIdListe) { + runCatching { + receive(HentForespoerslerRequest.serializer()) + }.onSuccess { request -> + if (request.vedtaksperiodeIdListe.size > MAKS_ANTALL_VEDTAKSPERIODE_IDER) { + loggErrorSikkerOgUsikker( + "Stopper forsøk på å hente forespørsler for mer enn $MAKS_ANTALL_VEDTAKSPERIODE_IDER vedtaksperiode-IDer på en gang.", + ) + respondBadRequest(UGYLDIG_REQUEST, String.serializer()) + } else { + try { + hentForespoersler(request, hentForespoerslerProducer, redisPoller, tilgangskontroll) + } catch (_: ManglerAltinnRettigheterException) { + respondForbidden("Mangler rettigheter for organisasjon.", String.serializer()) + } catch (e: RedisPollerTimeoutException) { + loggErrorSikkerOgUsikker("Fikk timeout ved henting av forespørselIDer for vedtaksperiodeIDene: ${request.vedtaksperiodeIdListe}", e) + respondInternalServerError(RedisTimeoutResponse(), RedisTimeoutResponse.serializer()) + } catch (e: Exception) { + loggErrorSikkerOgUsikker("Ukjent feil ved henting av forespørselIDer for vedtaksperiodeIDene: ${request.vedtaksperiodeIdListe}", e) + respondInternalServerError(TEKNISK_FEIL_FORBIGAAENDE, String.serializer()) + } + } + }.onFailure { + "Klarte ikke lese request.".let { feilMelding -> + loggErrorSikkerOgUsikker(feilMelding, it) + respondBadRequest(feilMelding, String.serializer()) } - } - }.also { - requestTimer.observeDuration() - }.onFailure { - "Klarte ikke lese request.".let { feilMelding -> - loggErrorSikkerOgUsikker(feilMelding, it) - respondBadRequest(feilMelding, String.serializer()) } } } diff --git a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/innsending/InnsendingRoute.kt b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/innsending/InnsendingRoute.kt index d001165ef..e30d84af0 100644 --- a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/innsending/InnsendingRoute.kt +++ b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/innsending/InnsendingRoute.kt @@ -5,12 +5,13 @@ import io.ktor.server.application.call import io.ktor.server.request.receiveText import io.ktor.server.routing.Route import io.ktor.server.routing.post -import io.prometheus.client.Summary import kotlinx.serialization.builtins.serializer import no.nav.helse.rapids_rivers.RapidsConnection import no.nav.helsearbeidsgiver.domene.inntektsmelding.v1.skjema.SkjemaInntektsmelding import no.nav.helsearbeidsgiver.felles.Tekst import no.nav.helsearbeidsgiver.felles.domene.ResultJson +import no.nav.helsearbeidsgiver.felles.metrics.Metrics +import no.nav.helsearbeidsgiver.felles.metrics.recordTime import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisConnection import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisPrefix import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisStore @@ -29,9 +30,8 @@ import no.nav.helsearbeidsgiver.inntektsmelding.api.utils.respondInternalServerE import no.nav.helsearbeidsgiver.utils.json.fromJson import no.nav.helsearbeidsgiver.utils.json.parseJson import java.util.UUID -import kotlin.system.measureTimeMillis -fun Route.innsendingRoute( +fun Route.innsending( rapid: RapidsConnection, tilgangskontroll: Tilgangskontroll, redisConnection: RedisConnection, @@ -39,17 +39,9 @@ fun Route.innsendingRoute( val producer = InnsendingProducer(rapid) val redisPoller = RedisStore(redisConnection, RedisPrefix.Innsending).let(::RedisPoller) - val requestLatency = - Summary - .build() - .name("simba_innsending_latency_seconds") - .help("innsending endpoint latency in seconds") - .register() - // TODO ubrukt path param satt til optional. fjern i frontend, så her. post(Routes.INNSENDING + "/{forespoerselId?}") { - val requestTimer = requestLatency.startTimer() - measureTimeMillis { + Metrics.innsendingEndpoint.recordTime(Route::innsending) { val transaksjonId = UUID.randomUUID() val skjema = @@ -119,9 +111,6 @@ fun Route.innsendingRoute( } } } - }.also { - requestTimer.observeDuration() - logger.info("Api call to ${Routes.INNSENDING} took $it ms") } } } diff --git a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/kvittering/KvitteringRoute.kt b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/kvittering/KvitteringRoute.kt index 86c63e9a8..44360e80d 100644 --- a/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/kvittering/KvitteringRoute.kt +++ b/api/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/kvittering/KvitteringRoute.kt @@ -3,7 +3,6 @@ package no.nav.helsearbeidsgiver.inntektsmelding.api.kvittering import io.ktor.server.application.call import io.ktor.server.routing.Route import io.ktor.server.routing.get -import io.prometheus.client.Summary import kotlinx.serialization.SerializationException import kotlinx.serialization.builtins.serializer import no.nav.helse.rapids_rivers.RapidsConnection @@ -16,6 +15,8 @@ import no.nav.helsearbeidsgiver.felles.Tekst import no.nav.helsearbeidsgiver.felles.domene.EksternInntektsmelding import no.nav.helsearbeidsgiver.felles.domene.InnsendtInntektsmelding import no.nav.helsearbeidsgiver.felles.domene.ResultJson +import no.nav.helsearbeidsgiver.felles.metrics.Metrics +import no.nav.helsearbeidsgiver.felles.metrics.recordTime import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisConnection import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisPrefix import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisStore @@ -38,9 +39,8 @@ import no.nav.helsearbeidsgiver.utils.json.fromJson import no.nav.helsearbeidsgiver.utils.pipe.orDefault import java.time.ZoneId import java.util.UUID -import kotlin.system.measureTimeMillis -fun Route.kvitteringRoute( +fun Route.kvittering( rapid: RapidsConnection, tilgangskontroll: Tilgangskontroll, redisConnection: RedisConnection, @@ -48,13 +48,6 @@ fun Route.kvitteringRoute( val kvitteringProducer = KvitteringProducer(rapid) val redisPoller = RedisStore(redisConnection, RedisPrefix.Kvittering).let(::RedisPoller) - val requestLatency = - Summary - .build() - .name("simba_kvittering_latency_seconds") - .help("kvittering endpoint latency in seconds") - .register() - get(Routes.KVITTERING) { val transaksjonId = UUID.randomUUID() @@ -71,14 +64,9 @@ fun Route.kvitteringRoute( } } else { logger.info("Henter data for forespørselId: $forespoerselId") - val requestTimer = requestLatency.startTimer() - measureTimeMillis { + Metrics.kvitteringEndpoint.recordTime(Route::kvittering) { try { - measureTimeMillis { - tilgangskontroll.validerTilgangTilForespoersel(call.request, forespoerselId) - }.also { - logger.info("Authorize took $it") - } + tilgangskontroll.validerTilgangTilForespoersel(call.request, forespoerselId) kvitteringProducer.publish(transaksjonId, forespoerselId) val resultatJson = redisPoller.hent(transaksjonId).fromJson(ResultJson.serializer()) @@ -108,9 +96,6 @@ fun Route.kvitteringRoute( logger.error("Fikk timeout for forespørselId: $forespoerselId") respondInternalServerError(RedisTimeoutResponse(forespoerselId), RedisTimeoutResponse.serializer()) } - }.also { - requestTimer.observeDuration() - logger.info("api call took $it") } } } diff --git a/api/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/utils/TestUtils.kt b/api/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/utils/TestUtils.kt index c007b2541..a2274c504 100644 --- a/api/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/utils/TestUtils.kt +++ b/api/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/api/utils/TestUtils.kt @@ -13,7 +13,6 @@ import io.ktor.serialization.kotlinx.json.json import io.ktor.server.testing.ApplicationTestBuilder import io.ktor.server.testing.testApplication import io.mockk.mockk -import io.prometheus.client.CollectorRegistry import kotlinx.coroutines.runBlocking import kotlinx.serialization.KSerializer import no.nav.helsearbeidsgiver.felles.domene.Tilgang @@ -22,7 +21,6 @@ import no.nav.helsearbeidsgiver.felles.rapidsrivers.redis.RedisConnection import no.nav.helsearbeidsgiver.inntektsmelding.api.apiModule import no.nav.helsearbeidsgiver.utils.json.jsonConfig import no.nav.helsearbeidsgiver.utils.json.toJson -import org.junit.jupiter.api.AfterEach val harTilgangResultat = TilgangResultat(Tilgang.HAR_TILGANG).toJson(TilgangResultat.serializer()).toString() val ikkeTilgangResultat = TilgangResultat(Tilgang.IKKE_TILGANG).toJson(TilgangResultat.serializer()).toString() @@ -40,11 +38,6 @@ abstract class ApiTest : MockAuthToken() { testClient.block() } - - @AfterEach - fun cleanupPrometheus() { - CollectorRegistry.defaultRegistry.clear() - } } class TestClient( diff --git a/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/ForespoerselRepository.kt b/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/ForespoerselRepository.kt index a57da55cf..8debf1a44 100644 --- a/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/ForespoerselRepository.kt +++ b/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/ForespoerselRepository.kt @@ -1,7 +1,8 @@ package no.nav.helsearbeidsgiver.inntektsmelding.db -import io.prometheus.client.Summary import no.nav.helsearbeidsgiver.felles.db.exposed.firstOrNull +import no.nav.helsearbeidsgiver.felles.metrics.Metrics +import no.nav.helsearbeidsgiver.felles.metrics.recordTime import no.nav.helsearbeidsgiver.inntektsmelding.db.tabell.ForespoerselEntitet import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.and @@ -15,30 +16,21 @@ import java.util.UUID class ForespoerselRepository( private val db: Database, ) { - private val requestLatency = - Summary - .build() - .name("simba_db_forespoersel_repo_latency_seconds") - .help("database forespoerselRepo latency in seconds") - .labelNames("method") - .register() - fun oppdaterOppgaveId( forespoerselId: String, oppgaveId: String, ) { - val requestTimer = requestLatency.labels("oppdaterOppgaveId").startTimer() - transaction(db) { - ForespoerselEntitet.update( - where = { - (ForespoerselEntitet.forespoerselId eq forespoerselId) and - ForespoerselEntitet.oppgaveId.isNull() - }, - ) { - it[ForespoerselEntitet.oppgaveId] = oppgaveId + Metrics.dbForespoersel.recordTime(::oppdaterOppgaveId) { + transaction(db) { + ForespoerselEntitet.update( + where = { + (ForespoerselEntitet.forespoerselId eq forespoerselId) and + ForespoerselEntitet.oppgaveId.isNull() + }, + ) { + it[ForespoerselEntitet.oppgaveId] = oppgaveId + } } - }.also { - requestTimer.observeDuration() } } @@ -46,58 +38,52 @@ class ForespoerselRepository( forespoerselId: String, sakId: String, ) { - val requestTimer = requestLatency.labels("oppdaterSakId").startTimer() - transaction(db) { - ForespoerselEntitet.update( - where = { - (ForespoerselEntitet.forespoerselId eq forespoerselId) and - ForespoerselEntitet.sakId.isNull() - }, - ) { - it[ForespoerselEntitet.sakId] = sakId + Metrics.dbForespoersel.recordTime(::oppdaterSakId) { + transaction(db) { + ForespoerselEntitet.update( + where = { + (ForespoerselEntitet.forespoerselId eq forespoerselId) and + ForespoerselEntitet.sakId.isNull() + }, + ) { + it[ForespoerselEntitet.sakId] = sakId + } } - }.also { - requestTimer.observeDuration() } } - fun hentOppgaveId(forespoerselId: UUID): String? { - val requestTimer = requestLatency.labels("hentOppgaveId").startTimer() - return transaction(db) { - ForespoerselEntitet - .selectAll() - .where { ForespoerselEntitet.forespoerselId eq forespoerselId.toString() } - .firstOrNull(ForespoerselEntitet.oppgaveId) - }.also { - requestTimer.observeDuration() + fun hentOppgaveId(forespoerselId: UUID): String? = + Metrics.dbForespoersel.recordTime(::hentOppgaveId) { + transaction(db) { + ForespoerselEntitet + .selectAll() + .where { ForespoerselEntitet.forespoerselId eq forespoerselId.toString() } + .firstOrNull(ForespoerselEntitet.oppgaveId) + } } - } - fun hentSakId(forespoerselId: UUID): String? { - val requestTimer = requestLatency.labels("hentSakId").startTimer() - return transaction(db) { - ForespoerselEntitet - .selectAll() - .where { ForespoerselEntitet.forespoerselId eq forespoerselId.toString() } - .firstOrNull(ForespoerselEntitet.sakId) - }.also { - requestTimer.observeDuration() + fun hentSakId(forespoerselId: UUID): String? = + Metrics.dbForespoersel.recordTime(::hentSakId) { + transaction(db) { + ForespoerselEntitet + .selectAll() + .where { ForespoerselEntitet.forespoerselId eq forespoerselId.toString() } + .firstOrNull(ForespoerselEntitet.sakId) + } } - } fun lagreForespoersel( forespoerselId: String, organisasjonsnummer: String, ) { - val requestTimer = requestLatency.labels("lagreForespoersel").startTimer() - transaction(db) { - ForespoerselEntitet.insert { - it[this.forespoerselId] = forespoerselId - it[orgnr] = organisasjonsnummer - it[opprettet] = LocalDateTime.now() + Metrics.dbForespoersel.recordTime(::lagreForespoersel) { + transaction(db) { + ForespoerselEntitet.insert { + it[this.forespoerselId] = forespoerselId + it[orgnr] = organisasjonsnummer + it[opprettet] = LocalDateTime.now() + } } - }.also { - requestTimer.observeDuration() } } } diff --git a/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/InntektsmeldingRepository.kt b/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/InntektsmeldingRepository.kt index 0688170d7..df47646f9 100644 --- a/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/InntektsmeldingRepository.kt +++ b/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/InntektsmeldingRepository.kt @@ -1,9 +1,9 @@ package no.nav.helsearbeidsgiver.inntektsmelding.db -import io.prometheus.client.Summary import no.nav.helsearbeidsgiver.domene.inntektsmelding.deprecated.Inntektsmelding import no.nav.helsearbeidsgiver.domene.inntektsmelding.v1.skjema.SkjemaInntektsmelding import no.nav.helsearbeidsgiver.felles.domene.EksternInntektsmelding +import no.nav.helsearbeidsgiver.felles.metrics.Metrics import no.nav.helsearbeidsgiver.felles.metrics.recordTime import no.nav.helsearbeidsgiver.inntektsmelding.db.tabell.InntektsmeldingEntitet import no.nav.helsearbeidsgiver.utils.log.logger @@ -26,16 +26,8 @@ class InntektsmeldingRepository( private val logger = logger() private val sikkerLogger = sikkerLogger() - private val requestLatency = - Summary - .build() - .name("simba_db_inntektsmelding_repo_latency_seconds") - .help("database inntektsmeldingRepo latency in seconds") - .labelNames("method") - .register() - fun hentNyesteInntektsmelding(forespoerselId: UUID): Inntektsmelding? = - requestLatency.recordTime(InntektsmeldingRepository::hentNyesteInntektsmelding) { + Metrics.dbInntektsmelding.recordTime(InntektsmeldingRepository::hentNyesteInntektsmelding) { transaction(db) { hentNyesteImQuery(forespoerselId) .firstOrNull() @@ -44,7 +36,7 @@ class InntektsmeldingRepository( } fun hentNyesteEksternEllerInternInntektsmelding(forespoerselId: UUID): Pair = - requestLatency.recordTime(InntektsmeldingRepository::hentNyesteEksternEllerInternInntektsmelding) { + Metrics.dbInntektsmelding.recordTime(InntektsmeldingRepository::hentNyesteEksternEllerInternInntektsmelding) { transaction(db) { InntektsmeldingEntitet .select(InntektsmeldingEntitet.dokument, InntektsmeldingEntitet.eksternInntektsmelding) @@ -64,7 +56,7 @@ class InntektsmeldingRepository( innsendingId: Long, journalpostId: String, ) { - requestLatency.recordTime(InntektsmeldingRepository::oppdaterJournalpostId) { + Metrics.dbInntektsmelding.recordTime(InntektsmeldingRepository::oppdaterJournalpostId) { val antallOppdatert = transaction(db) { InntektsmeldingEntitet.update( @@ -102,7 +94,7 @@ class InntektsmeldingRepository( } fun hentNyesteBerikedeInnsendingId(forespoerselId: UUID): Long? = - requestLatency.recordTime(InntektsmeldingRepository::hentNyesteBerikedeInnsendingId) { + Metrics.dbInntektsmelding.recordTime(InntektsmeldingRepository::hentNyesteBerikedeInnsendingId) { transaction(db) { hentNyesteImQuery(forespoerselId) .firstOrNull() @@ -111,7 +103,7 @@ class InntektsmeldingRepository( } fun lagreInntektsmeldingSkjema(inntektsmeldingSkjema: SkjemaInntektsmelding): Long = - requestLatency.recordTime(InntektsmeldingRepository::lagreInntektsmeldingSkjema) { + Metrics.dbInntektsmelding.recordTime(InntektsmeldingRepository::lagreInntektsmeldingSkjema) { transaction(db) { InntektsmeldingEntitet.insert { it[this.forespoerselId] = inntektsmeldingSkjema.forespoerselId.toString() @@ -122,7 +114,7 @@ class InntektsmeldingRepository( } fun hentNyesteInntektsmeldingSkjema(forespoerselId: UUID): SkjemaInntektsmelding? = - requestLatency.recordTime(InntektsmeldingRepository::hentNyesteInntektsmeldingSkjema) { + Metrics.dbInntektsmelding.recordTime(InntektsmeldingRepository::hentNyesteInntektsmeldingSkjema) { transaction(db) { hentNyesteImSkjemaQuery(forespoerselId) .firstOrNull() @@ -136,7 +128,7 @@ class InntektsmeldingRepository( inntektsmeldingDokument: Inntektsmelding, ) { val antallOppdatert = - requestLatency.recordTime(InntektsmeldingRepository::oppdaterMedBeriketDokument) { + Metrics.dbInntektsmelding.recordTime(InntektsmeldingRepository::oppdaterMedBeriketDokument) { transaction(db) { InntektsmeldingEntitet.update( where = { diff --git a/felles/build.gradle.kts b/felles/build.gradle.kts index 7041b1f47..c8058603f 100644 --- a/felles/build.gradle.kts +++ b/felles/build.gradle.kts @@ -2,6 +2,7 @@ val kotestVersion: String by project val lettuceVersion: String by project val mockkVersion: String by project val rapidsAndRiversVersion: String by project +val prometheusVersion: String by project val slf4jVersion: String by project val utilsVersion: String by project @@ -14,6 +15,7 @@ dependencies { api("org.slf4j:slf4j-api:$slf4jVersion") implementation("io.lettuce:lettuce-core:$lettuceVersion") + implementation("io.prometheus:simpleclient:$prometheusVersion") testFixturesApi("com.github.navikt:rapids-and-rivers:$rapidsAndRiversVersion") testFixturesApi("io.lettuce:lettuce-core:$lettuceVersion") diff --git a/felles/gradle.properties b/felles/gradle.properties index 75704f088..403a3675a 100644 --- a/felles/gradle.properties +++ b/felles/gradle.properties @@ -1,2 +1,3 @@ # Dependency versions +prometheusVersion=0.16.0 slf4jVersion=2.0.12 diff --git a/felles/src/main/kotlin/no/nav/helsearbeidsgiver/felles/metrics/Metrics.kt b/felles/src/main/kotlin/no/nav/helsearbeidsgiver/felles/metrics/Metrics.kt index b06de0f5d..d0b3167d0 100644 --- a/felles/src/main/kotlin/no/nav/helsearbeidsgiver/felles/metrics/Metrics.kt +++ b/felles/src/main/kotlin/no/nav/helsearbeidsgiver/felles/metrics/Metrics.kt @@ -1,11 +1,27 @@ package no.nav.helsearbeidsgiver.felles.metrics +import io.ktor.http.ContentType +import io.prometheus.client.CollectorRegistry import io.prometheus.client.Counter import io.prometheus.client.Summary +import io.prometheus.client.exporter.common.TextFormat import kotlinx.coroutines.runBlocking +import java.io.Writer import kotlin.reflect.KFunction object Metrics { + val hentForespoerselEndpoint = endpointMetric("hent forespoersel") + + val hentForespoerselIdListeEndpoint = endpointMetric("hent forespoersel ID liste") + + val innsendingEndpoint = endpointMetric("innsending") + + val kvitteringEndpoint = endpointMetric("kvittering") + + val dbInntektsmelding = databaseMetric("inntektsmelding", "inntektsmelding") + + val dbForespoersel = databaseMetric("inntektsmelding", "forespoersel") + val dbSelvbestemtIm = databaseMetric("inntektsmelding", "selvbestemt_inntektsmelding") val dbSelvbestemtSak = databaseMetric("notifikasjon", "selvbestemt_sak") @@ -27,6 +43,17 @@ object Metrics { val forespoerslerBesvartFraSimba = counterMetric("forespoersler besvart fra Simba") val forespoerslerBesvartFraSpleis = counterMetric("forespoersler besvart fra Spleis") + + object Expose { + val contentType004 = ContentType.parse(TextFormat.CONTENT_TYPE_004) + + fun filteredMetricsWrite004( + writer: Writer, + metricNames: Set, + ) { + TextFormat.write004(writer, CollectorRegistry.defaultRegistry.filteredMetricFamilySamples(metricNames)) + } + } } fun Summary.recordTime( @@ -41,6 +68,12 @@ fun Summary.recordTime( } } +private fun endpointMetric(endpointName: String): Summary = + latencyMetric( + name = endpointName, + description = "$endpointName endpoint", + ) + private fun databaseMetric( dbName: String, tableName: String, diff --git a/integrasjonstest/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/integrasjonstest/utils/EndToEndTest.kt b/integrasjonstest/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/integrasjonstest/utils/EndToEndTest.kt index 18c2d1c5b..f57deefb0 100644 --- a/integrasjonstest/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/integrasjonstest/utils/EndToEndTest.kt +++ b/integrasjonstest/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/integrasjonstest/utils/EndToEndTest.kt @@ -5,7 +5,6 @@ import io.mockk.clearAllMocks import io.mockk.coEvery import io.mockk.every import io.mockk.mockk -import io.prometheus.client.CollectorRegistry import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlinx.serialization.json.JsonElement @@ -263,8 +262,6 @@ abstract class EndToEndTest : ContainerTest() { @AfterAll fun afterAllEndToEnd() { - // Prometheus-metrikker spenner bein på testene uten denne - CollectorRegistry.defaultRegistry.clear() redisConnection.close() inntektsmeldingDatabase.dataSource.close() notifikasjonDatabase.dataSource.close() diff --git a/pdl/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/pdl/HentPersonerRiverTest.kt b/pdl/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/pdl/HentPersonerRiverTest.kt index dcf985978..c8944bbaa 100644 --- a/pdl/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/pdl/HentPersonerRiverTest.kt +++ b/pdl/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/pdl/HentPersonerRiverTest.kt @@ -9,7 +9,6 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.coVerifySequence import io.mockk.mockk -import io.prometheus.client.CollectorRegistry import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull import no.nav.helse.rapids_rivers.testsupport.TestRapid @@ -45,7 +44,6 @@ class HentPersonerRiverTest : beforeTest { testRapid.reset() clearAllMocks() - CollectorRegistry.defaultRegistry.clear() } test("finner én person") {