簡體   English   中英

如何在 Spring Boot 中記錄 Rest Web 服務所花費的時間?

[英]How to log time taken by Rest web service in Spring Boot?

我正在使用Spring Boot編寫一個 Web Rest Web 服務。

我想記錄我的網絡服務處理請求所花費的時間。 另外我想記錄標題、方法和調用的 URI。

幾個月前,我使用ContainerRequestFilter and ContainerResponseFilter filter() method.在我的 jersey web 服務中做了類似的事情ContainerRequestFilter and ContainerResponseFilter filter() method.

另外,AOP 更好還是過濾器更好?

您是否嘗試過這樣的基本過濾器?

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
@WebFilter("/*")
public class StatsFilter implements Filter {

    private static final Logger LOGGER = LoggerFactory.getLogger(StatsFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // empty
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        long time = System.currentTimeMillis();
        try {
            chain.doFilter(req, resp);
        } finally {
            time = System.currentTimeMillis() - time;
            LOGGER.trace("{}: {} ms ", ((HttpServletRequest) req).getRequestURI(),  time);
        }
    }

    @Override
    public void destroy() {
        // empty
    }
}

編輯:(感謝@suren-aznauryan)現在,它使用InstantDuration並避免System.currentTimeMillis()

import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
@WebFilter("/*")
public class StatsFilter implements Filter {

    private static final Logger LOGGER = LoggerFactory.getLogger(StatsFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // empty
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        Instant start = Instant.now();
        try {
            chain.doFilter(req, resp);
        } finally {
            Instant finish = Instant.now();
            long time = Duration.between(start, finish).toMillis();
            LOGGER.trace("{}: {} ms ", ((HttpServletRequest) req).getRequestURI(),  time);
        }
    }

    @Override
    public void destroy() {
        // empty
    }
}

Spring boot:日志攔截器

public class ApiLogger extends HandlerInterceptorAdapter {
  private static final Logger logger = LoggerFactory
    .getLogger(ApiLogger.class);

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String requestId = UUID.randomUUID().toString();
    log(request,response, requestId);
    long startTime = System.currentTimeMillis();
    request.setAttribute("startTime", startTime);
    request.setAttribute("requestId", requestId);
      return true;
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    super.afterCompletion(request, response, handler, ex);
    long startTime = (Long)request.getAttribute("startTime");    
    long endTime = System.currentTimeMillis();
    long executeTime = endTime - startTime;
    logger.info("requestId {}, Handle :{} , request take time: {}",request.getAttribute("requestId"), handler, executeTime);
  }

  private void log(HttpServletRequest request, HttpServletResponse response, String requestId) {
    logger.info("requestId {}, host {}  HttpMethod: {}, URI : {}",requestId, request.getHeader("host"),
      request.getMethod(), request.getRequestURI() );
  }
}

注冊攔截器:

@Configuration
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new ApiLogger()).addPathPatterns("/api/v1/*");
  }
}

David 的回答是正確的 - filter 是在 Spring Boot 中實現此類功能的好方法。

Spring Boot 有一個內置端點,它返回有關最近 100 個請求的信息,如下所示:

[{
    "timestamp": 1394343677415,
    "info": {
        "method": "GET",
        "path": "/trace",
        "headers": {
            "request": {
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                "Connection": "keep-alive",
                "Accept-Encoding": "gzip, deflate",
                "User-Agent": "Mozilla/5.0 Gecko/Firefox",
                "Accept-Language": "en-US,en;q=0.5",
                "Cookie": "_ga=GA1.1.827067509.1390890128; ..."
                "Authorization": "Basic ...",
                "Host": "localhost:8080"
            },
            "response": {
                "Strict-Transport-Security": "max-age=31536000 ; includeSubDomains",
                "X-Application-Context": "application:8080",
                "Content-Type": "application/json;charset=UTF-8",
                "status": "200"
            }
        }
    }
},{
    "timestamp": 1394343684465,
    ...
}]

如果您的應用程序正在調用其他服務或查詢數據庫,請考慮使用Sniffy進行分析 - 它不僅會顯示在服務器上花費的時間,還會顯示調用下游系統所花費的時間。 在此處查看現場演示(檢查頂部底部角落的黑色小部件)。

免責聲明:我是 Sniffy 的作者

Sniffy 演示截圖

如果您的控制器是異步的,請使用 Aspect 來獲得正確和完整的執行時間。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.UUID;

@Aspect
@Component
public class LoggingAspect {

  static Logger log = LoggerFactory.getLogger(LoggingAspect.class);

  @Around("execution(* com.aakchoo.api.controller..*(..))")
  public Object profileExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {

    long start = System.currentTimeMillis();
    String className = joinPoint.getSignature().getDeclaringTypeName();
    String methodName = joinPoint.getSignature().getName();
    String apiName = className + "."+ methodName;
    HttpServletRequest request =
        ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
    String requestId = UUID.randomUUID().toString();
    log.info("----->>>>>\nREQUESTED_ID: {}\nHOST: {} HttpMethod: {}\nURI: {}\nAPI: {}\nArguments: {}\n",
        requestId,
        request.getHeader("host"),
        request.getMethod(),
        request.getRequestURI(),
        apiName,
        Arrays.toString(joinPoint.getArgs()));

    Object result = joinPoint.proceed();
    long elapsedTime = System.currentTimeMillis() - start;
    log.info("<<<<<-----\nExecution Time: {} ms [REQUESTED_ID: {}] [API: {}]", elapsedTime,requestId,apiName);

    return result;
  }
}

將 @EnableAspectJAutoProxy 添加到您的 Applciation 類

@EnableAsync
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

而你的 build.gradle 將需要以下內容

compile 'org.aspectj:aspectjweaver:1.8.10'
compile 'org.springframework.boot:spring-boot-starter-aop'

我們可以通過覆蓋add()方法來使用 Springboot InMemoryHttpTraceRepository

public class MyCustomTraceRepository extends InMemoryHttpTraceRepository {


    @Override
    public void add(HttpTrace trace) {
        log(trace);
        super.add(trace);
    }

    //Log only you interested in
    public void log(HttpTrace trace) {
        Map<String, Object> traceMap = new LinkedHashMap<>();
        traceMap.put("status", trace.getResponse().getStatus());
        traceMap.put("method", trace.getRequest().getMethod());
        traceMap.put("uri", trace.getRequest().getUri().getPath());
        traceMap.put("timeTaken", trace.getTimeTaken());

        logger.info(traceMap)
    }
}    

//will Result into
{status=<>, method=<>, uri=/<>, timeTaken=<>}

@查看更多請求和響應跟蹤選項: https ://docs.spring.io/spring-boot/docs/current/actuator-api/html/#http-trace

我正在使用Spring Boot編寫Web Rest Web服務。

我想記錄我的Web服務處理請求所花費的時間。 我也想記錄標頭,方法和URI的調用。

幾個月前,在我的球衣Web服務中,我已經使用ContainerRequestFilter and ContainerResponseFilter filter() method.做了類似的事情ContainerRequestFilter and ContainerResponseFilter filter() method.

另外,AOP是更好還是過濾器?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM