diff --git a/.gitignore b/.gitignore index c3743f6..17a33e2 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,6 @@ out/ .env -src/main/resources/application.yml \ No newline at end of file +src/main/resources/application.yml +src/main/resources/application-local.yml +src/main/resources/application-prod.yml \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 9c06531..4fa2e0d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -79,6 +79,12 @@ dependencies { // Discord implementation (Dependency.Discord.WEB_HOOK) + + // Actuator + implementation (Dependency.Spring.ACTUATOR) + + // Prometheus + implementation (Dependency.Spring.PROMETHEUS) } kotlin { @@ -117,3 +123,7 @@ tasks.bootJar { into("static/docs") } } + +springBoot { + buildInfo() +} diff --git a/buildSrc/src/main/kotlin/Dependency.kt b/buildSrc/src/main/kotlin/Dependency.kt index a6da274..eadcc27 100644 --- a/buildSrc/src/main/kotlin/Dependency.kt +++ b/buildSrc/src/main/kotlin/Dependency.kt @@ -15,6 +15,8 @@ object Dependency { val BOOT_STARTER_ACTUATOR = starter("-actuator") val SPRINGDOC = "org.springdoc:springdoc-openapi-starter-webmvc-ui:$SPRINGDOC_VERSION" val RESTDOCS_MOCKMVC = "org.springframework.restdocs:spring-restdocs-mockmvc:$RESTDOCS_MOCKMVC_VERSION" + val ACTUATOR = "$BASE:spring-boot-starter-actuator" + val PROMETHEUS = "io.micrometer:micrometer-registry-prometheus" } object Kotlin { diff --git a/src/main/kotlin/com/coffee/api/cafe/application/CafeService.kt b/src/main/kotlin/com/coffee/api/cafe/application/CafeService.kt index 51ec745..d87f1e7 100644 --- a/src/main/kotlin/com/coffee/api/cafe/application/CafeService.kt +++ b/src/main/kotlin/com/coffee/api/cafe/application/CafeService.kt @@ -3,6 +3,7 @@ package com.coffee.api.cafe.application import com.coffee.api.cafe.application.port.inbound.FindCafe import com.coffee.api.cafe.application.port.outbound.CafeRepository import com.coffee.api.cafe.domain.CafeArea +import io.micrometer.core.annotation.Counted import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -12,6 +13,7 @@ class CafeService( private val cafeRepository: CafeRepository, ) : FindCafe { + @Counted(value = "cafe.find", description = "Number of times cafe list is requested") override fun invoke(query: FindCafe.Query): FindCafe.Result { val area = query.area?.let { code -> CafeArea.entries.find { it.name == code } diff --git a/src/main/kotlin/com/coffee/api/cafe/presentation/adapter/in/restapi/CafeController.kt b/src/main/kotlin/com/coffee/api/cafe/presentation/adapter/in/restapi/CafeController.kt index f5a14b7..f87c8ac 100644 --- a/src/main/kotlin/com/coffee/api/cafe/presentation/adapter/in/restapi/CafeController.kt +++ b/src/main/kotlin/com/coffee/api/cafe/presentation/adapter/in/restapi/CafeController.kt @@ -8,6 +8,7 @@ import com.coffee.api.cafe.presentation.adapter.`in`.restapi.dto.response.* import com.coffee.api.cafe.presentation.adapter.`in`.restapi.mapper.FindAllCafesResponseMapper import com.coffee.api.cafe.presentation.adapter.`in`.restapi.mapper.GetCafeDetailsResponseMapper import com.coffee.api.cafe.presentation.docs.CafeApi +import com.coffee.api.common.aop.PageViewed import com.coffee.api.common.support.response.ApiResponse import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -25,6 +26,7 @@ class CafeController( val findRecommendCafe: FindRecommendCafe, ) : CafeApi { + @PageViewed("cafe_list") @GetMapping override fun findAllCafes( @RequestParam(value = "lastCafeId", required = false) lastCafeId: UUID?, @@ -35,6 +37,7 @@ class CafeController( return ApiResponse.success(response) } + @PageViewed("cafe_details") @GetMapping("/details/{cafeId}") override fun getCafeDetails( @PathVariable cafeId: UUID, @@ -44,12 +47,14 @@ class CafeController( return ApiResponse.success(response) } + @PageViewed("areas") @GetMapping("/areas") override fun getAreas(): ApiResponse { val response = findCafeArea.execute(Unit) return ApiResponse.success(response) } + @PageViewed("cafe_recommend") @GetMapping("/recommend") override fun getRecommendCafes(lastGroupId: UUID?, limit: Int): ApiResponse { return ApiResponse.success(findRecommendCafe.execute(FindRecommendCafe.Query(lastGroupId, limit))) diff --git a/src/main/kotlin/com/coffee/api/common/aop/PageViewAspect.kt b/src/main/kotlin/com/coffee/api/common/aop/PageViewAspect.kt new file mode 100644 index 0000000..fcec862 --- /dev/null +++ b/src/main/kotlin/com/coffee/api/common/aop/PageViewAspect.kt @@ -0,0 +1,24 @@ +package com.coffee.api.common.aop + +import io.micrometer.core.instrument.MeterRegistry +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.springframework.stereotype.Component + +@Aspect +@Component +class PageViewAspect( + private val meterRegistry: MeterRegistry +) { + + @Before("@annotation(pageViewed)") + fun countPageView(joinPoint: JoinPoint, pageViewed: PageViewed) { + val counterName = "page.view.count" + val tagName = pageViewed.name + + meterRegistry + .counter(counterName, "page", tagName) + .increment() + } +} diff --git a/src/main/kotlin/com/coffee/api/common/aop/PageViewed.kt b/src/main/kotlin/com/coffee/api/common/aop/PageViewed.kt new file mode 100644 index 0000000..2f7e16b --- /dev/null +++ b/src/main/kotlin/com/coffee/api/common/aop/PageViewed.kt @@ -0,0 +1,5 @@ +package com.coffee.api.common.aop + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class PageViewed(val name: String) diff --git a/src/main/kotlin/com/coffee/api/config/ApiControllerAdvice.kt b/src/main/kotlin/com/coffee/api/config/ApiControllerAdvice.kt index ef4a767..9c1671f 100644 --- a/src/main/kotlin/com/coffee/api/config/ApiControllerAdvice.kt +++ b/src/main/kotlin/com/coffee/api/config/ApiControllerAdvice.kt @@ -8,7 +8,9 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.RestControllerAdvice -@RestControllerAdvice +@RestControllerAdvice( + basePackages = ["com.coffee.api.cafe.presentation.adapter.in.restapi"] +) @io.swagger.v3.oas.annotations.Hidden class ApiControllerAdvice( private val discordClientAdapter: DiscordClientAdapter diff --git a/src/main/kotlin/com/coffee/api/config/aop/CafeConfig.kt b/src/main/kotlin/com/coffee/api/config/aop/CafeConfig.kt new file mode 100644 index 0000000..e716a94 --- /dev/null +++ b/src/main/kotlin/com/coffee/api/config/aop/CafeConfig.kt @@ -0,0 +1,14 @@ +package com.coffee.api.config.aop + +import io.micrometer.core.aop.CountedAspect +import io.micrometer.core.instrument.MeterRegistry +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class CafeConfig { + + @Bean + fun countedAspect(registry: MeterRegistry): CountedAspect = + CountedAspect(registry) +}