在控制器建议中获取控制器名称和 java 服务路径

Posted

技术标签:

【中文标题】在控制器建议中获取控制器名称和 java 服务路径【英文标题】:Get controller name and java service path in controller advice 【发布时间】:2020-05-06 13:19:08 【问题描述】:

大家好,我已经在我的 Spring Boot 应用程序中创建了一个全局异常处理程序,并将发生在 AWS cloudwatch 中的异常写入下面的代码工作正常我能够在 cloudwatch 中编写异常,但挑战是我无法获得 Restcontroller 名称以及发生特定异常的服务路径。

示例 java 服务

@GetMapping(value = "DynamoDb/deleteTable")
public String deleteTable(@RequestParam String TableName) throws InterruptedException 
     Table table = dynamoDB.getTable(TableName);
        try 
            table.delete();
            table.waitForDelete();
         catch (Exception e) 
            throw e;
        
        return "Success";
    

当发生异常时,它会将控制权转移到 controlleradvice 全局异常处理程序

这是我的代码

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsRequest;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsResponse;
import software.amazon.awssdk.services.cloudwatchlogs.model.InputLogEvent;
import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsRequest;

import java.util.Arrays;

@ControllerAdvice
public class ExceptionControllerAdvice 
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex) 
        ErrorResponse error = new ErrorResponse();
        error.setErrorCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
        error.setMessage(ex.getMessage());
        error.setController(ex.getMessage());
        error.setService(ex.getMessage());
        error.setTimestamp(System.currentTimeMillis());
        PutLogEvents(error);
        return new ResponseEntity<ErrorResponse>(error, HttpStatus.OK);
    

    public static void PutLogEvents(ErrorResponse Er)
    
        String regionId = "us-east-1";
        String logGroupName = "xxxxxxx";
        String logStreamName = "xxxxxxx";

        CloudWatchLogsClient logsClient = CloudWatchLogsClient.builder().region(Region.of(regionId)).build();

        // A sequence token is required to put a log event in an existing stream.
        // Look up the stream to find its sequence token.
        String sequenceToken = getNextSequenceToken(logsClient, logGroupName, logStreamName);

        // Build a JSON log using the EmbeddedMetricFormat.

        String message = "[" +
                "  \"Timestamp\": " + Er.getTimestamp()  + "," +
                "  \"ErrorCode\": " + Er.getErrorCode()  + "," +
                "  \"ControllerName\": " + Er.getErrorCode()  + "," +
                "  \"ServiceName\": " + Er.getErrorCode()  + "," +
                "  \"ErrorMsg\": " + Er.getErrorCode()   + "" +
                "]";
        InputLogEvent inputLogEvent = InputLogEvent.builder()
                .message(message)
                .timestamp(Er.getTimestamp())
                .build();

        // Specify the request parameters.
        PutLogEventsRequest putLogEventsRequest = PutLogEventsRequest.builder()
                .logEvents(Arrays.asList(inputLogEvent))
                .logGroupName(logGroupName)
                .logStreamName(logStreamName)
                // Sequence token is required so that the log can be written to the
                // latest location in the stream.
                .sequenceToken(sequenceToken)
                .build();

        logsClient.putLogEvents(putLogEventsRequest);
    

    private static String getNextSequenceToken(CloudWatchLogsClient logsClient, String logGroupName, String logStreamName) 
        DescribeLogStreamsRequest logStreamRequest = DescribeLogStreamsRequest.builder()
                .logGroupName(logGroupName)
                .logStreamNamePrefix(logStreamName)
                .build();

        DescribeLogStreamsResponse describeLogStreamsResponse = logsClient.describeLogStreams(logStreamRequest);

        // Assume that a single stream is returned since a specific stream name was
        // specified in the previous request.
        return describeLogStreamsResponse.logStreams().get(0).uploadSequenceToken();
    


Errorresponse.class

public class ErrorResponse 
    private int errorCode;
    private String message;
    private String Controller;
    private String Service;
    private String ProjectName;
    private long Timestamp;

    public ErrorResponse(int errorCode, String message, String controller, String service, String projectName, long timestamp) 
        this.errorCode = errorCode;
        this.message = message;
        Controller = controller;
        Service = service;
        ProjectName = projectName;
        Timestamp = timestamp;
    

    public ErrorResponse() 

    

    @Override
    public String toString() 
        return "ErrorResponse" +
                "errorCode=" + errorCode +
                ", message='" + message + '\'' +
                ", Controller='" + Controller + '\'' +
                ", Service='" + Service + '\'' +
                ", ProjectName='" + ProjectName + '\'' +
                ", Timestamp=" + Timestamp +
                '';
    

    public int getErrorCode() 
        return errorCode;
    

    public void setErrorCode(int errorCode) 
        this.errorCode = errorCode;
    

    public String getMessage() 
        return message;
    

    public void setMessage(String message) 
        this.message = message;
    

    public String getController() 
        return Controller;
    

    public void setController(String controller) 
        Controller = controller;
    

    public String getService() 
        return Service;
    

    public void setService(String service) 
        Service = service;
    

    public String getProjectName() 
        return ProjectName;
    

    public void setProjectName(String projectName) 
        ProjectName = projectName;
    

    public long getTimestamp() 
        return Timestamp;
    

    public void setTimestamp(long timestamp) 
        Timestamp = timestamp;
    



