Skip to content

feat(observability): 세션수집·헬스체크·대시보드·시간대별지표 API 구현 #29 - #70

Open
tlgms wants to merge 4 commits into
feature/29-observability-01-foundationfrom
feature/29-observability-02-core-apis
Open

feat(observability): 세션수집·헬스체크·대시보드·시간대별지표 API 구현 #29#70
tlgms wants to merge 4 commits into
feature/29-observability-01-foundationfrom
feature/29-observability-02-core-apis

Conversation

@tlgms

@tlgms tlgms commented Aug 11, 2026

Copy link
Copy Markdown

Summary

Related Issue

Implementation

  • 세션 이벤트 수집: ENTER/HEARTBEAT/LEAVE, rate limit 적용
  • 헬스체크: JDK HttpClient로 각 서비스 actuator/health 호출, 응답시간/의존성 상태로 UP/DEGRADED/DOWN 판정
  • 대시보드: 트래픽/동시접속/기기분포는 세션 저장소, 서비스별 활동은 헬스체크 재사용. round 파라미터 검증(ROUND_NOT_FOUND)
  • 시간대별 지표: VISITOR는 세션 이벤트를 5분 단위 Redis Set에 기록 후 interval에 맞춰 합집합 집계. API_REQUEST는 수집 경로가 문서에 없어 0 고정(코드 주석으로 근거 명시)
  • 대시보드가 필요로 하는 ClientLogStorePort(카운트만)/StorageUsagePort/RoundPort의 최초 구현도 이 PR에 포함 (다음 PR에서 조회/수집 API로 확장)

Testing

  • bazel build/test //systems/observability/... 통과
  • 세션/헬스체크/대시보드/지표 서비스 단위 테스트 포함

Checklist

  • API 단위로 커밋 분리
  • 코드 리뷰

tlgms and others added 4 commits August 9, 2026 21:58
POST /api/monitor/v11/collect/session (ENTER/HEARTBEAT/LEAVE) 컨트롤러
추가. rate limit 초과 시 429, 알 수 없는 sessionId는 404
SESSION_NOT_FOUND로 응답.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GET /api/monitor/v11/health 구현. JDK HttpClient로 각 서비스의
actuator/health를 호출해 응답시간·의존성 상태를 UP/DEGRADED/DOWN으로
판정. monitor.services.<서비스>.base-url 설정으로 대상 서비스 지정,
로컬 기본값을 application.yaml에 추가.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GET /api/monitor/v11/dashboard 구현. 트래픽/동시접속/기기분포는 세션
저장소, 서비스별 활동은 헬스체크 재사용. round 파라미터는
monitor.round.name 설정과 비교해 ROUND_NOT_FOUND를 판정한다.

클라이언트 오류 집계(clientLog)와 스토리지 사용량(resource)을 위해
ClientLogStorePort/RedisClientLogStoreAdapter(fingerprint 그룹핑 공용
헬퍼 포함)와 StorageUsagePort/LocalDiskStorageUsageAdapter를 함께
추가 — logs/client, resources API에서 재사용한다.

API 요청 수(api)와 원서접수/PDF 통계(business)는 다른 서비스로부터
받는 수집 경로가 문서에 없어 0으로 고정하고 코드에 근거를 남겼다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GET /api/monitor/v11/metrics/series 구현. VISITOR는 세션 ENTER/HEARTBEAT
시각을 5분 단위 Redis Set에 기록해두고 조회 시 요청 interval에 맞춰
합집합으로 집계. 빈 버킷은 0으로 채운다. from>to·범위 90일 초과는
INVALID_TIME_RANGE, 지원하지 않는 interval이거나 버킷 1000개 초과는
INVALID_INTERVAL, 알 수 없는 지표명은 INVALID_METRIC.

API_REQUEST는 수집 경로가 아직 없어 0으로 고정(대시보드와 동일한 gap).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Caution

Review failed

Failed to post review comments.

We encountered an issue with GitHub. Use @coderabbitai full review to retry the review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2ae8cb5f-551d-4ea4-abbe-882ca7188834

📥 Commits

Reviewing files that changed from the base of the PR and between b0d675f and 4e1b95b.

📒 Files selected for processing (46)
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt
  • systems/observability/observability-adapter-out/deps.bzl
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-bootstrap/src/main/resources/application.yaml
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
🧰 Additional context used
📓 Path-based instructions (7)
**/{BUILD.bazel,*.bzl}

📄 CodeRabbit inference engine (Custom checks)

In BUILD.bazel and .bzl files, require buildifier-compatible formatting and stable target naming

Files:

  • systems/observability/observability-adapter-out/deps.bzl
**/*.bzl

⚙️ CodeRabbit configuration file

**/*.bzl: Apply Bazel Starlark (.bzl) style guidance.

Readability and docs:

  • Keep file/module docstrings and docstrings for public functions/macros.
  • Use descriptive parameter names and document attribute intent.

API design:

  • Macros should take a name argument and derive generated target names from it.
  • Prefer keyword arguments when calling macros for clarity and stability.
  • Keep macro side effects predictable and visible.

Encapsulation:

  • Use private visibility for helper targets created by macros unless explicitly public.
  • Avoid exposing internal implementation targets unintentionally.

Tooling:

  • Enforce buildifier formatting and lint compliance.

Files:

  • systems/observability/observability-adapter-out/deps.bzl
**/*.{kt,go}

📄 CodeRabbit inference engine (Custom checks)

If production logic is changed in Kotlin or Go files, require corresponding test updates in the same subsystem unless the PR description explicitly justifies why tests are unnecessary

Files:

  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt
**/*.{java,kt,scala,groovy,go,js,ts,tsx,jsx,py,rb,rs,cpp,c,h,hpp,cs}

📄 CodeRabbit inference engine (Custom checks)

Flag TODO/FIXME comments introduced by this PR that do not include an issue reference in the form #123 or a full tracker key like PROJ-123

Files:

  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: Apply Kotlin Official Coding Conventions.

Formatting and structure:

  • Use 4 spaces for indentation; no tabs.
  • Keep files focused and readable; avoid horizontal alignment for spacing.
  • Place related declarations together and keep overloads adjacent.
  • Keep implementation member order stable and logical for readability.

Naming:

  • Package names are lowercase and do not use underscores.
  • Class/object names use UpperCamelCase.
  • Functions/properties/local variables use lowerCamelCase.
  • Constants use UPPER_SNAKE_CASE only for true constants.

API and null-safety:

  • Avoid platform type leakage in public APIs.
  • Use explicit types in public APIs when inference obscures meaning.
  • Prefer immutable values (val) over mutable values (var) unless mutation is required.
  • Flag nullable flows that can be replaced with safer modeling.

Imports and idioms:

  • Avoid wildcard imports unless justified by language/tooling conventions.
  • Prefer expression bodies for short, clear functions.
  • Prefer standard library idioms over custom utility wrappers when equivalent.

Architecture and tests:

  • Respect module boundaries (domain/application/adapter/bootstrap layering).
  • Highlight behavior-changing code that lacks corresponding unit/integration tests.
  • Ask for deterministic tests and meaningful assertions, not only happy-path checks.

Files:

  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt
