feat: add trace-id and request-id header logging to tool controller

This commit is contained in:
jade
2026-07-21 13:18:38 +09:00
parent b9966bbbda
commit 580edd98a6
4 changed files with 26 additions and 10 deletions

View File

@@ -232,6 +232,10 @@ public class ExecuteService {
} }
String executeApiUrl = targetUrl + "/mcp/" + metadata.getName(); String executeApiUrl = targetUrl + "/mcp/" + metadata.getName();
Map<String, String> headers = new java.util.HashMap<>();
headers.put("trace-id", java.util.UUID.randomUUID().toString());
headers.put("request-id", context.requestId());
ObjectNode pageArguments = paginationValidator.normalize(arguments); ObjectNode pageArguments = paginationValidator.normalize(arguments);
LargeToolResponseService.Collector collector = largeResponses.newCollector(metadata.getName(), context.requestId()); LargeToolResponseService.Collector collector = largeResponses.newCollector(metadata.getName(), context.requestId());
@@ -244,7 +248,7 @@ public class ExecuteService {
JsonNode data = null; JsonNode data = null;
try { try {
data = toolInvoker.invoke(pagePayload, executeApiUrl); data = toolInvoker.invoke(pagePayload, executeApiUrl, headers);
} catch (org.springframework.web.client.RestClientResponseException e) { } catch (org.springframework.web.client.RestClientResponseException e) {
// HTTP 4xx, 5xx 에러는 연결 오류가 아니라 비즈니스 로직 오류이거나 검증 실패이므로 원본 에러를 그대로 반환 // HTTP 4xx, 5xx 에러는 연결 오류가 아니라 비즈니스 로직 오류이거나 검증 실패이므로 원본 에러를 그대로 반환
throw new ToolExecutionException(FailureType.SERVER_ERROR, "Tool Pod HTTP 에러 (" + e.getStatusCode() + "): " + e.getResponseBodyAsString()); throw new ToolExecutionException(FailureType.SERVER_ERROR, "Tool Pod HTTP 에러 (" + e.getStatusCode() + "): " + e.getResponseBodyAsString());
@@ -253,7 +257,7 @@ public class ExecuteService {
String fallbackUrl = executeApiUrl.replaceAll("http://tool-[a-zA-Z0-9-]+", "http://localhost"); String fallbackUrl = executeApiUrl.replaceAll("http://tool-[a-zA-Z0-9-]+", "http://localhost");
log.warn(" [ExecuteService] 호스트를 찾을 수 없어 localhost로 재시도합니다: {}", fallbackUrl); log.warn(" [ExecuteService] 호스트를 찾을 수 없어 localhost로 재시도합니다: {}", fallbackUrl);
try { try {
data = toolInvoker.invoke(pagePayload, fallbackUrl); data = toolInvoker.invoke(pagePayload, fallbackUrl, headers);
} catch (Exception ex) { } catch (Exception ex) {
throw new ToolExecutionException(FailureType.SERVER_ERROR, "Tool Pod 호출 실패 (localhost 재시도 포함): " + ex.getMessage()); throw new ToolExecutionException(FailureType.SERVER_ERROR, "Tool Pod 호출 실패 (localhost 재시도 포함): " + ex.getMessage());
} }

View File

@@ -40,14 +40,17 @@ public class HttpToolInvoker implements ToolInvoker {
} }
@Override @Override
public JsonNode invoke(Map<String, Object> payload, String targetUrl) { public JsonNode invoke(Map<String, Object> payload, String targetUrl, Map<String, String> headers) {
try { try {
Object httpResult = restClient.post() RestClient.RequestBodySpec requestSpec = restClient.post()
.uri(targetUrl) .uri(targetUrl)
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON);
// TODO: Use actual tenant's key
.header("X-Trace-Id", UUID.randomUUID().toString()) if (headers != null) {
.body(payload) headers.forEach(requestSpec::header);
}
Object httpResult = requestSpec.body(payload)
.retrieve() .retrieve()
.body(Object.class); .body(Object.class);

View File

@@ -22,5 +22,5 @@ import java.util.Map;
* Tool 서버 호출 transport의 최소 공통 인터페이스입니다. * Tool 서버 호출 transport의 최소 공통 인터페이스입니다.
*/ */
public interface ToolInvoker { public interface ToolInvoker {
JsonNode invoke(Map<String, Object> payload, String targetUrl); JsonNode invoke(Map<String, Object> payload, String targetUrl, Map<String, String> headers);
} }

View File

@@ -70,10 +70,13 @@ public class BusinessToolController {
public ResponseEntity<?> executeDynamicTool( public ResponseEntity<?> executeDynamicTool(
@PathVariable("name") String functionName, @PathVariable("name") String functionName,
@RequestHeader(value = "X-Request-Id", required = false) String headerRequestId, @RequestHeader(value = "X-Request-Id", required = false) String headerRequestId,
@RequestHeader(value = "trace-id", required = false) String traceId,
@RequestHeader(value = "request-id", required = false) String requestId,
@RequestBody(required = false) Map<String, Object> arguments) { @RequestBody(required = false) Map<String, Object> arguments) {
String finalRequestId = headerRequestId; String finalRequestId = headerRequestId;
log.info(" [Tool] IN - trace-id: {}, request-id: {}", traceId, requestId);
log.info("\n [Tool] 동적 툴 실행 요청 수신 (함수명): {}", functionName); log.info("\n [Tool] 동적 툴 실행 요청 수신 (함수명): {}", functionName);
if (arguments != null) { if (arguments != null) {
try { try {
@@ -198,7 +201,13 @@ public class BusinessToolController {
log.info("[Tool -> MCP Gateway] 동적 툴 실행 결과 반환: {}", methodResult); log.info("[Tool -> MCP Gateway] 동적 툴 실행 결과 반환: {}", methodResult);
} }
return ResponseEntity.ok(methodResult); log.info(" [Tool] OUT - trace-id: {}, request-id: {}", traceId, requestId);
ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.ok();
if (traceId != null) responseBuilder.header("trace-id", traceId);
if (requestId != null) responseBuilder.header("request-id", requestId);
return responseBuilder.body(methodResult);
} catch (Exception e) { } catch (Exception e) {
log.error("[Tool] 리플렉션 실행 중 예외 발생: {}", e.getMessage()); log.error("[Tool] 리플렉션 실행 중 예외 발생: {}", e.getMessage());