feat(ranking): 랭킹 조회 관측 로그를 추가한다

This commit is contained in:
2026-06-09 00:09:17 +09:00
parent 5f08165239
commit 394786e6bc
2 changed files with 89 additions and 17 deletions

View File

@@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingItem
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingBlockPort
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotPort
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotRecord
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@@ -15,11 +16,18 @@ class CreatorRankingQueryService(
@Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String
) {
private val log = LoggerFactory.getLogger(javaClass)
@Transactional(readOnly = true)
fun getCreatorRankings(viewerMemberId: Long?): CreatorRankingResult {
val startedAt = System.currentTimeMillis()
return runCatching {
val latestItems = snapshotPort.findLatestSnapshots().toRankedItems()
if (latestItems.isEmpty()) {
return CreatorRankingResult(showRankChange = false, items = emptyList())
return@runCatching QueryLogResult(
result = CreatorRankingResult(showRankChange = false, items = emptyList()),
blockedCreatorCount = 0
)
}
val previousItems = snapshotPort.findPreviousCompletedSnapshots().toRankedItems()
@@ -34,9 +42,33 @@ class CreatorRankingQueryService(
).maskIfBlocked(blockedCreatorIds)
}
return CreatorRankingResult(showRankChange = showRankChange, items = items)
QueryLogResult(
result = CreatorRankingResult(showRankChange = showRankChange, items = items),
blockedCreatorCount = blockedCreatorIds.size
)
}.onSuccess { logResult ->
log.info(
"event=creator_ranking_query_success showRankChange={} itemCount={} blockedCreatorCount={} elapsedMs={}",
logResult.result.showRankChange,
logResult.result.items.size,
logResult.blockedCreatorCount,
System.currentTimeMillis() - startedAt
)
}.onFailure { ex ->
log.warn(
"event=creator_ranking_query_failure elapsedMs={} error={}",
System.currentTimeMillis() - startedAt,
ex.message,
ex
)
}.getOrThrow().result
}
private data class QueryLogResult(
val result: CreatorRankingResult,
val blockedCreatorCount: Int
)
private fun List<CreatorRankingSnapshotRecord>.toRankedItems(): List<CreatorRankingItem> {
return groupBy { it.finalScore }
.toSortedMap(compareByDescending { it })

View File

@@ -8,11 +8,16 @@ import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotRecord
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.system.CapturedOutput
import org.springframework.boot.test.system.OutputCaptureExtension
import java.time.LocalDateTime
@ExtendWith(OutputCaptureExtension::class)
class CreatorRankingQueryServiceTest {
@Test
@DisplayName("스냅샷 후보와 조회 item 내부 모델은 순위 변화와 신규 진입 값을 담을 수 있다")
@@ -177,6 +182,37 @@ class CreatorRankingQueryServiceTest {
assertEquals("profile-1.png", result.items.single().profileImageUrl)
}
@Test
@DisplayName("크리에이터 랭킹 조회 성공은 순위 변화 노출 여부와 반환 수를 로그로 남긴다")
fun shouldLogCreatorRankingQuerySuccessWithResultCounts(output: CapturedOutput) {
val snapshotPort = FakeCreatorRankingQuerySnapshotPort()
snapshotPort.latestSnapshots = listOf(snapshot(creatorId = 1L, finalScore = 100.0))
val service = service(snapshotPort = snapshotPort)
service.getCreatorRankings(viewerMemberId = null)
assertTrue(output.out.contains("event=creator_ranking_query_success"))
assertTrue(output.out.contains("showRankChange=false"))
assertTrue(output.out.contains("itemCount=1"))
assertTrue(output.out.contains("blockedCreatorCount=0"))
}
@Test
@DisplayName("크리에이터 랭킹 조회 실패는 에러를 로그로 남기고 예외를 전파한다")
fun shouldLogCreatorRankingQueryFailureWithError(output: CapturedOutput) {
val snapshotPort = FakeCreatorRankingQuerySnapshotPort()
snapshotPort.latestFailure = IllegalStateException("latest snapshots failed")
val service = service(snapshotPort = snapshotPort)
val exception = assertThrows(IllegalStateException::class.java) {
service.getCreatorRankings(viewerMemberId = 99L)
}
assertEquals("latest snapshots failed", exception.message)
assertTrue(output.out.contains("event=creator_ranking_query_failure"))
assertTrue(output.out.contains("error=latest snapshots failed"))
}
private fun service(
snapshotPort: CreatorRankingSnapshotPort = FakeCreatorRankingQuerySnapshotPort(),
blockPort: CreatorRankingBlockPort = FakeCreatorRankingBlockPort()
@@ -219,13 +255,17 @@ class CreatorRankingQueryServiceTest {
private class FakeCreatorRankingQuerySnapshotPort : CreatorRankingSnapshotPort {
var latestSnapshots: List<CreatorRankingSnapshotRecord> = emptyList()
var previousSnapshots: List<CreatorRankingSnapshotRecord> = emptyList()
var latestFailure: RuntimeException? = null
override fun findSnapshotsByAggregationPeriod(
aggregationStartAtUtc: LocalDateTime,
aggregationEndAtUtc: LocalDateTime
): List<CreatorRankingSnapshotRecord> = emptyList()
override fun findLatestSnapshots(): List<CreatorRankingSnapshotRecord> = latestSnapshots
override fun findLatestSnapshots(): List<CreatorRankingSnapshotRecord> {
latestFailure?.let { throw it }
return latestSnapshots
}
override fun findPreviousCompletedSnapshots(): List<CreatorRankingSnapshotRecord> = previousSnapshots