**/*-application/**/*.{java,kt,scala,groovy}

📄 CodeRabbit inference engine (Custom checks)

For files under *-application modules, flag direct dependency on infrastructure-specific framework classes unless justified

Files:

  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
**/*-domain/**/*.{java,kt,scala,groovy}

📄 CodeRabbit inference engine (Custom checks)

For files under *-domain modules, fail if imports reference adapter or bootstrap packages

Files:

  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
🪛 detekt (1.23.8)
systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt

[warning] 45-45: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)


[warning] 45-45: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

📝 Walkthrough

세션 활동, 서비스 상태, 대시보드 지표, 방문자 시계열 데이터를 조회할 수 있는 관측성 API를 제공합니다.

주요 변경 사항

  • ENTER, HEARTBEAT, LEAVE 세션 이벤트 수집 API를 추가했습니다.
  • 세션 이벤트 처리에 rate limit과 세션 오류 처리를 적용했습니다.
  • /api/monitor/v11/health에서 서비스별 Actuator health 상태를 조회합니다.
  • /api/monitor/v11/dashboard에서 대시보드 스냅샷을 제공합니다.
  • /api/monitor/v11/metrics/series에서 5m, 30m, 1h, 1d 간격의 시계열을 조회합니다.
  • Redis Set 기반으로 5분 단위 고유 방문자 수를 집계하고 91일 TTL을 적용했습니다.
  • 클라이언트 로그, 스토리지 사용량, 모니터링 회차를 위한 포트와 어댑터를 추가했습니다.
  • API 요청 및 일부 비즈니스 지표는 수집 경로가 없어 0으로 반환합니다.

아키텍처 변경

  • 웹 계층에 세션 수집, 헬스체크, 대시보드, 시계열 조회 컨트롤러를 추가했습니다.
  • 애플리케이션 계층에 조회 Use Case와 서비스 구현을 추가했습니다.
  • 출력 DTO와 도메인 결과 모델 사이의 ResponseMapper를 추가했습니다.
  • Actuator 호출은 JDK HttpClient를 사용하며 요청 및 연결 제한 시간은 3초입니다.
  • Redis 어댑터가 방문자 집계와 클라이언트 로그 저장을 담당합니다.
  • ClientLogStorePort, StorageUsagePort, RoundPort, HealthCheckPort, MetricsStorePort 경계를 추가했습니다.

위험 영역

  • 대상 서비스 URL이 없거나 health 응답이 비정상이면 의존성 정보와 응답 시간이 비어 있는 상태로 반환됩니다.
  • 대시보드의 API 및 비즈니스 지표는 현재 실제 수집값이 아닙니다.
  • 시계열 조회 기간은 최대 90일이며 버킷 수는 최대 1,000개입니다.
  • Redis 임시 키와 SUNIONSTORE 사용량을 모니터링해야 합니다.
  • 클라이언트 IP는 X-Forwarded-For의 첫 번째 값을 사용하므로 프록시 신뢰 경계를 확인해야 합니다.

마이그레이션 및 호환성

  • Redis 연결 설정을 배포 환경에 추가해야 합니다.
  • monitor.services, monitor.round, 리포트 저장 경로를 환경에 맞게 설정해야 합니다.
  • 기존 세션 수집 서비스 생성자는 MetricsStorePort 의존성을 포함해야 합니다.
  • 신규 API를 사용하는 클라이언트는 응답의 0, null, 상태 분류 값을 수집 공백 또는 점검 실패로 처리해야 합니다.

검증 체크리스트 및 롤아웃

  • bazel build/test //systems/observability/... 통과
  • 세션 방문자 기록 및 시간대별 집계 검증
  • 서비스 상태와 의존성 상태 계산 검증
  • 대시보드 기간, 로그, 스토리지, 디바이스, 활성 사용자 지표 검증
  • 지원 간격과 시간 범위 검증
  • fingerprint 결정성 및 형식 검증
  • 배포 전 Redis TTL, 임시 키, 메모리 사용량 확인
  • 배포 전 Actuator URL과 프록시의 X-Forwarded-For 설정 확인
  • 단계적으로 API를 활성화하고 0 반환 지표를 별도 모니터링

Walkthrough

관측성 모니터링 기능을 추가했습니다. 대시보드, 헬스, 메트릭 시계열, 세션 수집 API와 애플리케이션 서비스를 구현했습니다. Redis 저장소, Actuator 헬스 점검, 라운드 및 디스크 사용량 어댑터와 관련 설정을 추가했습니다.

Changes

관측성 모니터링 기능

