Skip to content

Commit e44d053

Browse files
committed
Lagt til flere felles moduler
1 parent 17ddfcc commit e44d053

File tree

20 files changed

+299
-22
lines changed

20 files changed

+299
-22
lines changed

Diff for: gradle/libs.versions.toml

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.r
4949
ktor-server-cors = { group = "io.ktor", name = "ktor-server-cors", version.ref = "ktorVersion" }
5050
ktor-server-swagger = { group = "io.ktor", name = "ktor-server-swagger", version.ref = "ktorVersion" }
5151
ktor-server-callId = { group = "io.ktor", name = "ktor-server-call-id", version.ref = "ktorVersion" }
52+
ktor-server-callLogging = { group = "io.ktor", name = "ktor-server-call-logging", version.ref = "ktorVersion" }
5253
ktor-server-statusPages = { group = "io.ktor", name = "ktor-server-status-pages", version.ref = "ktorVersion" }
5354
ktor-server-contentNegotiation = { group = "io.ktor", name = "ktor-server-content-negotiation", version.ref = "ktorVersion" }
5455
ktor-server-coreJvm = { group = "io.ktor", name = "ktor-server-core-jvm", version.ref = "ktorVersion" }

Diff for: lib/error-handling/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ dependencies {
77
compileOnly(libs.ktor.server.cors)
88
compileOnly(libs.ktor.server.statusPages)
99
compileOnly(libs.ktor.serialization.jackson)
10+
compileOnly(libs.jackson.datatypeJsr310)
1011
compileOnly(libs.kafka.streams.core)
1112
compileOnly(libs.logbackClassic)
1213
compileOnly(libs.micrometer.registryPrometheus)

Diff for: lib/error-handling/src/main/kotlin/no/nav/paw/error/handler/HttpExceptionHandler.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ private const val MDC_EXCEPTION_KEY = "exception"
2424

2525
suspend fun ApplicationCall.handleException(
2626
throwable: Throwable,
27-
resolver: (throwable: Throwable) -> ProblemDetails? = { null }
27+
customResolver: (throwable: Throwable) -> ProblemDetails? = { null }
2828
) {
29-
val problemDetails = resolveProblemDetails(request, throwable, resolver)
29+
val problemDetails = resolveProblemDetails(request, throwable, customResolver)
3030

3131
MDC.put(MDC_ERROR_ID_KEY, problemDetails.id.toString())
3232
MDC.put(MDC_ERROR_TYPE_KEY, problemDetails.type.toString())
@@ -44,9 +44,9 @@ suspend fun ApplicationCall.handleException(
4444
fun resolveProblemDetails(
4545
request: ApplicationRequest,
4646
throwable: Throwable,
47-
resolver: (throwable: Throwable) -> ProblemDetails? = { null }
47+
customResolver: (throwable: Throwable) -> ProblemDetails? = { null }
4848
): ProblemDetails {
49-
val problemDetails = resolver(throwable)
49+
val problemDetails = customResolver(throwable)
5050
if (problemDetails != null) {
5151
return problemDetails
5252
}

Diff for: lib/error-handling/src/main/kotlin/no/nav/paw/error/model/ProblemDetails.kt

+11-7
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,26 @@ data class ProblemDetails(
2121
val detail: String? = null,
2222
val instance: String,
2323
val timestamp: Instant = Instant.now()
24-
): Response<Nothing>
24+
) : Response<Nothing>
2525

2626
class ProblemDetailsBuilder private constructor(
27-
var type: URI = ErrorType.default().build(),
28-
var status: HttpStatusCode = HttpStatusCode.InternalServerError,
29-
var title: String? = null,
30-
var detail: String? = null,
31-
var instance: String = "/"
27+
private var type: URI = ErrorType.default().build(),
28+
private var status: HttpStatusCode = HttpStatusCode.InternalServerError,
29+
private var title: String? = null,
30+
private var detail: String? = null,
31+
private var instance: String = "/"
3232
) {
3333
fun type(type: URI) = apply { this.type = type }
3434
fun status(status: HttpStatusCode) = apply { this.status = status }
3535
fun title(title: String) = apply { this.title = title }
3636
fun detail(detail: String) = apply { this.detail = detail }
3737
fun instance(instance: String) = apply { this.instance = instance }
3838
fun build(): ProblemDetails = ProblemDetails(
39-
type = type, status = status, title = title ?: status.description, detail = detail, instance = instance
39+
type = type,
40+
status = status,
41+
title = title ?: status.description,
42+
detail = detail,
43+
instance = instance
4044
)
4145

4246
companion object {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package no.nav.paw.error.plugin
2+
3+
import io.ktor.server.application.Application
4+
import io.ktor.server.application.install
5+
import no.nav.paw.error.model.ProblemDetails
6+
7+
fun Application.installErrorHandling(
8+
customResolver: (throwable: Throwable) -> ProblemDetails? = { null }
9+
) {
10+
install(ErrorHandlingPlugin) {
11+
this.customResolver = customResolver
12+
}
13+
}

Diff for: lib/error-handling/src/main/kotlin/no/nav/paw/error/plugin/ErrorHandlingPlugin.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import no.nav.paw.error.model.ProblemDetails
1212
const val ERROR_HANDLING_PLUGIN_NAME: String = "ErrorHandlingPlugin"
1313

1414
class ErrorHandlingPluginConfig {
15-
val resolveProblemDetails: ((Throwable) -> ProblemDetails?)? = null
15+
var customResolver: ((Throwable) -> ProblemDetails?)? = null
1616
}
1717

1818
val ErrorHandlingPlugin
@@ -21,11 +21,11 @@ val ErrorHandlingPlugin
2121
::ErrorHandlingPluginConfig
2222
) {
2323
application.log.info("Installerer {}", ERROR_HANDLING_PLUGIN_NAME)
24-
val resolveProblemDetails = pluginConfig.resolveProblemDetails ?: { null }
24+
val customResolver = pluginConfig.customResolver ?: { null }
2525

2626
application.install(StatusPages) {
2727
exception<Throwable> { call: ApplicationCall, cause: Throwable ->
28-
call.handleException(cause, resolveProblemDetails)
28+
call.handleException(cause, customResolver)
2929
}
3030
}
3131
}

Diff for: lib/error-handling/src/main/kotlin/no/nav/paw/health/route/HealthRoutes.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import no.nav.paw.health.model.getAggregatedStatus
1010
import no.nav.paw.health.repository.HealthIndicatorRepository
1111

1212
fun Route.healthRoutes(
13-
healthIndicatorRepository: HealthIndicatorRepository,
13+
healthIndicatorRepository: HealthIndicatorRepository = HealthIndicatorRepository(),
1414
) {
1515

1616
get("/internal/isAlive") {

Diff for: lib/error-handling/src/test/kotlin/no/nav/paw/error/handler/HttpExceptionHandlerTest.kt

-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import io.ktor.client.request.get
99
import io.ktor.http.HttpStatusCode
1010
import io.ktor.serialization.jackson.jackson
1111
import io.ktor.server.plugins.BadRequestException
12-
import io.ktor.server.routing.IgnoreTrailingSlash
1312
import io.ktor.server.routing.get
1413
import io.ktor.server.routing.routing
1514
import io.ktor.server.testing.testApplication
@@ -24,7 +23,6 @@ class HttpExceptionHandlerTest : FreeSpec({
2423
"Skal håndtere exceptions og returnere ProblemDetails response" {
2524
testApplication {
2625
application {
27-
serverInstall(IgnoreTrailingSlash)
2826
serverInstall(ErrorHandlingPlugin)
2927
serverInstall(ServerContentNegotiation) {
3028
jackson {

Diff for: lib/error-handling/src/test/kotlin/no/nav/paw/health/route/HealthRoutesTest.kt

+32-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import io.ktor.client.call.body
66
import io.ktor.client.request.get
77
import io.ktor.http.HttpStatusCode
88
import io.ktor.serialization.jackson.jackson
9-
import io.ktor.server.plugins.statuspages.StatusPages
109
import io.ktor.server.routing.IgnoreTrailingSlash
1110
import io.ktor.server.routing.routing
1211
import io.ktor.server.testing.testApplication
12+
import no.nav.paw.error.plugin.ErrorHandlingPlugin
1313
import no.nav.paw.health.model.HealthStatus
1414
import no.nav.paw.health.model.LivenessHealthIndicator
1515
import no.nav.paw.health.model.ReadinessHealthIndicator
@@ -20,14 +20,43 @@ import io.ktor.server.plugins.contentnegotiation.ContentNegotiation as ServerCon
2020

2121
class HealthRoutesTest : FreeSpec({
2222

23-
"Endepunkter for helsesjekk skal returnere korrekt helsesjekk-status" {
23+
"Helsesjekk skal returnere korrekt status basert på default helseindikator" {
24+
testApplication {
25+
application {
26+
serverInstall(IgnoreTrailingSlash)
27+
serverInstall(ErrorHandlingPlugin)
28+
serverInstall(ServerContentNegotiation) {
29+
jackson {}
30+
}
31+
routing {
32+
healthRoutes()
33+
}
34+
}
35+
36+
val client = createClient {
37+
install(ClientContentNegotiation) {
38+
jackson {}
39+
}
40+
}
41+
42+
val livenessResponse1 = client.get("/internal/isAlive")
43+
val readinessResponse1 = client.get("/internal/isReady")
44+
45+
livenessResponse1.status shouldBe HttpStatusCode.OK
46+
livenessResponse1.body<String>() shouldBe HealthStatus.HEALTHY.value
47+
readinessResponse1.status shouldBe HttpStatusCode.OK
48+
readinessResponse1.body<String>() shouldBe HealthStatus.HEALTHY.value
49+
}
50+
}
51+
52+
"Helsesjekk skal returnere korrekt status basert på modifisert helseindikator" {
2453

2554
val healthIndicatorRepository = HealthIndicatorRepository()
2655

2756
testApplication {
2857
application {
2958
serverInstall(IgnoreTrailingSlash)
30-
serverInstall(StatusPages)
59+
serverInstall(ErrorHandlingPlugin)
3160
serverInstall(ServerContentNegotiation) {
3261
jackson {}
3362
}

Diff for: lib/logging/build.gradle.kts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
plugins {
2+
kotlin("jvm")
3+
}
4+
5+
val jvmMajorVersion: String by project
6+
7+
dependencies {
8+
compileOnly(project(":lib:hoplite-config"))
9+
compileOnly(libs.ktor.server.core)
10+
compileOnly(libs.ktor.server.callId)
11+
compileOnly(libs.ktor.server.callLogging)
12+
compileOnly(libs.logbackClassic)
13+
compileOnly(libs.logstashLogbackEncoder)
14+
compileOnly(libs.nav.common.log)
15+
compileOnly(libs.nav.common.auditLog)
16+
17+
testImplementation(libs.test.junit5.runner)
18+
testImplementation(libs.test.kotest.assertionsCore)
19+
}
20+
21+
java {
22+
toolchain {
23+
languageVersion.set(JavaLanguageVersion.of(jvmMajorVersion))
24+
}
25+
}
26+
27+
tasks.withType<Test>().configureEach {
28+
useJUnitPlatform()
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package no.nav.paw.logging.logger
2+
3+
import no.nav.common.audit_log.cef.CefMessage
4+
import no.nav.common.audit_log.cef.CefMessageEvent
5+
import no.nav.common.audit_log.cef.CefMessageSeverity
6+
import no.nav.paw.config.env.RuntimeEnvironment
7+
import no.nav.paw.config.env.appNameOrDefaultForLocal
8+
import no.nav.paw.config.env.currentRuntimeEnvironment
9+
import org.slf4j.Logger
10+
import org.slf4j.LoggerFactory
11+
12+
inline val buildAuditLogger: AuditLogger get() = AuditLogger.getLogger()
13+
14+
class AuditLogger private constructor(
15+
private val logger: Logger = LoggerFactory.getLogger("AuditLogger"),
16+
private val log: (String) -> Unit = logger::info // Endre ønsket loggnivå her
17+
) {
18+
fun audit(
19+
melding: String,
20+
aktorIdent: String,
21+
sluttbrukerIdent: String,
22+
event: CefMessageEvent = CefMessageEvent.ACCESS,
23+
runtimeEnvironment: RuntimeEnvironment = currentRuntimeEnvironment,
24+
) {
25+
val message = CefMessage.builder()
26+
.applicationName(runtimeEnvironment.appNameOrDefaultForLocal())
27+
.event(event)
28+
.name("Sporingslogg")
29+
.severity(CefMessageSeverity.INFO)
30+
.sourceUserId(aktorIdent)
31+
.destinationUserId(sluttbrukerIdent)
32+
.timeEnded(System.currentTimeMillis())
33+
.extension("msg", melding)
34+
.build()
35+
.toString()
36+
log(message)
37+
}
38+
39+
companion object {
40+
fun getLogger(): AuditLogger = AuditLogger()
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package no.nav.paw.logging.logger
2+
3+
import org.slf4j.Logger
4+
import org.slf4j.LoggerFactory
5+
6+
inline val <reified T> T.buildLogger: Logger get() = LoggerFactory.getLogger(T::class.java)
7+
8+
fun buildNamedLogger(name: String): Logger = LoggerFactory.getLogger("no.nav.paw.logger.${name.lowercase()}")
9+
inline val buildServerLogger: Logger get() = buildNamedLogger("server")
10+
inline val buildApplicationLogger: Logger get() = buildNamedLogger("application")
11+
inline val buildErrorLogger: Logger get() = buildNamedLogger("error")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package no.nav.paw.logging.plugin
2+
3+
import io.ktor.server.application.Application
4+
import io.ktor.server.application.install
5+
import io.ktor.server.plugins.callid.CallId
6+
import io.ktor.server.plugins.callid.callIdMdc
7+
import io.ktor.server.plugins.calllogging.CallLogging
8+
import io.ktor.server.request.path
9+
import no.nav.paw.logging.logger.buildServerLogger
10+
import org.slf4j.Logger
11+
import java.util.*
12+
13+
const val CALL_ID_HEADER = "x_callId"
14+
const val ADDITIONAL_CALL_ID_HEADER = "x_call-id"
15+
val CALL_ID_HEADERS = listOf(CALL_ID_HEADER, ADDITIONAL_CALL_ID_HEADER)
16+
const val CALL_ID_MDC = "x_callId"
17+
18+
fun Application.installLoggingPlugin(
19+
logger: Logger = buildServerLogger,
20+
) {
21+
install(CallId) {
22+
CALL_ID_HEADERS.forEach { retrieveFromHeader(it) }
23+
generate { UUID.randomUUID().toString() }
24+
verify { it.isNotEmpty() }
25+
}
26+
install(CallLogging) {
27+
callIdMdc(CALL_ID_MDC)
28+
disableDefaultColors()
29+
filter { !it.request.path().startsWith("/internal") }
30+
this.logger = logger
31+
}
32+
}

Diff for: lib/metrics/build.gradle.kts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
plugins {
2+
kotlin("jvm")
3+
}
4+
5+
val jvmMajorVersion: String by project
6+
7+
dependencies {
8+
compileOnly(libs.ktor.server.core)
9+
compileOnly(libs.ktor.server.metricsMicrometer)
10+
compileOnly(libs.micrometer.registryPrometheus)
11+
testImplementation(libs.test.junit5.runner)
12+
testImplementation(libs.test.kotest.assertionsCore)
13+
}
14+
15+
java {
16+
toolchain {
17+
languageVersion.set(JavaLanguageVersion.of(jvmMajorVersion))
18+
}
19+
}
20+
21+
tasks.withType<Test>().configureEach {
22+
useJUnitPlatform()
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package no.nav.paw.metrics.plugin
2+
3+
import io.ktor.server.application.Application
4+
import io.ktor.server.application.install
5+
import io.ktor.server.metrics.micrometer.MicrometerMetrics
6+
import io.micrometer.core.instrument.MeterRegistry
7+
import io.micrometer.core.instrument.binder.MeterBinder
8+
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
9+
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
10+
import io.micrometer.core.instrument.binder.system.ProcessorMetrics
11+
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig
12+
import java.time.Duration
13+
14+
private val defaultMeterBinders = listOf(
15+
JvmMemoryMetrics(),
16+
JvmGcMetrics(),
17+
ProcessorMetrics()
18+
)
19+
20+
fun Application.installMetricsPlugin(
21+
meterRegistry: MeterRegistry,
22+
additionalMeterBinders: List<MeterBinder> = emptyList(),
23+
distributionStatisticConfig: DistributionStatisticConfig = DistributionStatisticConfig.NONE
24+
) {
25+
install(MicrometerMetrics) {
26+
this.registry = meterRegistry
27+
this.meterBinders = defaultMeterBinders + additionalMeterBinders
28+
this.distributionStatisticConfig = distributionStatisticConfig
29+
}
30+
}
31+
32+
fun Application.installWebAppMetricsPlugin(
33+
meterRegistry: MeterRegistry,
34+
additionalMeterBinders: List<MeterBinder> = emptyList(),
35+
enablePercentilesHistogram: Boolean = true,
36+
minimumExpectedValue: Duration = Duration.ofMillis(10),
37+
maximumExpectedValue: Duration = Duration.ofSeconds(1),
38+
serviceLevelObjectives: List<Duration> = listOf(
39+
Duration.ofMillis(50),
40+
Duration.ofMillis(100),
41+
Duration.ofMillis(300),
42+
Duration.ofMillis(500)
43+
)
44+
) {
45+
val distributionStatisticConfig =
46+
DistributionStatisticConfig.builder()
47+
.percentilesHistogram(enablePercentilesHistogram)
48+
.minimumExpectedValue(minimumExpectedValue.toNanos().toDouble())
49+
.maximumExpectedValue(maximumExpectedValue.toNanos().toDouble())
50+
.serviceLevelObjectives(*serviceLevelObjectives.map { it.toNanos().toDouble() }.toDoubleArray())
51+
.build()
52+
installMetricsPlugin(meterRegistry, additionalMeterBinders, distributionStatisticConfig)
53+
}

0 commit comments

Comments
 (0)