feat(observability): 클라이언트/서버 오류로그·스토리지 사용량 API 구현 #29 - #71
Conversation
POST /api/monitor/v11/collect/client-log 구현. 메시지는 500자로 절단, 배치 20건 초과·빈 배치는 INVALID_PAYLOAD, 본문 64KB 초과는 PAYLOAD_TOO_LARGE. User-Agent에서 브라우저/OS를 휴리스틱으로 추출해 로그 항목에 함께 저장한다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GET /api/monitor/v11/logs/client 구현. 기본 조회 범위 1시간, 최대 7일 초과 시 INVALID_TIME_RANGE. size는 최대 100으로 clamp, cursor는 손상 시 Cursor.decode에서 INVALID_CURSOR로 처리된다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GET /api/monitor/v11/logs/server 구현. service는 ServiceName 검증 실패 시 INVALID_SERVICE, status는 4xx/5xx 또는 구체 코드로 파싱하고 형식이 올바르지 않으면 INVALID_PAYLOAD. 서버 오류를 이 서비스로 보고하는 수집 경로가 문서에 없어 EmptyServerLogStoreAdapter가 항상 빈 결과를 반환한다. 각 서비스에 오류 리포팅 연동이 추가되면 (client-log와 동일한 fingerprint 그룹핑 패턴으로) Redis 기반 어댑터로 교체한다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GET /api/monitor/v11/resources 구현. LocalDiskStorageUsageAdapter가 매 요청 실시간으로 측정하므로 database.totalBytes/usageRatio는 DB 접속 권한이 없어 null, bucket은 로컬 리포트 저장 디렉터리 사용량으로 대체한다. 실시간 측정이라 METRIC_UNAVAILABLE 케이스는 발생하지 않는다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
🚫 Excluded labels (none allowed) (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 Walkthrough사용자는 클라이언트 로그와 서버 API 오류를 조회하고, 데이터베이스·버킷 스토리지 사용량을 확인할 수 있습니다. 클라이언트 로그는 안전한 크기와 배치 제한을 적용해 수집합니다. 아키텍처 변경
위험 영역
마이그레이션 및 호환성
검증 체크리스트 및 롤아웃
Walkthrough클라이언트 로그 수집·조회 API와 서버 로그 조회 API를 추가했다. 조회 시간과 상태 필터를 검증한다. 스토리지 사용량 조회 API를 추가했다. 응답 DTO와 User-Agent 파서를 정의하고 애플리케이션 테스트를 추가했다. Changes관측성 API 확장
Estimated code review effort: 4 (Complex) | ~45 minutes Suggested labels: 🚥 Pre-merge checks | ✅ 8 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (8 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 12
🤖 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/ClientLogCollectController.kt`:
- Around line 53-58: Update the clientIp function to use X-Forwarded-For only
when the request originates from a configured trusted proxy and the header is
normalized by that proxy; otherwise, always use request.remoteAddr. Reuse the
existing trusted-proxy configuration or validation mechanism rather than
trusting the header for every request.
- Around line 28-34: Move the 64KB payload enforcement out of
ClientLogCollectController’s post-deserialization check and into a request-level
mechanism that runs before `@RequestBody` processing, such as the proxy/servlet
container limit or a body-counting filter. Ensure both declared Content-Length
and chunked requests are rejected with ErrorCode.PAYLOAD_TOO_LARGE when the
request body exceeds MAX_PAYLOAD_BYTES.
In
`@systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogController.kt`:
- Around line 20-33: 컨트롤러의 입력 변환 동작을 검증하는 테스트를 추가하십시오. ClientLogController의 logs
메서드를 직접 실행해 쉼표로 구분된 다중 level이 올바르게 변환되고 cursor가 디코딩되어 use case에 전달되는지 의미 있는 검증을
작성하십시오. 또한 잘못된 LogLevel과 잘못된 cursor 입력이 각각 예상되는 오류 코드로 처리되는 테스트를 추가하십시오.
In
`@systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/ClientLogCollectRequest.kt`:
- Line 12: Update the logs property in ClientLogCollectRequest with field-level
nested validation so each ClientLogItemRequest is validated, and add an MVC test
confirming entries with empty message or pageUrl are rejected with a 4xx
response before reaching the service.
In
`@systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionService.kt`:
- Around line 12-14: Remove the Spring-specific import and `@Service` annotation
from ClientLogCollectionService so the observability-application module remains
framework-independent. Move its bean registration to the appropriate adapter or
bootstrap configuration while preserving the service’s existing use-case
behavior.
In
`@systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryService.kt`:
- Around line 13-19: Remove the Spring `Service` import and `@Service`
annotation from `ClientLogQueryService`; register this use-case implementation
in the bootstrap or adapter composition point instead, keeping the application
module free of direct Spring dependencies.
- Around line 22-25: ClientLogQueryService의 from 기본값 계산을 현재 시각 now가 아닌
resolvedTo 기준으로 변경하십시오. to가 지정되지 않은 경우에도 기존처럼 현재 시각을 기준으로 유지하고, to만 지정된 과거 조회에서는
resolvedFrom이 resolvedTo.minus(DEFAULT_LOOKBACK)이 되도록 하십시오. 해당 조회가 저장소에 [to -
1시간, to] 범위를 전달하는지 검증하는 테스트를 추가하십시오.
In
`@systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.kt`:
- Around line 40-47: Update ServerLogQueryService.parseStatus to accept status
classes only when the leading digit is 1 through 5, and exact status codes only
within 100..599; reject 0xx, 6xx, 99, 600, and -1 with
MonitorDomainException(ErrorCode.INVALID_PAYLOAD). Add corresponding
out-of-range rejection tests in ServerLogQueryServiceTest at lines 36-60.
- Around line 14-16: Remove the org.springframework.stereotype.Service import
and `@Service` annotation from ServerLogQueryService, keeping it as a plain Kotlin
class. Register ServerLogQueryService as a bean through the
observability-bootstrap configuration instead, so the observability-application
module no longer depends directly on Spring.
In
`@systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryService.kt`:
- Around line 8-14: StorageUsageQueryService에서
org.springframework.stereotype.Service import와 `@Service` 애노테이션을 제거해 애플리케이션 계층의
Spring 의존성을 없애세요. Spring bean 등록은 adapter 또는 bootstrap 구성으로 이동하고,
StorageUsageQueryService는 현재처럼 GetStorageUsageUseCase와 StorageUsagePort만 의존하도록
유지하세요.
In
`@systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionServiceTest.kt`:
- Around line 43-59: Update rejectsEmptyOrOversizedBatch and
rateLimitExceededThrowsTooManyRequests to capture the thrown
MonitorDomainException and assert its ErrorCode, using INVALID_PAYLOAD for empty
or oversized batches and TOO_MANY_REQUESTS for the rate-limit case. Keep the
existing exception-type assertions while adding these scenario-specific code
checks.
In
`@systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryServiceTest.kt`:
- Around line 22-26: Update defaultsToOneHourLookbackAndClampsSize and
FakeClientLogStorePort so list captures its from, to, and size arguments instead
of only returning totalCount. Using the fixed clock, assert from is exactly one
hour before the current time, to equals the current time, and size equals 100;
retain deterministic inputs and meaningful argument-based assertions.
🪄 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: d52e50b5-2611-44d5-925b-0f2a5ad227a6
📒 Files selected for processing (30)
systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogCollectController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ResourceController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ServerLogController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/ClientLogCollectRequest.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogAcceptResponse.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogPageResponse.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServerLogPageResponse.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/StorageUsageResponse.ktsystems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/log/EmptyServerLogStoreAdapter.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetClientLogsUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServerLogsUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetStorageUsageUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/RecordClientLogUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ClientLogAcceptResult.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/StorageUsageResult.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ServerLogStorePort.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionServiceTest.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryServiceTest.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryServiceTest.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryServiceTest.ktsystems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/UserAgentParser.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/UserAgentParserTest.kt
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{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-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetStorageUsageUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ClientLogAcceptResult.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryServiceTest.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogAcceptResponse.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetClientLogsUseCase.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServerLogsUseCase.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryServiceTest.ktsystems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/UserAgentParser.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/StorageUsageResult.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/UserAgentParserTest.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogCollectController.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/RecordClientLogUseCase.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ResourceController.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ServerLogStorePort.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogPageResponse.ktsystems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/log/EmptyServerLogStoreAdapter.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionService.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ServerLogController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServerLogPageResponse.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/StorageUsageResponse.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryService.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/ClientLogCollectRequest.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/GetStorageUsageUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ClientLogAcceptResult.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetClientLogsUseCase.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServerLogsUseCase.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/StorageUsageResult.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/RecordClientLogUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ServerLogStorePort.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryService.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
#123or a full tracker key like PROJ-123
Files:
systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetStorageUsageUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ClientLogAcceptResult.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryServiceTest.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogAcceptResponse.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetClientLogsUseCase.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServerLogsUseCase.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryServiceTest.ktsystems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/UserAgentParser.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/StorageUsageResult.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/UserAgentParserTest.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogCollectController.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/RecordClientLogUseCase.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ResourceController.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ServerLogStorePort.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogPageResponse.ktsystems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/log/EmptyServerLogStoreAdapter.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionService.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ServerLogController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServerLogPageResponse.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/StorageUsageResponse.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryService.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/ClientLogCollectRequest.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-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetStorageUsageUseCase.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ClientLogAcceptResult.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryServiceTest.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogAcceptResponse.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetClientLogsUseCase.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServerLogsUseCase.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryServiceTest.ktsystems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/UserAgentParser.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogQueryService.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/StorageUsageResult.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/UserAgentParserTest.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ClientLogCollectController.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/RecordClientLogUseCase.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ResourceController.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ServerLogStorePort.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogPageResponse.ktsystems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/log/EmptyServerLogStoreAdapter.ktsystems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionServiceTest.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionService.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ServerLogController.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServerLogPageResponse.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/StorageUsageResponse.ktsystems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryService.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.ktsystems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/request/ClientLogCollectRequest.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/TestMain.ktsystems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/UserAgentParser.ktsystems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/UserAgentParserTest.kt
🪛 detekt (1.23.8)
systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.kt
[warning] 43-43: This condition is too complex (4). Defined complexity threshold for conditions is set to '4'
(detekt.complexity.ComplexCondition)
🔇 Additional comments (20)
systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/RecordClientLogUseCase.kt (1)
1-19: LGTM!systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/ClientLogAcceptResult.kt (1)
1-3: LGTM!systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogAcceptResponse.kt (1)
1-3: LGTM!systems/observability/observability-domain/src/main/kotlin/hs/kr/entrydsm/observability/domain/service/UserAgentParser.kt (1)
1-32: LGTM!systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/observability/domain/UserAgentParserTest.kt (1)
1-30: LGTM!systems/observability/observability-domain/src/test/kotlin/hs/kr/entrydsm/TestMain.kt (1)
13-13: LGTM!systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/common/ResponseMapper.kt (1)
5-8: LGTM!Also applies to: 19-37, 54-56, 140-196
systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetClientLogsUseCase.kt (1)
8-11: LGTM!systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ClientLogPageResponse.kt (1)
7-27: LGTM!systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetStorageUsageUseCase.kt (1)
5-7: LGTM!systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/result/StorageUsageResult.kt (1)
5-22: LGTM!systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ResourceController.kt (1)
10-16: LGTM!systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/StorageUsageResponse.kt (1)
5-22: LGTM!systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryServiceTest.kt (1)
9-42: LGTM!systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/TestMain.kt (1)
12-15: LGTM!systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/out/ServerLogStorePort.kt (1)
7-42: LGTM!systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/port/in/GetServerLogsUseCase.kt (1)
8-16: LGTM!systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/controller/ServerLogController.kt (1)
16-36: LGTM!systems/observability/observability-adapter-in/src/main/kotlin/hs/kr/entrydsm/observability/adapterin/web/dto/response/ServerLogPageResponse.kt (1)
6-25: LGTM!systems/observability/observability-adapter-out/src/main/kotlin/hs/kr/entrydsm/observability/adapterout/log/EmptyServerLogStoreAdapter.kt (1)
11-24: LGTM!
| @Valid @RequestBody request: ClientLogCollectRequest, | ||
| @RequestHeader(value = "User-Agent", required = false) userAgent: String?, | ||
| httpRequest: HttpServletRequest, | ||
| ): ResponseEntity<ApiResponse<ClientLogAcceptResponse>> { | ||
| if (httpRequest.contentLengthLong > MAX_PAYLOAD_BYTES) { | ||
| throw MonitorDomainException(ErrorCode.PAYLOAD_TOO_LARGE) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
64KB 제한을 본문 역직렬화 전에 적용해야 합니다.
컨트롤러 메서드가 실행될 때는 @RequestBody가 이미 역직렬화되었습니다. 따라서 큰 본문은 제한 확인 전에 서버 메모리와 CPU를 사용합니다. 또한 청크 전송 요청은 contentLengthLong == -1이므로 현재 검사를 우회합니다. 프록시 또는 서블릿 컨테이너의 요청 크기 제한, 또는 본문 읽기 전 크기를 세는 필터를 사용하십시오.
🤖 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/ClientLogCollectController.kt`
around lines 28 - 34, Move the 64KB payload enforcement out of
ClientLogCollectController’s post-deserialization check and into a request-level
mechanism that runs before `@RequestBody` processing, such as the proxy/servlet
container limit or a body-counting filter. Ensure both declared Content-Length
and chunked requests are rejected with ErrorCode.PAYLOAD_TOO_LARGE when the
request body exceeds MAX_PAYLOAD_BYTES.
| private fun clientIp(request: HttpServletRequest): String = | ||
| request.getHeader("X-Forwarded-For") | ||
| ?.substringBefore(",") | ||
| ?.trim() | ||
| ?.takeIf { it.isNotBlank() } | ||
| ?: request.remoteAddr |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
X-Forwarded-For는 신뢰된 프록시에서만 사용해야 합니다.
외부 클라이언트가 이 헤더를 임의 값으로 설정할 수 있습니다. 그러면 요청마다 다른 clientIp 레이트 리밋 키를 만들어 IP 기반 제한을 우회할 수 있습니다. 신뢰된 프록시에서 정규화한 전달 헤더만 사용하십시오. 그 외 연결에서는 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/ClientLogCollectController.kt`
around lines 53 - 58, Update the clientIp function to use X-Forwarded-For only
when the request originates from a configured trusted proxy and the header is
normalized by that proxy; otherwise, always use request.remoteAddr. Reuse the
existing trusted-proxy configuration or validation mechanism rather than
trusting the header for every request.
| @GetMapping("/api/monitor/v11/logs/client") | ||
| fun logs( | ||
| @RequestParam(required = false) level: String?, | ||
| @RequestParam(required = false) from: Instant?, | ||
| @RequestParam(required = false) to: Instant?, | ||
| @RequestParam(defaultValue = "20") size: Int, | ||
| @RequestParam(required = false) cursor: String?, | ||
| ): ApiResponse<ClientLogPageResponse> { | ||
| val levels = level?.split(",")?.map { | ||
| runCatching { LogLevel.valueOf(it.trim()) }.getOrElse { throw MonitorDomainException(ErrorCode.INVALID_PAYLOAD) } | ||
| }?.toSet() | ||
| val decodedCursor = cursor?.let { Cursor.decode(it) } | ||
| val page = getClientLogsUseCase.getLogs(levels, from, to, size, decodedCursor) | ||
| return ApiResponse(data = page.toResponse()) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
컨트롤러 입력 변환을 검증하는 테스트를 추가하십시오.
제공된 ClientLogQueryServiceTest는 이 컨트롤러를 실행하지 않습니다. 따라서 level 분할, 잘못된 LogLevel 처리, cursor 디코딩을 검증하지 않습니다.
유효한 다중 level과 cursor 전달을 검증하십시오. 잘못된 level과 cursor가 각각 예상 오류 코드로 처리되는 테스트를 추가하십시오.
As per coding guidelines, Kotlin production logic changes require corresponding test updates in the same subsystem. As per path instructions, behavior-changing code must have corresponding unit or integration tests with meaningful assertions.
🤖 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/ClientLogController.kt`
around lines 20 - 33, 컨트롤러의 입력 변환 동작을 검증하는 테스트를 추가하십시오. ClientLogController의
logs 메서드를 직접 실행해 쉼표로 구분된 다중 level이 올바르게 변환되고 cursor가 디코딩되어 use case에 전달되는지 의미 있는
검증을 작성하십시오. 또한 잘못된 LogLevel과 잘못된 cursor 입력이 각각 예상되는 오류 코드로 처리되는 테스트를 추가하십시오.
Sources: Coding guidelines, Path instructions
|
|
||
| data class ClientLogCollectRequest( | ||
| @field:NotBlank val sessionId: String, | ||
| @field:NotEmpty val logs: List<ClientLogItemRequest>, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
logs 항목 검증을 전파해야 합니다.
현재 @Valid는 루트 ClientLogCollectRequest만 검증합니다. logs에 @field:Valid가 없으므로 빈 message 또는 빈 pageUrl을 포함한 항목이 서비스까지 전달됩니다. 중첩 항목 검증을 추가하고, 빈 항목이 4xx로 거부되는 MVC 테스트를 추가하십시오.
수정 예시
+import jakarta.validation.Valid
import jakarta.validation.constraints.NotBlank
...
- `@field`:NotEmpty val logs: List<ClientLogItemRequest>,
+ `@field`:NotEmpty
+ `@field`:Valid
+ val logs: List<ClientLogItemRequest>,🤖 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/request/ClientLogCollectRequest.kt`
at line 12, Update the logs property in ClientLogCollectRequest with field-level
nested validation so each ClientLogItemRequest is validated, and add an MVC test
confirming entries with empty message or pageUrl are rejected with a 4xx
response before reaching the service.
| import org.springframework.stereotype.Service | ||
|
|
||
| @Service |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
애플리케이션 계층에서 Spring 의존성을 제거해야 합니다.
observability-application 모듈이 org.springframework.stereotype.Service와 @Service에 직접 의존합니다. 이 클래스는 순수한 유스케이스 구현으로 유지하십시오. Spring bean 등록은 adapter 또는 bootstrap 구성으로 이동하십시오.
As per coding guidelines: "For files under *-application modules, flag direct dependency on infrastructure-specific framework classes unless justified." As per path instructions: "Respect module boundaries (domain/application/adapter/bootstrap layering)."
🤖 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/main/kotlin/hs/kr/entrydsm/observability/application/ClientLogCollectionService.kt`
around lines 12 - 14, Remove the Spring-specific import and `@Service` annotation
from ClientLogCollectionService so the observability-application module remains
framework-independent. Move its bean registration to the appropriate adapter or
bootstrap configuration while preserving the service’s existing use-case
behavior.
Sources: Coding guidelines, Path instructions
| import org.springframework.stereotype.Service | ||
|
|
||
| @Service |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 'ServerLogQueryService|`@Service`|`@Bean`|ComponentScan' systems/observabilityRepository: EntryDSM/entrydsm-platform
Length of output: 13049
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- application module build files ---'
find systems/observability -maxdepth 3 -type f \( -name 'build.gradle*' -o -name 'pom.xml' -o -name 'settings.gradle*' \) -print
printf '%s\n' '--- ServerLogQueryService and related registrations ---'
rg -n -C 5 'ServerLogQueryService|GetServerLogsUseCase|`@Service`|`@Bean`|ComponentScan' systems/observability
printf '%s\n' '--- Spring dependencies in observability modules ---'
rg -n -C 3 'spring|framework' systems/observability --glob 'build.gradle*' --glob 'pom.xml'Repository: EntryDSM/entrydsm-platform
Length of output: 27894
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- Gradle and Maven files ---'
git ls-files | grep -E '(^|/)(build\.gradle(\.kts)?|settings\.gradle(\.kts)?|pom\.xml)$' || true
printf '%s\n' '--- observability module files ---'
git ls-files systems/observability | grep -E '(build\.gradle|settings\.gradle|pom\.xml|Application|Config|Bootstrap|Main)' || true
printf '%s\n' '--- component scanning and bootstrap registrations ---'
rg -n -C 5 'ComponentScan|SpringBootApplication|`@Import`|ServerLogQueryService|GetServerLogsUseCase|observability.application' systems/observability --glob '*.kt' --glob '*.java'
printf '%s\n' '--- module dependency declarations ---'
rg -n -C 5 'observability-application|spring-boot-starter|spring-context|spring-framework|implementation|api' . --glob 'build.gradle*' --glob 'pom.xml' --glob '!build/**' --glob '!**/node_modules/**'Repository: EntryDSM/entrydsm-platform
Length of output: 50383
observability-application 모듈에서 Spring 의존성을 제거하십시오.
ServerLogQueryService가 org.springframework.stereotype.Service와 @Service를 직접 사용합니다. 서비스를 일반 Kotlin 클래스로 유지하고, 빈 등록을 observability-bootstrap의 구성으로 이동하십시오.
🤖 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/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.kt`
around lines 14 - 16, Remove the org.springframework.stereotype.Service import
and `@Service` annotation from ServerLogQueryService, keeping it as a plain Kotlin
class. Register ServerLogQueryService as a bean through the
observability-bootstrap configuration instead, so the observability-application
module no longer depends directly on Spring.
Source: Coding guidelines
| private fun parseStatus(status: String?): StatusFilter? { | ||
| if (status == null) return null | ||
| val trimmed = status.trim() | ||
| if (trimmed.length == 3 && trimmed[1] == 'x' && trimmed[2] == 'x' && trimmed[0].isDigit()) { | ||
| return StatusFilter.StatusClass(trimmed[0]) | ||
| } | ||
| return trimmed.toIntOrNull()?.let { StatusFilter.Exact(it) } | ||
| ?: throw MonitorDomainException(ErrorCode.INVALID_PAYLOAD) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
HTTP 상태 값의 유효 범위를 검증하십시오.
현재 0xx, 6xx, 99, 600, -1 같은 값을 허용합니다. 이 값은 유효한 HTTP 상태 필터가 아닙니다. 상태 클래스는 1xx부터 5xx까지만 허용하고, 정확한 상태 코드는 100..599로 제한하십시오. 0xx, 6xx, 범위 밖 정수의 거부 테스트도 추가하십시오.
systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.kt#L40-L47: 상태 클래스와 정확한 상태 코드의 허용 범위를 검사하십시오.systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryServiceTest.kt#L36-L60: 범위를 벗어난 상태 입력이MonitorDomainException을 발생시키는 테스트를 추가하십시오.
수정 예시
private fun parseStatus(status: String?): StatusFilter? {
- if (status == null) return null
- val trimmed = status.trim()
- if (trimmed.length == 3 && trimmed[1] == 'x' && trimmed[2] == 'x' && trimmed[0].isDigit()) {
- return StatusFilter.StatusClass(trimmed[0])
- }
- return trimmed.toIntOrNull()?.let { StatusFilter.Exact(it) }
- ?: throw MonitorDomainException(ErrorCode.INVALID_PAYLOAD)
+ val trimmed = status?.trim() ?: return null
+ val code = trimmed.toIntOrNull()
+ return when {
+ trimmed.length == 3 && trimmed.substring(1) == "xx" && trimmed[0] in '1'..'5' ->
+ StatusFilter.StatusClass(trimmed[0])
+ code != null && code in 100..599 ->
+ StatusFilter.Exact(code)
+ else -> throw MonitorDomainException(ErrorCode.INVALID_PAYLOAD)
+ }
}📝 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.
| private fun parseStatus(status: String?): StatusFilter? { | |
| if (status == null) return null | |
| val trimmed = status.trim() | |
| if (trimmed.length == 3 && trimmed[1] == 'x' && trimmed[2] == 'x' && trimmed[0].isDigit()) { | |
| return StatusFilter.StatusClass(trimmed[0]) | |
| } | |
| return trimmed.toIntOrNull()?.let { StatusFilter.Exact(it) } | |
| ?: throw MonitorDomainException(ErrorCode.INVALID_PAYLOAD) | |
| private fun parseStatus(status: String?): StatusFilter? { | |
| val trimmed = status?.trim() ?: return null | |
| val code = trimmed.toIntOrNull() | |
| return when { | |
| trimmed.length == 3 && trimmed.substring(1) == "xx" && trimmed[0] in '1'..'5' -> | |
| StatusFilter.StatusClass(trimmed[0]) | |
| code != null && code in 100..599 -> | |
| StatusFilter.Exact(code) | |
| else -> throw MonitorDomainException(ErrorCode.INVALID_PAYLOAD) | |
| } | |
| } |
🧰 Tools
🪛 detekt (1.23.8)
[warning] 43-43: This condition is too complex (4). Defined complexity threshold for conditions is set to '4'
(detekt.complexity.ComplexCondition)
📍 Affects 2 files
systems/observability/observability-application/src/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.kt#L40-L47(this comment)systems/observability/observability-application/src/test/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryServiceTest.kt#L36-L60
🤖 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/main/kotlin/hs/kr/entrydsm/observability/application/ServerLogQueryService.kt`
around lines 40 - 47, Update ServerLogQueryService.parseStatus to accept status
classes only when the leading digit is 1 through 5, and exact status codes only
within 100..599; reject 0xx, 6xx, 99, 600, and -1 with
MonitorDomainException(ErrorCode.INVALID_PAYLOAD). Add corresponding
out-of-range rejection tests in ServerLogQueryServiceTest at lines 36-60.
Source: Linters/SAST tools
| import org.springframework.stereotype.Service | ||
|
|
||
| /** ponytail: 실시간으로 측정하므로 서버 자체 캐시는 두지 않는다. cacheTtlSeconds는 클라이언트에 권장하는 캐시 기간이다. */ | ||
| @Service | ||
| class StorageUsageQueryService( | ||
| private val storageUsagePort: StorageUsagePort, | ||
| ) : GetStorageUsageUseCase { |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
애플리케이션 계층에서 Spring 의존성을 제거하세요.
StorageUsageQueryService가 org.springframework.stereotype.Service와 @Service에 직접 의존합니다. 이 의존성은 애플리케이션 계층을 Spring 구성에 결합합니다. Spring bean 등록을 adapter 또는 bootstrap 구성으로 이동하고, 이 클래스는 입력 포트와 출력 포트에만 의존하게 하세요.
As per coding guidelines, "For files under *-application modules, flag direct dependency on infrastructure-specific framework classes unless justified".
🤖 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/main/kotlin/hs/kr/entrydsm/observability/application/StorageUsageQueryService.kt`
around lines 8 - 14, StorageUsageQueryService에서
org.springframework.stereotype.Service import와 `@Service` 애노테이션을 제거해 애플리케이션 계층의
Spring 의존성을 없애세요. Spring bean 등록은 adapter 또는 bootstrap 구성으로 이동하고,
StorageUsageQueryService는 현재처럼 GetStorageUsageUseCase와 StorageUsagePort만 의존하도록
유지하세요.
Source: Coding guidelines
| fun rejectsEmptyOrOversizedBatch() { | ||
| val service = ClientLogCollectionService(store, FakeRateLimitPort(true)) | ||
|
|
||
| assertThrows(MonitorDomainException::class.java) { service.record("sess_1", emptyList(), null, "127.0.0.1") } | ||
| assertThrows(MonitorDomainException::class.java) { | ||
| service.record("sess_1", List(21) { item() }, null, "127.0.0.1") | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| fun rateLimitExceededThrowsTooManyRequests() { | ||
| val service = ClientLogCollectionService(store, FakeRateLimitPort(false)) | ||
|
|
||
| assertThrows(MonitorDomainException::class.java) { | ||
| service.record("sess_1", listOf(item()), null, "127.0.0.1") | ||
| } | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
예외 코드도 검증해야 합니다.
현재 테스트는 MonitorDomainException 타입만 검증합니다. INVALID_PAYLOAD와 TOO_MANY_REQUESTS가 아닌 오류 코드가 발생해도 테스트가 통과합니다. 예외를 변수로 받은 뒤 각 시나리오의 ErrorCode를 단언하십시오.
As per path instructions: "Ask for deterministic tests and meaningful assertions, not only happy-path checks."
🤖 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/ClientLogCollectionServiceTest.kt`
around lines 43 - 59, Update rejectsEmptyOrOversizedBatch and
rateLimitExceededThrowsTooManyRequests to capture the thrown
MonitorDomainException and assert its ErrorCode, using INVALID_PAYLOAD for empty
or oversized batches and TOO_MANY_REQUESTS for the rate-limit case. Keep the
existing exception-type assertions while adding these scenario-specific code
checks.
Source: Path instructions
| fun defaultsToOneHourLookbackAndClampsSize() { | ||
| val result = service.getLogs(null, null, null, 500, null) | ||
|
|
||
| assertEquals(1, result.totalCount) | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
저장소 호출 인자를 검증하십시오.
FakeClientLogStorePort.list는 모든 인자를 무시하고 항상 totalCount = 1을 반환합니다. 따라서 Line 25의 검증은 기본 1시간 범위와 size = 100 제한을 확인하지 않습니다.
Fake에서 from, to, size를 저장하십시오. 고정 clock 기준으로 from이 1시간 전이고 to가 현재 시각이며 size가 100인지 단언하십시오.
As per path instructions, tests must use deterministic inputs and meaningful assertions.
🤖 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/ClientLogQueryServiceTest.kt`
around lines 22 - 26, Update defaultsToOneHourLookbackAndClampsSize and
FakeClientLogStorePort so list captures its from, to, and size arguments instead
of only returning totalCount. Using the fixed clock, assert from is exactly one
hour before the current time, to equals the current time, and size equals 100;
retain deterministic inputs and meaningful argument-based assertions.
Source: Path instructions
…observability-03-logs-resources
- 수집 엔드포인트 본문 크기를 역직렬화 전에 필터에서 제한하고 길이를 알 수 없는 요청도 거부한다 - 클라이언트 로그 수집도 ClientIpResolver로 IP를 판정한다 - logs 항목까지 검증이 전파되도록 @Valid를 중첩 타입에 붙인다 - to만 지정한 과거 조회가 거부되지 않도록 기본 from을 to 기준으로 계산한다 - 상태 필터를 1xx~5xx와 100..599로 제한한다 - 남은 유스케이스의 @service를 제거하고 빈 등록을 UseCaseConfig로 옮긴다 - 예외 코드·저장소 전달 인자·거부 시 미기록을 단언하도록 테스트를 보강한다 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…observability-03-logs-resources
…observability-03-logs-resources
Summary
Related Issue
Implementation
Testing
bazel build/test //systems/observability/...통과Checklist