Layer / File(s) Summary
애플리케이션 계약과 도메인 모델
systems/observability/observability-application/.../port/**, systems/observability/observability-domain/.../service/*
대시보드, 헬스, 메트릭 시계열, 클라이언트 로그, 방문자 메트릭, 라운드, 저장소 사용량 계약과 결과 모델을 추가했습니다. fingerprint 생성과 시간 버킷 계산도 추가했습니다.
저장소와 외부 서비스 연동
systems/observability/observability-adapter-out/.../health/*, .../redis/*, .../round/*, .../storage/*, systems/observability/observability-bootstrap/.../application.yaml, systems/observability/observability-adapter-out/deps.bzl
Redis 방문자·클라이언트 로그 저장소, Actuator 헬스 점검, 정적 라운드, 로컬 디스크 사용량 어댑터를 추가했습니다. Redis, JWT, 서비스 URL, 라운드 설정을 추가했습니다.
관측성 애플리케이션 서비스
systems/observability/observability-application/.../MetricsSeriesService.kt, .../MonitorDashboardService.kt, .../MonitorHealthService.kt, .../SessionCollectionService.kt
대시보드 스냅샷과 서비스 헬스 결과를 생성합니다. 메트릭 시계열 입력과 버킷 수를 검증합니다. 세션 입장 및 heartbeat 이벤트를 방문자 메트릭에 기록합니다.
웹 API와 응답 변환
systems/observability/observability-adapter-in/.../controller/*, .../dto/**
대시보드, 헬스, 메트릭 시계열 조회와 세션 이벤트 수집 GET·POST 엔드포인트를 추가했습니다. 애플리케이션 결과를 웹 응답 DTO로 변환합니다.
서비스와 도메인 검증
systems/observability/observability-application/src/test/**, systems/observability/observability-domain/src/test/**
대시보드, 헬스, 메트릭 시계열, 세션 수집, fingerprint, 시간 버킷 동작과 예외 처리를 검증하는 테스트를 추가했습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: feature, kotlin, bazel, security

Sequence Diagram(s)

sequenceDiagram
  participant DashboardController
  participant MonitorDashboardService
  participant RoundPort
  participant HealthCheckPort
  participant ClientLogStorePort

  DashboardController->>MonitorDashboardService: getSnapshot(round)
  MonitorDashboardService->>RoundPort: current()
  MonitorDashboardService->>HealthCheckPort: check(service)
  MonitorDashboardService->>ClientLogStorePort: countByLevel(from, to)
  MonitorDashboardService-->>DashboardController: DashboardSnapshotResponse
Loading
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/29-observability-02-core-apis
  • 🛠️ cleanup stale imports
  • 🛠️ harden error messages
  • 🛠️ test clarity pass

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 21

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt (1)

23-28: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

visitor 기록을 검증하도록 테스트 대역을 변경하세요.

FakeMetricsStorePort.recordVisitor가 무동작이므로 SessionCollectionService.kt의 Line 41과 Line 49 호출이 제거되어도 이 테스트는 통과합니다. 기록된 (sessionId, at) 목록을 저장하세요. 성공한 ENTER와 HEARTBEAT가 각각 올바른 세션 ID와 고정 Clock 시간을 기록하는지 검증하세요. 알 수 없는 HEARTBEAT는 기록하지 않는지도 검증하세요.

As per coding guidelines, “If production logic is changed in Kotlin or Go files, require corresponding test updates in the same subsystem unless the PR description explicitly justifies why tests are unnecessary.”

Also applies to: 79-81

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt`
around lines 23 - 28, Update SessionCollectionServiceTest and
FakeMetricsStorePort so recordVisitor stores each recorded (sessionId, at) pair
instead of being a no-op. Add assertions that successful ENTER and HEARTBEAT
calls record the expected session ID with the fixed Clock time, and that an
unknown HEARTBEAT records nothing.

Sources: Coding guidelines, Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt`:
- Around line 19-31: MetricsSeriesController의 /api/monitor/v11/metrics/series
엔드포인트에 대한 adapter-in 컨트롤러 테스트를 추가하세요. metrics=VISITOR,API_REQUEST 요청이 정상 응답과 예상한
시리즈 데이터를 반환하는지 검증하고, 존재하지 않는 지표명이 포함된 요청은 INVALID_METRIC 오류 응답을 반환하는지 검증하세요.

In
`@systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt`:
- Around line 37-42: clientIp가 신뢰되지 않은 요청의 X-Forwarded-For를 rate limit 키로 사용하지
않도록 수정하십시오. 요청의 remoteAddr가 기존 신뢰 프록시 설정에 포함된 경우에만 전달 헤더의 정규화된 첫 번째 주소를 사용하고, 그
외에는 항상 request.remoteAddr를 반환하도록 clientIp를 변경하십시오. 신뢰 프록시 및 비신뢰 요청, 헤더 변조 사례를
검증하는 SessionCollectController 테스트를 추가하십시오.

In
`@systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt`:
- Around line 42-122: adapter-in 계약 테스트를 추가하십시오. ResponseMapper.kt 42-122의 변환
함수들은 중첩 응답 필드가 올바르게 매핑되는지 검증하고, DashboardController.kt 15-19는 선택적 round와 응답 본문을
검증하십시오. HealthController.kt 14-16은 상태 코드와 응답 본문을 검증하십시오.
SessionCollectController.kt 21-42는 202 Accepted, 요청 검증, User-Agent 및 클라이언트 IP 전달
처리를 검증하십시오.

In
`@systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt`:
- Around line 29-35: Move the HttpRequest construction, including URI.create in
the health-check flow of ActuatorHealthCheckAdapter, inside the existing try
block so malformed monitor.services.*.base-url values are caught and handled as
DOWN responses. Keep the current request timing and response handling behavior
unchanged for valid URLs.
- Around line 45-46: Update the exception handling around HttpClient.send in the
health-check adapter to catch InterruptedException separately, restore the
thread’s interrupt status with Thread.currentThread().interrupt(), and then
return the existing failed ServiceHealthCheck result; retain the generic
Exception handling for other failures.

In
`@systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt`:
- Around line 29-31: Update the upsert flow in FingerprintLogStore so
fingerprint Hash entries and index ZSet members follow an explicit retention
period. Reuse the existing occurredAt score and retention configuration to
remove entries older than the retention cutoff from each relevant index via
removeRangeByScore, and apply expiration to entryKey records as needed so both
storage paths are bounded.
- Around line 16-32: Update FingerprintLogStore.upsert to remove the
redis.hasKey(entryKey) check and atomically increment FIELD_COUNT first, using
the returned count to detect whether the hash was newly created. Initialize
fields and FIELD_FIRST_OCCURRED_AT only when the count is 1, while preserving
the existing last-occurrence and sorted-set updates; use a Lua script if the
entire upsert must be atomic.
- Around line 40-49: Update the page method’s Redis fetch limit so that when
cursor is non-null it requests one additional record beyond size + 1,
compensating for the subsequent lastId removal; retain the current limit for an
initial page and continue deriving hasNext from the filtered results.

In
`@systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt`:
- Line 44: Update the item-loading logic around fingerprints and
RedisClientLogStoreAdapter so store.entry is executed through
StringRedisTemplate.executePipelined, batching all HGETALL operations into a
single Redis round trip; preserve the existing toEntry mapping and null
filtering behavior.
- Around line 41-55: Update RedisClientLogStoreAdapter.list so multi-level
queries do not page through the ALL group and then discard entries. For
levels.size > 1, fetch and merge matching level entries by score (or continue
fetching until size filtered items are collected), ensuring items fills the
requested size where data exists. Recalculate hasNext and nextCursor from the
final filtered items and preserve correct continuation past excluded entries.
- Around line 61-63: Update the Redis entry conversion in toEntry so unknown
persisted values for LogLevel or LogSource are handled without propagating
IllegalArgumentException. Catch or otherwise safely handle enum resolution
failures and return null for that entry, allowing list retrieval to skip only
the invalid record while preserving normal conversion for valid values.

In
`@systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt`:
- Around line 27-53: Update visitorCount and its bucket-range handling so
queries with partial 5-minute boundaries do not count events outside [from, to);
either enforce and validate aligned ranges for every caller or replace the Set
aggregation with a time-aware structure that filters exact timestamps. Preserve
correct empty, single-bucket, and multi-bucket behavior, and add tests covering
partial ranges and exact bucket boundaries.

In
`@systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt`:
- Around line 36-49: Update MetricsSeriesService to validate the expected bucket
count before materializing bucketStarts, rejecting ranges above MAX_BUCKETS
without creating the full list. Add a batch interval-query method to
MetricsStorePort and implement it in the storage adapter using a pipeline or
single script, then have the VISITOR branch reuse the returned values for all
buckets instead of calling visitorCount per bucket; preserve API_REQUEST’s zero
values and existing result ordering.

In
`@systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt`:
- Around line 57-66: Update MonitorDashboardService’s per-service aggregation to
run health checks concurrently and enforce one short overall timeout, relying on
ActuatorHealthCheckAdapter’s exception-to-unhealthy conversion without duplicate
handling. Add a brief health-result cache, extend SessionStorePort with a
batched per-service concurrent-user lookup (or Redis pipeline), and use it
instead of individual concurrentUsers calls. Add deterministic tests covering
parallel execution, timeout behavior, caching, and batched session retrieval.

In
`@systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt`:
- Around line 22-36: Update MonitorHealthService.getHealth so
healthCheckPort.check(service) calls do not execute sequentially; use bounded
parallel execution or a short-TTL cache to keep total latency near a single
timeout. Preserve the existing ServiceHealthItemResult mapping and
status/dependency handling for each service.
- Around line 14-20: Remove the Spring `@Service` annotation and its import from
MonitorHealthService, then register a GetServiceHealthUseCase bean in the
observability-bootstrap configuration that constructs MonitorHealthService with
the configured HealthCheckPort and Clock dependencies.

In
`@systems/observability/observability-bootstrap/src/main/resources/application.yaml`:
- Around line 48-51: application.yaml의 round.from 및 round.to 기본값이 2026년에 고정되지
않도록 수정하세요. 배포 환경에서 MONITOR_ROUND_FROM과 MONITOR_ROUND_TO를 반드시 설정하도록 필수 설정으로 만들고,
해당 환경 변수 설정 방법을 배포 문서에 명시하세요.
- Line 28: Remove the hardcoded fallback from the JWT secret configuration value
so it references only the required JWT_SECRET environment variable and startup
fails when it is missing. Keep any development-only secret confined to
application-local.yaml rather than the shared application.yaml configuration.

In
`@systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt`:
- Around line 7-10: Fingerprint.of에서 SHA-256 결과를 8자로 자르는 동작을 16자 이상을 사용하도록 변경해
충돌 가능성을 낮추세요. FingerprintLogStore.upsert와의 기존 fingerprint 생성 계약은 유지하고,
MessageDigest.getInstance("SHA-256") 호출마다 새 인스턴스를 생성하는 현재 동작은 재사용하지 마세요.

In
`@systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt`:
- Around line 6-16: ServiceLabels의 런타임 Map 프로퍼티 LABELS를 lowerCamelCase인 labels로
변경하고, 함수 of 내부의 모든 참조도 labels를 사용하도록 업데이트하세요.

In
`@systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt`:
- Around line 18-26: Update the public bucketStarts function to validate
interval at entry and reject zero or negative durations before entering the
while loop. Preserve the existing bucketing behavior for positive intervals and
use the project’s established argument-validation convention if available.

---

Outside diff comments:
In
`@systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt`:
- Around line 23-28: Update SessionCollectionServiceTest and
FakeMetricsStorePort so recordVisitor stores each recorded (sessionId, at) pair
instead of being a no-op. Add assertions that successful ENTER and HEARTBEAT
calls record the expected session ID with the fixed Clock time, and that an
unknown HEARTBEAT records nothing.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2ae8cb5f-551d-4ea4-abbe-882ca7188834

📥 Commits

Reviewing files that changed from the base of the PR and between b0d675f and 4e1b95b.

📒 Files selected for processing (46)
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt
  • systems/observability/observability-adapter-out/deps.bzl
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-bootstrap/src/main/resources/application.yaml
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/{BUILD.bazel,*.bzl}

📄 CodeRabbit inference engine (Custom checks)

In BUILD.bazel and .bzl files, require buildifier-compatible formatting and stable target naming

Files:

  • systems/observability/observability-adapter-out/deps.bzl
**/*.bzl

