diff --git a/backend/src/main/java/com/staccato/config/log/CreateMomentMetricsAspect.java b/backend/src/main/java/com/staccato/config/log/CreateMomentMetricsAspect.java new file mode 100644 index 00000000..a4257114 --- /dev/null +++ b/backend/src/main/java/com/staccato/config/log/CreateMomentMetricsAspect.java @@ -0,0 +1,58 @@ +package com.staccato.config.log; + + +import java.time.LocalDate; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; +import com.staccato.member.domain.Member; +import com.staccato.moment.service.MomentService; +import com.staccato.moment.service.dto.request.MomentRequest; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import lombok.RequiredArgsConstructor; + +@Aspect +@Component +@RequiredArgsConstructor +public class CreateMomentMetricsAspect { + + private final MeterRegistry meterRegistry; + + @Pointcut("execution(public * com.staccato.moment.service.MomentService.createMoment(..)) && args(momentRequest, member)") + public void createMomentPointcut(MomentRequest momentRequest, Member member) { + } + + @AfterReturning(pointcut = "createMomentPointcut(momentRequest, member)", returning = "result") + public void afterSuccessfulCreateMoment(MomentRequest momentRequest, Member member, Object result) { + LocalDate visitedAt = momentRequest.visitedAt().toLocalDate(); + LocalDate now = LocalDate.now(); + if (isPastDate(visitedAt, now)) { + recordCounter("past"); + return; + } + if (isFutureDate(visitedAt, now)) { + recordCounter("future"); + return; + } + recordCounter("now"); + } + + private boolean isPastDate(LocalDate visitedAt, LocalDate now) { + return visitedAt.isBefore(now); + } + + private boolean isFutureDate(LocalDate visitedAt, LocalDate now) { + return visitedAt.isAfter(now); + } + + private void recordCounter(String viewPoint) { + Counter.builder("staccato_record_viewpoint") + .tag("class", MomentService.class.getName()) + .tag("method", "createMoment") + .tag("viewPoint", viewPoint) + .description("counts different view points for Staccato Record") + .register(meterRegistry).increment(); + } +} diff --git a/backend/src/main/java/com/staccato/config/log/ReadAllCategoryMetricsAspect.java b/backend/src/main/java/com/staccato/config/log/ReadAllCategoryMetricsAspect.java new file mode 100644 index 00000000..a067e0f7 --- /dev/null +++ b/backend/src/main/java/com/staccato/config/log/ReadAllCategoryMetricsAspect.java @@ -0,0 +1,52 @@ +package com.staccato.config.log; + +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; +import com.staccato.member.domain.Member; +import com.staccato.memory.service.MemoryFilter; +import com.staccato.memory.service.MemoryService; +import com.staccato.memory.service.MemorySort; +import com.staccato.memory.service.dto.request.MemoryReadRequest; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import lombok.RequiredArgsConstructor; + +@Aspect +@Component +@RequiredArgsConstructor +public class ReadAllCategoryMetricsAspect { + + private final MeterRegistry meterRegistry; + + @Pointcut("execution(public * com.staccato.memory.service.MemoryService.readAllMemories(..)) && args(member, memoryReadRequest)") + public void createMemoryPointcut(Member member, MemoryReadRequest memoryReadRequest) { + } + + @AfterReturning(pointcut = "createMemoryPointcut(member, memoryReadRequest)", returning = "result", argNames = "member,memoryReadRequest,result") + public void afterSuccessfulReadMemory(Member member, MemoryReadRequest memoryReadRequest, Object result) { + for (MemoryFilter filter : memoryReadRequest.getFilters()) { + countFilter(filter); + } + countSort(memoryReadRequest.getSort()); + } + + private void countFilter(MemoryFilter filter) { + Counter.builder("category_filter_count") + .tag("class", MemoryService.class.getName()) + .tag("method", "readAllMemories") + .tag("filter", filter.getName()) + .description("counts for filter usage of categories") + .register(meterRegistry).increment(); + } + + private void countSort(MemorySort sort) { + Counter.builder("category_sort_count") + .tag("class", MemoryService.class.getName()) + .tag("method", "readAllMemories") + .tag("sort", sort.getName()) + .description("counts for sort usage of categories") + .register(meterRegistry).increment(); + } +} diff --git a/backend/src/main/java/com/staccato/memory/controller/CategoryController.java b/backend/src/main/java/com/staccato/memory/controller/CategoryController.java index 425adae1..601d9ae9 100644 --- a/backend/src/main/java/com/staccato/memory/controller/CategoryController.java +++ b/backend/src/main/java/com/staccato/memory/controller/CategoryController.java @@ -2,10 +2,8 @@ import java.net.URI; import java.time.LocalDate; - import jakarta.validation.Valid; import jakarta.validation.constraints.Min; - import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; @@ -18,7 +16,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; - import com.staccato.config.auth.LoginMember; import com.staccato.config.log.annotation.Trace; import com.staccato.member.domain.Member; @@ -26,7 +23,6 @@ import com.staccato.memory.service.MemoryService; import com.staccato.memory.service.dto.request.CategoryReadRequest; import com.staccato.memory.service.dto.request.CategoryRequest; -import com.staccato.memory.service.dto.request.MemoryReadRequest; import com.staccato.memory.service.dto.response.CategoryDetailResponse; import com.staccato.memory.service.dto.response.CategoryIdResponse; import com.staccato.memory.service.dto.response.CategoryNameResponses; @@ -35,7 +31,6 @@ import com.staccato.memory.service.dto.response.MemoryIdResponse; import com.staccato.memory.service.dto.response.MemoryNameResponses; import com.staccato.memory.service.dto.response.MemoryResponses; - import lombok.RequiredArgsConstructor; @Trace @@ -52,7 +47,8 @@ public ResponseEntity createCategory( @LoginMember Member member ) { MemoryIdResponse memoryIdResponse = memoryService.createMemory(CategoryDtoMapper.toMemoryRequest(categoryRequest), member); - return ResponseEntity.created(URI.create("/categories/" + memoryIdResponse.memoryId())).body(CategoryDtoMapper.toCategoryIdResponse(memoryIdResponse)); + return ResponseEntity.created(URI.create("/categories/" + memoryIdResponse.memoryId())) + .body(CategoryDtoMapper.toCategoryIdResponse(memoryIdResponse)); } @GetMapping diff --git a/backend/src/main/java/com/staccato/memory/service/MemoryFilter.java b/backend/src/main/java/com/staccato/memory/service/MemoryFilter.java index 638d7dce..0a47ed33 100644 --- a/backend/src/main/java/com/staccato/memory/service/MemoryFilter.java +++ b/backend/src/main/java/com/staccato/memory/service/MemoryFilter.java @@ -6,9 +6,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import com.staccato.memory.domain.Memory; +import lombok.Getter; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor +@Getter public enum MemoryFilter { TERM("term", memoryList -> memoryList.stream() diff --git a/backend/src/main/java/com/staccato/memory/service/MemorySort.java b/backend/src/main/java/com/staccato/memory/service/MemorySort.java index 9a5a1523..0a27c752 100644 --- a/backend/src/main/java/com/staccato/memory/service/MemorySort.java +++ b/backend/src/main/java/com/staccato/memory/service/MemorySort.java @@ -5,9 +5,11 @@ import java.util.function.Function; import java.util.stream.Stream; import com.staccato.memory.domain.Memory; +import lombok.Getter; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor +@Getter public enum MemorySort { UPDATED("UPDATED", memoryList -> memoryList.stream() diff --git a/backend/src/main/resources/error-appender.xml b/backend/src/main/resources/error-appender.xml index 01044b0e..b996ccb0 100644 --- a/backend/src/main/resources/error-appender.xml +++ b/backend/src/main/resources/error-appender.xml @@ -1,6 +1,6 @@ - ./logs/error/error-${BY_DATE}.log + ./logs/error/error.log ERROR @@ -13,7 +13,7 @@ ./logs/error/error-%d{yyyy-MM-dd}.%i.log 10MB - 15 + 365 3GB diff --git a/backend/src/main/resources/info-appender.xml b/backend/src/main/resources/info-appender.xml index 7fe9243b..6c3e1d93 100644 --- a/backend/src/main/resources/info-appender.xml +++ b/backend/src/main/resources/info-appender.xml @@ -1,6 +1,6 @@ - ./logs/info/info-${BY_DATE}.log + ./logs/info/info.log INFO @@ -13,7 +13,7 @@ ./logs/info/info-%d{yyyy-MM-dd}.%i.log 10MB - 15 + 365 3GB diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index c67155e9..af46cf00 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -1,7 +1,6 @@ - diff --git a/backend/src/main/resources/warn-appender.xml b/backend/src/main/resources/warn-appender.xml index b8556bf3..624c8a0c 100644 --- a/backend/src/main/resources/warn-appender.xml +++ b/backend/src/main/resources/warn-appender.xml @@ -1,6 +1,6 @@ - ./logs/warn/warn-${BY_DATE}.log + ./logs/warn/warn.log WARN @@ -13,7 +13,7 @@ ./logs/warn/warn-%d{yyyy-MM-dd}.%i.log 10MB - 15 + 365 3GB diff --git a/backend/src/test/java/com/staccato/config/log/CreateMomentMetricsAspectTest.java b/backend/src/test/java/com/staccato/config/log/CreateMomentMetricsAspectTest.java new file mode 100644 index 00000000..9aeeb6fd --- /dev/null +++ b/backend/src/test/java/com/staccato/config/log/CreateMomentMetricsAspectTest.java @@ -0,0 +1,109 @@ +package com.staccato.config.log; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.springframework.beans.factory.annotation.Autowired; +import com.staccato.ServiceSliceTest; +import com.staccato.fixture.Member.MemberFixture; +import com.staccato.fixture.memory.MemoryFixture; +import com.staccato.member.domain.Member; +import com.staccato.member.repository.MemberRepository; +import com.staccato.memory.domain.Memory; +import com.staccato.memory.repository.MemoryRepository; +import com.staccato.moment.service.MomentService; +import com.staccato.moment.service.dto.request.MomentRequest; +import io.micrometer.core.instrument.MeterRegistry; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +class CreateMomentMetricsAspectTest extends ServiceSliceTest { + + @Autowired + private MemoryRepository memoryRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired + private MomentService momentService; + @Autowired + private MeterRegistry meterRegistry; + + @DisplayName("기록되는 시점을 매트릭을 통해 표현 할 수 있습니다.") + @TestFactory + Stream createMomentMetricsAspect() { + Member member = saveMember(); + Memory memory = saveMemory(member); + LocalDateTime now = LocalDateTime.now(); + + return Stream.of( + dynamicTest("과거, 미래 기록 매트릭을 등록합니다.", () -> { + // given + MomentRequest pastRequest = createRequest(memory.getId(), now.minusDays(1)); + MomentRequest futureRequest = createRequest(memory.getId(), now.plusDays(1)); + + //when + momentService.createMoment(pastRequest, member); + momentService.createMoment(futureRequest, member); + + //then + assertAll( + () -> assertThat(getPastCount()).isEqualTo(1.0), + () -> assertThat(getFutureCount()).isEqualTo(1.0) + ); + }), + dynamicTest("과거 요청 → 누적: past:2, future:1", () -> { + // given + MomentRequest momentRequest = createRequest(memory.getId(), now.minusDays(1)); + + // when + momentService.createMoment(momentRequest, member); + + // then + assertAll( + () -> assertThat(getPastCount()).isEqualTo(2.0), + () -> assertThat(getFutureCount()).isEqualTo(1.0) + ); + }) + ); + } + + private Member saveMember() { + return memberRepository.save(MemberFixture.create()); + } + + private Memory saveMemory(Member member) { + Memory memory = MemoryFixture.create(); + memory.addMemoryMember(member); + return memoryRepository.save(memory); + } + + private double getFutureCount() { + return meterRegistry.get("staccato_record_viewpoint") + .tag("viewPoint", "future") + .counter().count(); + } + + private double getPastCount() { + return meterRegistry.get("staccato_record_viewpoint") + .tag("viewPoint", "past") + .counter().count(); + } + + private MomentRequest createRequest(Long memoryId, LocalDateTime localDateTime) { + return new MomentRequest( + "staccatoTitle", + "placeName", + "address", + BigDecimal.ONE, + BigDecimal.ONE, + localDateTime, + memoryId, + List.of()); + } +} diff --git a/backend/src/test/java/com/staccato/config/log/ReadAllCategoryMetricsAspectTest.java b/backend/src/test/java/com/staccato/config/log/ReadAllCategoryMetricsAspectTest.java new file mode 100644 index 00000000..f016294b --- /dev/null +++ b/backend/src/test/java/com/staccato/config/log/ReadAllCategoryMetricsAspectTest.java @@ -0,0 +1,52 @@ +package com.staccato.config.log; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import com.staccato.ServiceSliceTest; +import com.staccato.fixture.Member.MemberFixture; +import com.staccato.member.domain.Member; +import com.staccato.member.repository.MemberRepository; +import com.staccato.memory.service.MemoryService; +import com.staccato.memory.service.dto.request.MemoryReadRequest; +import io.micrometer.core.instrument.MeterRegistry; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +public class ReadAllCategoryMetricsAspectTest extends ServiceSliceTest { + + @Autowired + private MemoryService memoryService; + @Autowired + private MemberRepository memberRepository; + @Autowired + private MeterRegistry meterRegistry; + + @DisplayName("필터와 정렬 조건마다 호출 횟수를 메트릭으로 남긴다.") + @Test + void testMetricsAfterServiceExecution() { + // given + Member member = MemberFixture.create(); + memberRepository.save(member); + MemoryReadRequest memoryReadRequest = new MemoryReadRequest("term", "oldest"); + + // when + memoryService.readAllMemories(member, memoryReadRequest); + + // then + double filterCount = meterRegistry.counter("category_filter_count", + "class", MemoryService.class.getName(), + "method", "readAllMemories", + "filter", "term").count(); + double sortCount = meterRegistry.counter("category_sort_count", + "class", MemoryService.class.getName(), + "method", "readAllMemories", + "sort", "OLDEST").count(); + assertAll( + () -> assertThat(filterCount).isEqualTo(1.0), + () -> assertThat(sortCount).isEqualTo(1.0) + ); + } +} + diff --git a/backend/src/test/java/com/staccato/fixture/memory/MemoryFixture.java b/backend/src/test/java/com/staccato/fixture/memory/MemoryFixture.java index 22d6168e..184aa741 100644 --- a/backend/src/test/java/com/staccato/fixture/memory/MemoryFixture.java +++ b/backend/src/test/java/com/staccato/fixture/memory/MemoryFixture.java @@ -5,6 +5,14 @@ import com.staccato.memory.domain.Memory; public class MemoryFixture { + public static Memory create() { + return Memory.builder() + .thumbnailUrl("https://example.com/memorys/geumohrm.jpg") + .title("title") + .description("친구들과 함께한 여름 휴가 추억") + .build(); + } + public static Memory create(String title) { return Memory.builder() .thumbnailUrl("https://example.com/memorys/geumohrm.jpg")