add Film observability with trace id and metrics (#44)

* add Film observability with trace id and metrics

* test: provide meter registry in FilmServiceTest
This commit was merged in pull request #44.
This commit is contained in:
skettiks
2026-05-19 16:13:10 +03:00
committed by GitHub
parent 051b4ca80b
commit e956c058ac
5 changed files with 219 additions and 31 deletions
@@ -3,6 +3,8 @@ package com.project.movienight.adapters.web
import com.project.movienight.domain.exception.BlockedValueException
import com.project.movienight.domain.exception.DomainException
import com.project.movienight.domain.exception.EntityNotFoundException
import org.slf4j.LoggerFactory
import org.slf4j.MDC
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseStatus
@@ -10,22 +12,60 @@ import org.springframework.web.bind.annotation.RestControllerAdvice
@RestControllerAdvice
class ApiExceptionHandler {
private val log = LoggerFactory.getLogger(javaClass)
@ExceptionHandler(EntityNotFoundException::class)
@ResponseStatus(HttpStatus.NOT_FOUND)
fun handleNotFound(exception: EntityNotFoundException): ErrorResponse =
ErrorResponse(message = exception.message ?: "Entity not found")
fun handleNotFound(exception: EntityNotFoundException): ErrorResponse {
val traceId = currentTraceId()
log.warn("Entity not found: traceId='{}', message='{}'", traceId, exception.message)
return ErrorResponse(
message = exception.message ?: "Entity not found",
traceId = traceId,
)
}
@ExceptionHandler(BlockedValueException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
fun handleBlockedValue(exception: BlockedValueException): ErrorResponse =
ErrorResponse(message = exception.message ?: "Blocked value")
fun handleBlockedValue(exception: BlockedValueException): ErrorResponse {
val traceId = currentTraceId()
log.warn("Blocked value: traceId='{}', message='{}'", traceId, exception.message)
return ErrorResponse(
message = exception.message ?: "Blocked value",
traceId = traceId,
)
}
@ExceptionHandler(DomainException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
fun handleDomainException(exception: DomainException): ErrorResponse =
ErrorResponse(message = exception.message ?: "Domain error")
fun handleDomainException(exception: DomainException): ErrorResponse {
val traceId = currentTraceId()
log.warn("Domain error: traceId='{}', message='{}'", traceId, exception.message)
return ErrorResponse(
message = exception.message ?: "Domain error",
traceId = traceId,
)
}
@ExceptionHandler(Exception::class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
fun handleUnexpectedException(exception: Exception): ErrorResponse {
val traceId = currentTraceId()
log.error("Unexpected error: traceId='{}'", traceId, exception)
return ErrorResponse(
message = "Internal server error",
traceId = traceId,
)
}
private fun currentTraceId(): String = MDC.get("traceId") ?: "unknown"
}
data class ErrorResponse(
val message: String,
val traceId: String,
)