⚙️ CodeRabbit configuration file

**/*.bzl: Apply Bazel Starlark (.bzl) style guidance.

Readability and docs:

  • Keep file/module docstrings and docstrings for public functions/macros.
  • Use descriptive parameter names and document attribute intent.

API design:

  • Macros should take a name argument and derive generated target names from it.
  • Prefer keyword arguments when calling macros for clarity and stability.
  • Keep macro side effects predictable and visible.

Encapsulation:

  • Use private visibility for helper targets created by macros unless explicitly public.
  • Avoid exposing internal implementation targets unintentionally.

Tooling:

  • Enforce buildifier formatting and lint compliance.

Files:

  • systems/observability/observability-adapter-out/deps.bzl
**/*.{kt,go}

📄 CodeRabbit inference engine (Custom checks)

If production logic is changed in Kotlin or Go files, require corresponding test updates in the same subsystem unless the PR description explicitly justifies why tests are unnecessary

Files:

  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt
**/*.{java,kt,scala,groovy,go,js,ts,tsx,jsx,py,rb,rs,cpp,c,h,hpp,cs}

📄 CodeRabbit inference engine (Custom checks)

Flag TODO/FIXME comments introduced by this PR that do not include an issue reference in the form #123 or a full tracker key like PROJ-123

Files:

  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: Apply Kotlin Official Coding Conventions.

Formatting and structure:

  • Use 4 spaces for indentation; no tabs.
  • Keep files focused and readable; avoid horizontal alignment for spacing.
  • Place related declarations together and keep overloads adjacent.
  • Keep implementation member order stable and logical for readability.

Naming:

  • Package names are lowercase and do not use underscores.
  • Class/object names use UpperCamelCase.
  • Functions/properties/local variables use lowerCamelCase.
  • Constants use UPPER_SNAKE_CASE only for true constants.

API and null-safety:

  • Avoid platform type leakage in public APIs.
  • Use explicit types in public APIs when inference obscures meaning.
  • Prefer immutable values (val) over mutable values (var) unless mutation is required.
  • Flag nullable flows that can be replaced with safer modeling.

Imports and idioms:

  • Avoid wildcard imports unless justified by language/tooling conventions.
  • Prefer expression bodies for short, clear functions.
  • Prefer standard library idioms over custom utility wrappers when equivalent.

Architecture and tests:

  • Respect module boundaries (domain/application/adapter/bootstrap layering).
  • Highlight behavior-changing code that lacks corresponding unit/integration tests.
  • Ask for deterministic tests and meaningful assertions, not only happy-path checks.

Files:

  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisClientLogStoreAdapter.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/FingerprintLogStore.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
  • systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/redis/RedisMetricsStoreAdapter.kt
**/*-application/**/*.{java,kt,scala,groovy}

📄 CodeRabbit inference engine (Custom checks)

For files under *-application modules, flag direct dependency on infrastructure-specific framework classes unless justified

Files:

  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthService.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt
  • systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardService.kt
  • systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt
**/*-domain/**/*.{java,kt,scala,groovy}

📄 CodeRabbit inference engine (Custom checks)

For files under *-domain modules, fail if imports reference adapter or bootstrap packages

Files:

  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt
  • systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt
  • systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt
🪛 detekt (1.23.8)
systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt

[warning] 45-45: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)


[warning] 45-45: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🔇 Additional comments (30)
systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetDashboardSnapshotUseCase.kt (1)

5-8: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/DashboardSnapshotResult.kt (1)

7-67: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/RoundPort.kt (1)

5-9: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/StorageUsagePort.kt (1)

5-15: LGTM!

systems/observability/observability-bootstrap/src/main/resources/application.yaml (1)

32-35: 🩺 Stability & Availability

auth의 프로세스 구성을 확인하세요.

현재 저장소에는 별도 auth bootstrap 모듈이 없지만, observability는 AUTH를 독립 서비스로 헬스 체크합니다. 각 서비스의 server.port 선언도 없습니다. authidentity와 별도 프로세스라면 기본 포트를 분리하세요.

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetMetricsSeriesUseCase.kt (1)

7-9: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/MetricsSeriesResult.kt (1)

6-15: LGTM!

systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/StaticRoundAdapter.kt (1)

7-12: LGTM!

systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/MetricsSeriesResponse.kt (1)

6-15: LGTM!

systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServiceHealthResponse.kt (1)