谁能帮助我如何在全局异常处理程序中获取 Restcontroller 名称和服务路径?

【问题讨论】:

您需要对我的答案发表评论,而不是尝试对其进行编辑。 添加HandlerMethod 类型的方法参数以获取方法名称和封闭控制器。 @ExceptionHandler 注解的方法可以使用多种不同类型的参数。 通过使用代码 Class controllerClass = handlerMethod.getMethod().getDeclaringClass(); 我可以获得控制器名称 知道如何使用处理程序方法获取 java 服务路径或方法名称 【参考方案1】:

大家好,感谢大家使用下面的代码,我能够得到客户建议的结果。希望这可以帮助一些人。谢谢

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.HandlerMethod;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsRequest;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsResponse;
import software.amazon.awssdk.services.cloudwatchlogs.model.InputLogEvent;
import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsRequest;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;

@ControllerAdvice
public class ExceptionControllerAdvice 

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");

    @Value("$application.name")
    private String applicationName;

    @Value("$aws.logGroupName")
    private String logGroupName;

    @Value("$aws.logStreamName")
    private String logStreamName;


    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex, HandlerMethod handlerMethod, HttpServletRequest request) throws JsonProcessingException 
        Class ControllerName = handlerMethod.getMethod().getDeclaringClass();
        String MethodName = handlerMethod.getMethod().getName();
        ErrorResponse error = new ErrorResponse();

        error.setErrorCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
        error.setErrorMessage(ex.getMessage());
        error.setControllerName(ControllerName.toString());
        error.setServiceName(MethodName.toString());
        error.setTimeStamp(sdf.format(System.currentTimeMillis()));
        error.setProjectName(applicationName);
        error.setServicePath(request.getRequestURL().toString());
        PutLogEvents(error);

        return new ResponseEntity<ErrorResponse>(error, HttpStatus.OK);
    

    public void PutLogEvents(ErrorResponse Er) throws JsonProcessingException 
        String regionId = "xxxxx";
        String logGroupName = "xxxxx";
        String logStreamName = "xxxxx";

        CloudWatchLogsClient logsClient = CloudWatchLogsClient.builder().region(Region.of(regionId)).build();
        // A sequence token is required to put a log event in an existing stream.
        // Look up the stream to find its sequence token.
        String sequenceToken = getNextSequenceToken(logsClient, logGroupName, logStreamName);

        // Build a JSON log using the EmbeddedMetricFormat.

        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
        String json = ow.writeValueAsString(Er);

        String message =json;
        InputLogEvent inputLogEvent = InputLogEvent.builder()
                .message(message)
                .timestamp(System.currentTimeMillis())
                .build();

        // Specify the request parameters.
        PutLogEventsRequest putLogEventsRequest = PutLogEventsRequest.builder()
                .logEvents(Arrays.asList(inputLogEvent))
                .logGroupName(logGroupName)
                .logStreamName(logStreamName)
                // Sequence token is required so that the log can be written to the
                // latest location in the stream.
                .sequenceToken(sequenceToken)
                .build();

        logsClient.putLogEvents(putLogEventsRequest);
    

    private static String getNextSequenceToken(CloudWatchLogsClient logsClient, String logGroupName, String logStreamName) 
        DescribeLogStreamsRequest logStreamRequest = DescribeLogStreamsRequest.builder()
                .logGroupName(logGroupName)
                .logStreamNamePrefix(logStreamName)
                .build();

        DescribeLogStreamsResponse describeLogStreamsResponse = logsClient.describeLogStreams(logStreamRequest);

        // Assume that a single stream is returned since a specific stream name was
        // specified in the previous request.
        return describeLogStreamsResponse.logStreams().get(0).uploadSequenceToken();
    


结果应该是这样的


    "errorMessage": "Table already exists: ProductFgh (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ResourceInUseException; Request ID: 6S14VS0E6ESUMG55DL937IC42JVV4KQNSO5AEMVJF66Q9ASUAAJG)",
    "timeStamp": "2020-Jan-22 11:53:58",
    "errorCode": 500,
    "projectName": "DynamoDB",
    "servicePath": "http://localhost:8090/DynamoDb/createTable",
    "controllerName": "class com.example.DynamoDB.DynamoDBController",
    "serviceName": "createExampleTable"


到目前为止,我已经通过上面的代码实现了这一点,如果有更好的方法可以告诉我。谢谢大家

【讨论】:

【参考方案2】:

您可以通过以下方式获取引发异常的类名:

ex.getStackTrace()[0].getClassName();

【讨论】:

我得到这样的结果 com.amazonaws.http.AmazonHttpClient$RequestExecutor

以上是关于在控制器建议中获取控制器名称和 java 服务路径的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PHP Laravel Web 服务中获取照片路径

jmeter 怎么测试https

是否可以从布局中获取视图名称?

使用Route属性在Controller方法中获取控制器和动作名称

获取 ASP.NET-Core 2.2 控制器中的控制器名称和方法名称

如何在OnActionExecuting中获取控制器和操作名称?