Skip to content

Commit

Permalink
feat : HTTP 요청 건수 API 구현
Browse files Browse the repository at this point in the history
- 5시간 이내의 HTTP 요청 10분 별로 집계
- 메트릭 데이터 수집 및 저장
  • Loading branch information
JiwonKKang committed Oct 7, 2024
1 parent fa4f47b commit 85a6d2d
Show file tree
Hide file tree
Showing 30 changed files with 625 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.univ.tracedin.api.metric;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

import com.univ.tracedin.api.global.dto.Response;
import com.univ.tracedin.api.metric.dto.AppendServiceMetricsRequest;
import com.univ.tracedin.api.metric.dto.HttpRequestCountResponse;
import com.univ.tracedin.domain.metric.ServiceMetricsService;
import com.univ.tracedin.domain.project.ServiceNode;

@RestController
@RequestMapping("/api/v1/metrics")
@RequiredArgsConstructor
public class ServiceMetricsApi implements ServiceMetricsApiDocs {

private final ServiceMetricsService serviceMetricService;

@PostMapping
public void appendMetrics(@RequestBody AppendServiceMetricsRequest requests) {
serviceMetricService.appendMetrics(requests.toDomain());
}

@GetMapping("/http-request-count")
public Response<List<HttpRequestCountResponse>> getHttpRequestCount(ServiceNode serviceNode) {
List<HttpRequestCountResponse> responses =
serviceMetricService.getHttpRequestCount(serviceNode).stream()
.map(HttpRequestCountResponse::from)
.toList();
return Response.success(responses);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.univ.tracedin.api.metric;

import java.util.List;

import com.univ.tracedin.api.global.dto.Response;
import com.univ.tracedin.api.metric.dto.AppendServiceMetricsRequest;
import com.univ.tracedin.api.metric.dto.HttpRequestCountResponse;
import com.univ.tracedin.domain.project.ServiceNode;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "메트릭 API")
public interface ServiceMetricsApiDocs {

@Operation(summary = "메트릭 추가(라이브러리 전용)")
void appendMetrics(AppendServiceMetricsRequest requests);

@Operation(
summary = "5시간 이내의 10분 별로 HTTP 요청 횟수 조회",
description = "5시간 이내의 10분 별로 HTTP 요청 횟수 조회, 개발 기간에는 5시간 이내 조건 없음")
Response<List<HttpRequestCountResponse>> getHttpRequestCount(ServiceNode serviceNode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.univ.tracedin.api.metric.dto;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import com.univ.tracedin.domain.metric.Metric;
import com.univ.tracedin.domain.metric.MetricType;
import com.univ.tracedin.domain.metric.ServiceMetrics;

public record AppendServiceMetricsRequest(
String projectKey, String serviceName, List<MetricRequest> metrics) {

public record MetricRequest(
String name,
String description,
String unit,
String type,
Double value,
Long count,
Double sum,
Double min,
Double max,
Map<String, Object> attributes) {

public Metric toDomain() {
return Metric.builder()
.name(name)
.description(description)
.unit(unit)
.type(MetricType.valueOf(type))
.value(value)
.count(count)
.sum(sum)
.min(min)
.max(max)
.attributes(attributes)
.timestamp(LocalDateTime.now())
.build();
}
}

public ServiceMetrics toDomain() {
return ServiceMetrics.builder()
.projectKey(projectKey)
.serviceName(serviceName)
.metrics(metrics.stream().map(MetricRequest::toDomain).toList())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.univ.tracedin.api.metric.dto;

import java.time.LocalDateTime;

import com.univ.tracedin.domain.metric.HttpRequestCount;

public record HttpRequestCountResponse(LocalDateTime timestamp, Long httpRequestCount) {

public static HttpRequestCountResponse from(HttpRequestCount httpRequestCount) {
return new HttpRequestCountResponse(
httpRequestCount.timestamp(), httpRequestCount.httpRequestCount());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.univ.tracedin.api.span;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -28,9 +30,9 @@ public class SpanApi implements SpanApiDocs {
private final SpanService spanService;

@PostMapping
public void appendSpan(@RequestBody AppendSpanRequest request) {
public void appendSpan(@RequestBody List<AppendSpanRequest> request) {
log.info("appendSpan request: {}", request.toString());
spanService.appendSpan(request.toSpan());
spanService.appendSpan(request.stream().map(AppendSpanRequest::toSpan).toList());
}

@GetMapping("/traces")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.univ.tracedin.api.span;

import java.util.List;

import com.univ.tracedin.api.global.dto.Response;
import com.univ.tracedin.api.span.dto.AppendSpanRequest;
import com.univ.tracedin.api.span.dto.ReadSpanRequest;
Expand All @@ -15,7 +17,7 @@
public interface SpanApiDocs {

@Operation(summary = "스팬 데이터 추가 API(라이브러리 전용)", description = "라이브러리에서 수집한 스팬 데이터를 추가합니다.")
void appendSpan(AppendSpanRequest request);
void appendSpan(List<AppendSpanRequest> request);

@Operation(summary = "트레이스(트랜잭션) 조회 API", description = "프로젝트의 특정 서비스의 트레이스(트랜잭션)를 조회합니다.")
Response<SearchResult<TraceResponse>> getTraces(ReadSpanRequest request, SearchCursor cursor);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.univ.tracedin.domain.metric;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

public record HttpRequestCount(LocalDateTime timestamp, Long httpRequestCount) {

public static HttpRequestCount of(long endEpochMillis, Long httpRequestCount) {
LocalDateTime timestamp =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(endEpochMillis), ZoneId.systemDefault());
return new HttpRequestCount(timestamp, httpRequestCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.univ.tracedin.domain.metric;

import java.time.LocalDateTime;
import java.util.Map;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Metric {

private String name;
private String description;
private String unit;
private MetricType type;
private Double value;
private Long count;
private Double sum;
private Double min;
private Double max;
private Map<String, Object> attributes;
private LocalDateTime timestamp;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.univ.tracedin.domain.metric;

public enum MetricType {
LONG_GAUGE,
DOUBLE_GAUGE,
LONG_SUM,
DOUBLE_SUM,
SUMMARY,
HISTOGRAM,
EXPONENTIAL_HISTOGRAM;

public static MetricType fromValue(String value) {
for (MetricType type : MetricType.values()) {
if (type.name().equalsIgnoreCase(value)) {
return type;
}
}
throw new IllegalArgumentException("Unknown MetricType: " + value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.univ.tracedin.domain.metric;

import java.util.List;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ServiceMetrics {

private String projectKey;
private String serviceName;
List<Metric> metrics;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.univ.tracedin.domain.metric;

import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class ServiceMetricsAppender {

private final ServiceMetricsRepository serviceMetricsRepository;

public void append(ServiceMetrics metrics) {
serviceMetricsRepository.save(metrics);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.univ.tracedin.domain.metric;

import java.util.List;

import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;

import com.univ.tracedin.domain.project.ServiceNode;

@Component
@RequiredArgsConstructor
public class ServiceMetricsReader {

private final ServiceMetricsRepository serviceMetricsRepository;

public List<HttpRequestCount> readHttpRequestCount(ServiceNode serviceNode) {
return serviceMetricsRepository.getHttpRequestCount(serviceNode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.univ.tracedin.domain.metric;

import java.util.List;

import com.univ.tracedin.domain.project.ServiceNode;

public interface ServiceMetricsRepository {

void save(ServiceMetrics metrics);

List<HttpRequestCount> getHttpRequestCount(ServiceNode serviceNode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.univ.tracedin.domain.metric;

import java.util.List;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;

import com.univ.tracedin.domain.project.ServiceNode;

@Service
@RequiredArgsConstructor
public class ServiceMetricsService {

private final ServiceMetricsAppender serviceMetricAppender;
private final ServiceMetricsReader serviceMetricReader;

public void appendMetrics(ServiceMetrics metrics) {
serviceMetricAppender.append(metrics);
}

public List<HttpRequestCount> getHttpRequestCount(ServiceNode serviceNode) {
return serviceMetricReader.readHttpRequestCount(serviceNode);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.univ.tracedin.domain.span;

import java.util.List;

import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
Expand All @@ -10,7 +12,7 @@ public class SpanAppender {

private final SpanRepository spanRepository;

public void append(Span span) {
spanRepository.save(span);
public void append(List<Span> spans) {
spanRepository.save(spans);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

public interface SpanRepository {

void save(Span span);
void save(List<Span> spans);

Span findById(String id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public class SpanService {
private final SpanAppender spanAppender;
private final SpanReader spanReader;

public void appendSpan(Span span) {
spanAppender.append(span);
public void appendSpan(List<Span> spans) {
spanAppender.append(spans);
}

public SearchResult<Trace> getTraces(ServiceNode serviceNode, SearchCursor cursor) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.univ.tracedin.infra.user.cache;
package com.univ.tracedin.infra.auth.cache;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.univ.tracedin.infra.user.config;
package com.univ.tracedin.infra.auth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.univ.tracedin.infra.span.repository;
package com.univ.tracedin.infra.elasticsearch;

import java.io.IOException;

Expand Down
Loading

0 comments on commit 85a6d2d

Please sign in to comment.