7-25: LGTM!

systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt (1)

6-13: LGTM!

systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MetricsSeriesServiceTest.kt (1)

14-66: LGTM!

systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/TimeBucketerTest.kt (1)

10-27: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServiceHealthUseCase.kt (1)

5-7: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ServiceHealthResult.kt (1)

7-25: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/HealthCheckPort.kt (1)

5-13: LGTM!

systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/MonitorServiceProperties.kt (1)

7-13: LGTM!

systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/round/MonitorRoundProperties.kt (1)

7-14: LGTM!

systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/storage/LocalDiskStorageUsageAdapter.kt (1)

11-30: LGTM!

systems/observability/observability-adapter-out/deps.bzl (1)

1-12: LGTM!

systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorHealthServiceTest.kt (1)

13-34: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ClientLogStorePort.kt (1)

8-46: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/MetricsStorePort.kt (1)

5-10: LGTM!

systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/SessionCollectionService.kt (1)

5-5: LGTM!

Also applies to: 22-22, 41-49

systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/SessionEventRequest.kt (1)

7-12: LGTM!

systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/SessionEventResponse.kt (1)

3-6: LGTM!

systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/DashboardSnapshotResponse.kt (1)

7-60: LGTM!

systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/MonitorDashboardServiceTest.kt (1)

19-87: LGTM!

systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt (1)

11-12: LGTM!

systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/FingerprintTest.kt (1)

8-24: LGTM!

Comment on lines +19 to +31
@GetMapping("/api/monitor/v11/metrics/series")
fun series(
@RequestParam metrics: String,
@RequestParam(required = false) from: Instant?,
@RequestParam(required = false) to: Instant?,
@RequestParam(required = false) interval: String?,
): ApiResponse<MetricsSeriesResponse> {
val parsedMetrics = metrics.split(",").map { raw ->
runCatching { MetricType.valueOf(raw.trim()) }
.getOrElse { throw MonitorDomainException(ErrorCode.INVALID_METRIC) }
}
val result = getMetricsSeriesUseCase.getSeries(parsedMetrics, from, to, interval)
return ApiResponse(data = result.toResponse())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt \
  --items all

fd -t f -i '.*MetricsSeries.*Test.*' systems/observability

rg -n -C 3 \
  'MetricsSeriesController|/api/monitor/v11/metrics/series|INVALID_METRIC' \
  systems/observability

Repository: EntryDSM/entrydsm-platform

Length of output: 6811


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- adapter-in test files ---'
git ls-files 'systems/observability/observability-adapter-in/src/test/**' | sort

printf '%s\n' '--- related controller tests and endpoint assertions ---'
rg -n -i -C 4 \
  'Controller|MockMvc|WebTestClient|api/monitor|metrics/series|MetricType|INVALID_METRIC' \
  systems/observability/observability-adapter-in/src/test || true

printf '%s\n' '--- relevant diff summary ---'
git diff --stat -- \
  systems/observability/observability-adapter-in \
  systems/observability/observability-application \
  systems/observability/observability-domain

Repository: EntryDSM/entrydsm-platform

Length of output: 517


컨트롤러 테스트를 추가하세요.

MetricsSeriesController/api/monitor/v11/metrics/series 엔드포인트를 검증하는 adapter-in 테스트가 없습니다. metrics=VISITOR,API_REQUEST의 정상 응답과 잘못된 지표명에 대한 INVALID_METRIC 오류 응답을 검증하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt`
around lines 19 - 31, MetricsSeriesController의 /api/monitor/v11/metrics/series
엔드포인트에 대한 adapter-in 컨트롤러 테스트를 추가하세요. metrics=VISITOR,API_REQUEST 요청이 정상 응답과 예상한
시리즈 데이터를 반환하는지 검증하고, 존재하지 않는 지표명이 포함된 요청은 INVALID_METRIC 오류 응답을 반환하는지 검증하세요.

Sources: Coding guidelines, Path instructions

Comment on lines +37 to +42
private fun clientIp(request: HttpServletRequest): String =
request.getHeader("X-Forwarded-For")
?.substringBefore(",")
?.trim()
?.takeIf { it.isNotBlank() }
?: request.remoteAddr

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect forwarded-header and trusted-proxy configuration.
fd -a -t f 'application*.yml' 'application*.yaml' \
  systems/observability/observability-bootstrap/src/main/resources

rg -n -C 3 \
  'forward-headers|X-Forwarded-For|trusted-proxies|RemoteIp|proxy' \
  systems/observability/observability-bootstrap/src/main/resources \
  systems/observability/observability-adapter-in

Repository: EntryDSM/entrydsm-platform

Length of output: 1477


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- controller and nearby call sites ---'
cat -n systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
rg -n -C 4 'RecordSessionEventUseCase|clientIp|remoteAddr|X-Forwarded-For|rate.?limit|fingerprint' systems/observability

printf '%s\n' '--- configuration files ---'
fd -a -t f . | rg '(^|/)(application[^/]*\.(yml|yaml|properties)|.*(ingress|gateway|nginx|traefik|deployment|helm|values).*\.ya?ml)$' |
  while IFS= read -r file; do
    printf '\n--- %s ---\n' "$file"
    rg -n -C 3 'forward-headers|X-Forwarded-For|trusted-proxies|RemoteIp|proxy|ingress|rate.?limit|fingerprint' "$file" || true
  done

Repository: EntryDSM/entrydsm-platform

Length of output: 39580


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
def client_ip(headers, remote_addr):
    value = headers.get("X-Forwarded-For")
    if value is not None:
        first = value.split(",", 1)[0].strip()
        if first.strip():
            return first
    return remote_addr

cases = [
    ({"X-Forwarded-For": "198.51.100.10"}, "10.0.0.4"),
    ({"X-Forwarded-For": "198.51.100.10, 10.0.0.4"}, "10.0.0.5"),
    ({"X-Forwarded-For": "   "}, "10.0.0.6"),
    ({}, "10.0.0.7"),
]
for headers, remote_addr in cases:
    print(f"{headers!r} -> {client_ip(headers, remote_addr)!r}")

print("rate-limit keys:")
for headers, remote_addr in cases[:2]:
    ip = client_ip(headers, remote_addr)
    print(f"session:{ip}")
PY

printf '%s\n' '--- observability tests ---'
fd -a -t f . systems/observability | rg 'src/test|Test\.kt$|IT\.kt$' | sort

printf '%s\n' '--- all forwarded-header and proxy configuration references ---'
rg -n -C 3 -i \
  'forward-headers|X-Forwarded-For|trusted-prox|RemoteIpValve|ForwardedHeaderFilter|server\.forward|proxy_set_header|use-forwarded-headers|externalTrafficPolicy' \
  . --glob '!**/build/**' --glob '!**/node_modules/**' || true

Repository: EntryDSM/entrydsm-platform

Length of output: 3759


신뢰되지 않은 X-Forwarded-For를 rate limit 키로 사용하지 마십시오.

현재 SessionCollectionServiceclientIpsession:$clientIp rate limit 키로 사용합니다. 클라이언트가 X-Forwarded-For 값을 변경하면 IP별 제한을 우회할 수 있습니다.

신뢰된 프록시 요청에서만 정규화된 전달 헤더를 사용하고, 그 외 요청에서는 request.remoteAddr를 사용하십시오. 이 동작을 검증하는 컨트롤러 테스트도 추가하십시오.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt`
around lines 37 - 42, clientIp가 신뢰되지 않은 요청의 X-Forwarded-For를 rate limit 키로 사용하지
않도록 수정하십시오. 요청의 remoteAddr가 기존 신뢰 프록시 설정에 포함된 경우에만 전달 헤더의 정규화된 첫 번째 주소를 사용하고, 그
외에는 항상 request.remoteAddr를 반환하도록 clientIp를 변경하십시오. 신뢰 프록시 및 비신뢰 요청, 헤더 변조 사례를
검증하는 SessionCollectController 테스트를 추가하십시오.

Comment on lines +42 to +122
fun SessionEventResult.toResponse(): SessionEventResponse =
SessionEventResponse(sessionId = sessionId, heartbeatIntervalSeconds = heartbeatIntervalSeconds)

fun ServiceHealthResult.toResponse(): ServiceHealthResponse =
ServiceHealthResponse(
overall = overall,
checkedAt = checkedAt,
services = services.map { it.toResponse() },
)

fun ServiceHealthItemResult.toResponse(): ServiceHealthItemResponse =
ServiceHealthItemResponse(
service = service,
label = label,
status = status,
responseTimeMs = responseTimeMs,
version = version,
dependencies = dependencies.map { it.toResponse() },
)

fun DependencyStatusResult.toResponse(): DependencyStatusResponse =
DependencyStatusResponse(name = name, status = status)

fun DashboardSnapshotResult.toResponse(): DashboardSnapshotResponse =
DashboardSnapshotResponse(
generatedAt = generatedAt,
period = period.toResponse(),
traffic = traffic.toResponse(),
api = api.toResponse(),
business = business.toResponse(),
services = services.toResponse(),
clientLog = clientLog.toResponse(),
resource = resource.toResponse(),
)

fun PeriodResult.toResponse(): PeriodResponse = PeriodResponse(type = type, round = round, from = from, to = to)

fun TrafficResult.toResponse(): TrafficResponse =
TrafficResponse(
totalVisitors = totalVisitors,
concurrent = concurrent.toResponse(),
avgSessionDurationSeconds = avgSessionDurationSeconds,
devices = devices.map { it.toResponse() },
)

fun ConcurrentResult.toResponse(): ConcurrentResponse = ConcurrentResponse(current = current, max = max, avg = avg)

fun DeviceStatResult.toResponse(): DeviceStatResponse = DeviceStatResponse(type = type, count = count, ratio = ratio)

fun ApiStatsResult.toResponse(): ApiStatsResponse =
ApiStatsResponse(
totalRequests = totalRequests,
successCount = successCount,
failureCount = failureCount,
failureRate = failureRate,
)

fun BusinessStatsResult.toResponse(): BusinessStatsResponse =
BusinessStatsResponse(applicationSubmit = applicationSubmit.toResponse(), pdfDownload = pdfDownload.toResponse())

fun OutcomeCountResult.toResponse(): OutcomeCountResponse = OutcomeCountResponse(success = success, failure = failure)

fun ServiceActivityResult.toResponse(): ServiceActivityResponse =
ServiceActivityResponse(windowSeconds = windowSeconds, items = items.map { it.toResponse() })

fun ServiceActivityItemResult.toResponse(): ServiceActivityItemResponse =
ServiceActivityItemResponse(service = service, label = label, activeUsers = activeUsers, status = status)

fun ClientLogCountResult.toResponse(): ClientLogCountResponse =
ClientLogCountResponse(errorCount = errorCount, warnCount = warnCount)

fun ResourceUsageBriefResult.toResponse(): ResourceUsageBriefResponse =
ResourceUsageBriefResponse(dbUsedBytes = dbUsedBytes, bucketUsedBytes = bucketUsedBytes, measuredAt = measuredAt)

fun MetricsSeriesResult.toResponse(): MetricsSeriesResponse =
MetricsSeriesResponse(from = from, to = to, interval = interval, series = series.map { it.toResponse() })

fun MetricSeriesResult.toResponse(): MetricSeriesResponse =
MetricSeriesResponse(metric = metric, points = points.map { it.toResponse() })

fun MetricPointResult.toResponse(): MetricPointResponse = MetricPointResponse(t = t, v = v)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- changed files/stat ---'
git diff --stat
git status --short
printf '%s\n' '--- relevant files ---'
git ls-files 'systems/observability/observability-adapter-in/*' | sed -n '1,200p'
printf '%s\n' '--- candidate tests ---'
fd -i -t f 'test|tests|spec' systems/observability/observability-adapter-in 2>/dev/null | sed -n '1,200p'
printf '%s\n' '--- source outlines ---'
for f in \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt
do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    wc -l "$f"
    ast-grep outline "$f" --lang kotlin 2>/dev/null || true
  else
    echo "MISSING $f"
  fi
done

Repository: EntryDSM/entrydsm-platform

Length of output: 6308


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- changed files/stat ---'
git diff --stat
git status --short
printf '%s\n' '--- relevant files ---'
git ls-files 'systems/observability/observability-adapter-in/*' | sed -n '1,200p'
printf '%s\n' '--- candidate tests ---'
fd -i -t f 'test|tests|spec' systems/observability/observability-adapter-in 2>/dev/null | sed -n '1,200p'

Repository: EntryDSM/entrydsm-platform

Length of output: 2886


🏁 Script executed:

#!/bin/bash
set -e
for f in \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/MetricsSeriesController.kt \
  systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt \
  systems/observability/observability-adapter-in/BUILD.bazel \
  systems/observability/observability-adapter-in/src/test/kotlin/hs/kr/entrydsm/observability/adapterin/web/exception/GlobalExceptionHandlerTest.kt
do
  echo "--- $f ---"
  cat -n "$f"
done
printf '%s\n' '--- all adapter-in test-like files ---'
git ls-files | rg 'observability-adapter-in.*/(src/test|test)/|observability-adapter-in.*(Test|Spec)\.(kt|java)$' || true
printf '%s\n' '--- controller/mapper test references across repository ---'
rg -n --glob '*.{kt,java}' '(DashboardController|HealthController|SessionCollectController|ResponseMapper|/api/monitor/v11)' . | sed -n '1,240p'

Repository: EntryDSM/entrydsm-platform

Length of output: 20178


adapter-in 계약 테스트를 추가하십시오.

현재 모듈에는 GlobalExceptionHandlerTest만 있습니다. 다음 동작을 검증하는 테스트가 필요합니다.

  • ResponseMapper.kt: 중첩 응답 필드 매핑
  • DashboardController.kt: 선택적 round와 응답 본문
  • HealthController.kt: 상태 코드와 응답 본문
  • SessionCollectController.kt: 202 Accepted, 요청 검증, User-Agent 및 클라이언트 IP 처리
📍 Affects 4 files
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt#L42-L122 (this comment)
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/DashboardController.kt#L15-L19
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/HealthController.kt#L14-L16
  • systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/SessionCollectController.kt#L21-L42
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt`
around lines 42 - 122, adapter-in 계약 테스트를 추가하십시오. ResponseMapper.kt 42-122의 변환
함수들은 중첩 응답 필드가 올바르게 매핑되는지 검증하고, DashboardController.kt 15-19는 선택적 round와 응답 본문을
검증하십시오. HealthController.kt 14-16은 상태 코드와 응답 본문을 검증하십시오.
SessionCollectController.kt 21-42는 202 Accepted, 요청 검증, User-Agent 및 클라이언트 IP 전달
처리를 검증하십시오.

Source: Coding guidelines

Comment on lines +29 to +35
val request = HttpRequest.newBuilder(URI.create("$baseUrl/actuator/health"))
.timeout(TIMEOUT)
.GET()
.build()
val start = System.nanoTime()
return try {
val response = client.send(request, HttpResponse.BodyHandlers.ofString())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

요청 생성도 try 블록 안에서 처리하세요.

Line 29의 URI.create()는 잘못된 monitor.services.*.base-url 값에서 예외를 발생시킵니다. 현재 예외는 try 블록 밖에서 발생하므로, 하나의 잘못된 설정이 해당 서비스 상태를 DOWN으로 반환하지 않고 헬스 API 전체를 실패시킵니다.

변경 예시
-        val request = HttpRequest.newBuilder(URI.create("$baseUrl/actuator/health"))
-            .timeout(TIMEOUT)
-            .GET()
-            .build()
-        val start = System.nanoTime()
         return try {
+            val request = HttpRequest.newBuilder(URI.create("$baseUrl/actuator/health"))
+                .timeout(TIMEOUT)
+                .GET()
+                .build()
+            val start = System.nanoTime()
             val response = client.send(request, HttpResponse.BodyHandlers.ofString())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val request = HttpRequest.newBuilder(URI.create("$baseUrl/actuator/health"))
.timeout(TIMEOUT)
.GET()
.build()
val start = System.nanoTime()
return try {
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
return try {
val request = HttpRequest.newBuilder(URI.create("$baseUrl/actuator/health"))
.timeout(TIMEOUT)
.GET()
.build()
val start = System.nanoTime()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt`
around lines 29 - 35, Move the HttpRequest construction, including URI.create in
the health-check flow of ActuatorHealthCheckAdapter, inside the existing try
block so malformed monitor.services.*.base-url values are caught and handled as
DOWN responses. Keep the current request timing and response handling behavior
unchanged for valid URLs.

Comment on lines +45 to +46
} catch (e: Exception) {
ServiceHealthCheck(responseTimeMs = null, version = null, dependencies = emptyMap())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,120p' systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt
printf '\n--- related tests/usages ---\n'
rg -n "ActuatorHealthCheckAdapter|ServiceHealthCheck|HttpClient\.send|InterruptedException" systems/observability

Repository: EntryDSM/entrydsm-platform

Length of output: 5637


🌐 Web query:

Java HttpClient send InterruptedException thread interrupt status official documentation

💡 Result:

According to official Java documentation and implementation details for the HttpClient class, if a thread is interrupted while performing a blocking synchronous request via HttpClient.send, the method throws an InterruptedException [1][2][3]. When an InterruptedException is thrown in Java, the interrupt status of the current thread is cleared [4]. Therefore, by the time the exception propagates to the caller, the thread's interrupt status is no longer set [4]. Regarding the behavior of the HTTP request itself: 1. Cancellation Attempt: The default HttpClient implementation attempts to cancel the underlying HTTP exchange when the operation is interrupted [1][5]. 2. No Immediate Guarantee: There is no guarantee as to exactly when this cancellation will take effect [1][3]. The request might still proceed or finish on the server side because processing may have already started asynchronously in another thread, and resource cleanup may occur asynchronously [1][3]. 3. Background Execution: Historically, older versions of the Java HttpClient (prior to fixes addressing JDK-8245462 and JDK-8251312) would throw an InterruptedException but allow the HTTP request to continue in the background [6]. Modern versions aim to release resources associated with the inflight request upon interruption, though the asynchronous nature of the cleanup remains [1][7]. For developers handling this, since the interrupt status is cleared upon catching InterruptedException, you should re-interrupt the thread if you intend for downstream components or callers to be aware of the interruption: try { client.send(request, bodyHandler); } catch (InterruptedException e) { Thread.currentThread.interrupt; // Restore interrupted status // Handle cancellation... }

Citations:


인터럽트 상태를 복원하세요.

HttpClient.send()InterruptedException을 발생시키면 catch (e: Exception)이 인터럽트 상태를 제거합니다. InterruptedException을 별도로 처리하고 Thread.currentThread().interrupt()를 호출한 뒤 실패 결과를 반환하세요.

🧰 Tools
🪛 detekt (1.23.8)

[warning] 45-45: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)


[warning] 45-45: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/health/ActuatorHealthCheckAdapter.kt`
around lines 45 - 46, Update the exception handling around HttpClient.send in
the health-check adapter to catch InterruptedException separately, restore the
thread’s interrupt status with Thread.currentThread().interrupt(), and then
return the existing failed ServiceHealthCheck result; retain the generic
Exception handling for other failures.

Source: Linters/SAST tools

root: INFO
auth:
jwt:
secret: ${JWT_SECRET:local-development-secret-key-change-me-0123456789}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

JWT secret의 기본값을 제거하세요. 저장소에 커밋된 비밀키입니다.

${JWT_SECRET:local-development-secret-key-change-me-0123456789}는 환경 변수가 없으면 하드코딩된 값을 사용합니다. 배포 환경에서 JWT_SECRET 설정이 누락되면 애플리케이션은 오류 없이 기동하고, 공개된 이 키로 토큰을 검증합니다. 공격자는 같은 키로 임의의 토큰을 위조할 수 있습니다.

기본값을 제거해 미설정 시 기동이 실패하게 하세요. 로컬 편의가 필요하면 application-local.yaml에만 개발용 값을 두세요.

🔒 제안 변경
 auth:
   jwt:
-    secret: ${JWT_SECRET:local-development-secret-key-change-me-0123456789}
+    secret: ${JWT_SECRET}
     issuer: ${JWT_ISSUER:entrydsm}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-bootstrap/src/main/resources/application.yaml`
at line 28, Remove the hardcoded fallback from the JWT secret configuration
value so it references only the required JWT_SECRET environment variable and
startup fails when it is missing. Keep any development-only secret confined to
application-local.yaml rather than the shared application.yaml configuration.

Comment on lines +48 to +51
round:
name: ${MONITOR_ROUND_NAME:current}
from: ${MONITOR_ROUND_FROM:2026-01-01T00:00:00+09:00}
to: ${MONITOR_ROUND_TO:2026-12-31T23:59:59+09:00}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

round 기본값이 2026년으로 고정되어 있습니다.

MONITOR_ROUND_FROMMONITOR_ROUND_TO가 설정되지 않으면 대시보드 기간이 항상 2026년이 됩니다. MonitorDashboardService.getSnapshot은 이 값을 PeriodResult.fromPeriodResult.to로 그대로 반환합니다. 해가 바뀌면 기간 표시가 틀립니다. 배포 환경에서 세 환경 변수를 반드시 설정하도록 문서화하거나, 기본값 없이 필수로 만드세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-bootstrap/src/main/resources/application.yaml`
around lines 48 - 51, application.yaml의 round.from 및 round.to 기본값이 2026년에 고정되지
않도록 수정하세요. 배포 환경에서 MONITOR_ROUND_FROM과 MONITOR_ROUND_TO를 반드시 설정하도록 필수 설정으로 만들고,
해당 환경 변수 설정 방법을 배포 문서에 명시하세요.

Comment on lines +7 to +10
fun of(vararg parts: String): String {
val digest = MessageDigest.getInstance("SHA-256").digest(parts.joinToString("|").toByteArray())
return digest.joinToString("") { "%02x".format(it) }.take(8)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

fingerprint 길이를 늘려 그룹 병합 오류를 줄이세요.

take(8)은 32비트만 사용합니다. FingerprintLogStore.upsert는 동일 fingerprint일 때 기존 Hash 필드를 유지하고 count만 증가시킵니다. 따라서 충돌이 발생하면 서로 다른 오류가 하나의 엔트리로 합쳐지고, 조회 시 다른 오류의 messagepageUrl이 표시됩니다. 고유 오류 종류가 수만 개로 늘어나면 충돌 확률이 실질적으로 커집니다. 16자(64비트) 이상으로 늘리면 저비용으로 위험을 낮출 수 있습니다.

또한 MessageDigest.getInstance("SHA-256")을 호출할 때마다 새 인스턴스를 만드는 점은 의도된 동작입니다. MessageDigest는 스레드 안전하지 않으므로 인스턴스를 재사용하지 마세요.

♻️ 제안 변경
-    fun of(vararg parts: String): String {
-        val digest = MessageDigest.getInstance("SHA-256").digest(parts.joinToString("|").toByteArray())
-        return digest.joinToString("") { "%02x".format(it) }.take(8)
-    }
+    private const val LENGTH = 16
+
+    fun of(vararg parts: String): String {
+        val digest = MessageDigest.getInstance("SHA-256").digest(parts.joinToString("|").toByteArray())
+        return digest.joinToString("") { "%02x".format(it) }.take(LENGTH)
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun of(vararg parts: String): String {
val digest = MessageDigest.getInstance("SHA-256").digest(parts.joinToString("|").toByteArray())
return digest.joinToString("") { "%02x".format(it) }.take(8)
}
private const val LENGTH = 16
fun of(vararg parts: String): String {
val digest = MessageDigest.getInstance("SHA-256").digest(parts.joinToString("|").toByteArray())
return digest.joinToString("") { "%02x".format(it) }.take(LENGTH)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/Fingerprint.kt`
around lines 7 - 10, Fingerprint.of에서 SHA-256 결과를 8자로 자르는 동작을 16자 이상을 사용하도록 변경해
충돌 가능성을 낮추세요. FingerprintLogStore.upsert와의 기존 fingerprint 생성 계약은 유지하고,
MessageDigest.getInstance("SHA-256") 호출마다 새 인스턴스를 생성하는 현재 동작은 재사용하지 마세요.

Comment on lines +6 to +16
private val LABELS = mapOf(
ServiceName.IDENTITY to "유저",
ServiceName.AUTH to "인증",
ServiceName.APPLICATION to "접수",
ServiceName.EVALUATION to "심사",
ServiceName.DOCUMENT to "서류",
ServiceName.NOTIFICATION to "알림",
ServiceName.SCHEDULE to "일정",
)

fun of(service: ServiceName): String = LABELS.getValue(service)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

런타임 맵 이름을 lowerCamelCase로 변경하세요.

LABELSconst val로 선언할 수 없는 런타임 Map입니다. labels로 변경하세요.

변경 예시
-private val LABELS = mapOf(
+private val labels = mapOf(
 ...
-fun of(service: ServiceName): String = LABELS.getValue(service)
+fun of(service: ServiceName): String = labels.getValue(service)

As per path instructions, “Functions/properties/local variables use lowerCamelCase. Constants use UPPER_SNAKE_CASE only for true constants.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private val LABELS = mapOf(
ServiceName.IDENTITY to "유저",
ServiceName.AUTH to "인증",
ServiceName.APPLICATION to "접수",
ServiceName.EVALUATION to "심사",
ServiceName.DOCUMENT to "서류",
ServiceName.NOTIFICATION to "알림",
ServiceName.SCHEDULE to "일정",
)
fun of(service: ServiceName): String = LABELS.getValue(service)
private val labels = mapOf(
ServiceName.IDENTITY to "유저",
ServiceName.AUTH to "인증",
ServiceName.APPLICATION to "접수",
ServiceName.EVALUATION to "심사",
ServiceName.DOCUMENT to "서류",
ServiceName.NOTIFICATION to "알림",
ServiceName.SCHEDULE to "일정",
)
fun of(service: ServiceName): String = labels.getValue(service)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/ServiceLabels.kt`
around lines 6 - 16, ServiceLabels의 런타임 Map 프로퍼티 LABELS를 lowerCamelCase인 labels로
변경하고, 함수 of 내부의 모든 참조도 labels를 사용하도록 업데이트하세요.

Source: Path instructions

Comment on lines +18 to +26
fun bucketStarts(from: Instant, to: Instant, interval: Duration): List<Instant> {
val starts = mutableListOf<Instant>()
var cursor = from
while (cursor.isBefore(to)) {
starts.add(cursor)
cursor = cursor.plus(interval)
}
return starts
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

interval이 0 또는 음수면 무한 루프와 OOM이 발생합니다.

bucketStarts는 public 함수이며 Duration을 직접 받습니다. interval이 0이거나 음수이면 cursor가 전진하지 않거나 뒤로 이동합니다. 그러면 while 루프가 끝나지 않고 starts 리스트가 무한히 커집니다. 현재 호출부는 durationOf 결과만 전달하므로 재현되지 않습니다. 향후 호출부 추가에 대비해 진입 시점에 검증을 추가하세요.

🛡️ 제안 변경
     fun bucketStarts(from: Instant, to: Instant, interval: Duration): List<Instant> {
+        require(!interval.isZero && !interval.isNegative) { "interval must be positive" }
         val starts = mutableListOf<Instant>()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun bucketStarts(from: Instant, to: Instant, interval: Duration): List<Instant> {
val starts = mutableListOf<Instant>()
var cursor = from
while (cursor.isBefore(to)) {
starts.add(cursor)
cursor = cursor.plus(interval)
}
return starts
}
fun bucketStarts(from: Instant, to: Instant, interval: Duration): List<Instant> {
require(!interval.isZero && !interval.isNegative) { "interval must be positive" }
val starts = mutableListOf<Instant>()
var cursor = from
while (cursor.isBefore(to)) {
starts.add(cursor)
cursor = cursor.plus(interval)
}
return starts
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/TimeBucketer.kt`
around lines 18 - 26, Update the public bucketStarts function to validate
interval at entry and reject zero or negative durations before entering the
while loop. Preserve the existing bucketing behavior for positive intervals and
use the project’s established argument-validation convention if available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant