SpringBoot系列——Logback日志,輸出到文件以及實時輸出到web頁面


  前言

  SpringBoot對所有內部日志使用通用日志記錄,但保留底層日志實現。為Java Util Logging、Log4J2和Logback提供了默認配置。在不同的情況下,日志記錄器都預先配置為使用控制台輸出,同時還提供可選的文件輸出。默認情況下,SpringBoot使用Logback進行日志記錄。

  日志級別有(從高到低):FATAL(致命),ERROR錯誤),WARN警告),INFO信息),DEBUG調試),TRACE跟蹤)或者 OFF(關閉),默認的日志配置在消息寫入時將消息回顯到控制台。默認情況下,將記錄錯誤級別、警告級別和信息級別的消息。

  PS:Logback does not have a FATAL level. It is mapped to ERROR  Logback沒有FATAL致命級別。它被映射到ERROR錯誤級別

  詳情請戳官方文檔:https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/#boot-features-logging

  本文主要記錄Logback日志輸出到文件以及實時輸出到web頁面

  

  輸出到文件

  我們創建SpringBoot項目時,spring-boot-starter已經包含了spring-boot-starter-logging,不需要再進行引入依賴

  標准日志格式

2014-03-05 10:57:51.112  INFO 45469 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.52
2014-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2014-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1358 ms
2014-03-05 10:57:51.698  INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2014-03-05 10:57:51.702  INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
  • Date and Time: Millisecond precision and easily sortable. 日期和時間:毫秒精度,易於排序。
  • Log Level: ERRORWARNINFODEBUG, or TRACE日志級別:錯誤、警告、信息、調試或跟蹤。
  • Process ID. 進程ID。
  • --- separator to distinguish the start of actual log messages. 分隔符,用於區分實際日志消息的開始。
  • Thread name: Enclosed in square brackets (may be truncated for console output). 線程名稱:括在方括號中(可能會被截斷以用於控制台輸出)。
  • Logger name: This is usually the source class name (often abbreviated). 日志程序名稱:這通常是源類名稱(通常縮寫)。
  • The log message. 日志消息。

  

  如何打印日志?

  方法1

    /**
     * 配置內部類
     */
    @Controller
    @Configuration
    class Config {
        /**
         * 獲取日志對象,構造函數傳入當前類,查找日志方便定位
         */
        private final Logger log = LoggerFactory.getLogger(this.getClass());

        @Value("${user.home}")
        private String userName;

        /**
         * 端口
         */
        @Value("${server.port}")
        private String port;
/**
         * 啟動成功
         */
        @Bean
        public ApplicationRunner applicationRunner() {
            return applicationArguments -> {
                try {
                    InetAddress ia = InetAddress.getLocalHost();
                    //獲取本機內網IP
                    log.info("啟動成功:" + "http://" + ia.getHostAddress() + ":" + port + "/");
                    log.info("${user.home} :" + userName);
                } catch (UnknownHostException ex) {
                    ex.printStackTrace();
                }
            };
        }
    }

  方法2  使用lombok的@Slf4j,幫我們創建Logger對象,效果與方法1一樣

    /**
     * 配置內部類
     */
    @Slf4j
    @Controller
    @Configuration
    class Config {

        @Value("${user.home}")
        private String userName;

        /**
         * 端口
         */
        @Value("${server.port}")
        private String port;/**
         * 啟動成功
         */
        @Bean
        public ApplicationRunner applicationRunner() {
            return applicationArguments -> {
                try {
                    InetAddress ia = InetAddress.getLocalHost();
                    //獲取本機內網IP
                    log.info("啟動成功:" + "http://" + ia.getHostAddress() + ":" + port + "/");
                    log.info("${user.home} :" + userName);
                } catch (UnknownHostException ex) {
                    ex.printStackTrace();
                }
            };
        }
    }

 

 

  簡單配置

  如果不需要進行復雜的日志配置,則在配置文件中進行簡單的日志配置即可,默認情況下,SpringBoot日志只記錄到控制台,不寫日志文件。如果希望在控制台輸出之外編寫日志文件,則需要進行配置

  logging:
    path: /Users/Administrator/Desktop/雜七雜八/ims #日志文件路徑
    file: ims.log #日志文件名稱
    level:
      root: info #日志級別 root表示所有包,也可以單獨配置具體包 fatal error warn info debug trace off

 

  重新啟動項目

  打開ims.log

 

  擴展配置

   Spring Boot包含許多Logback擴展,可以幫助進行高級配置。您可以在您的logback-spring.xml配置文件中使用這些擴展。如果需要比較復雜的配置,建議使用擴展配置的方式

  PS:SpringBoot推薦我們使用帶-spring后綴的 logback-spring.xml 擴展配置,因為默認的的logback.xml標准配置,Spring無法完全控制日志初始化。(spring擴展對springProfile節點的支持)

  

  以下是項目常見的完整logback-spring.xml,SpringBoot默認掃描classpath下面的logback.xml、logback-spring.xml,所以不需要再指定spring.logging.config,當然,你指定也沒有問題

logging:
  config: classpath:logback-spring.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--日志文件主目錄:這里${user.home}為當前服務器用戶主目錄-->
    <property name="LOG_HOME" value="${user.home}/log"/>
    <!--日志文件名稱:這里spring.application.name表示工程名稱-->
    <springProperty scope="context" name="APP_NAME" source="spring.application.name"/>

    <!--默認配置-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!--配置控制台(Console)-->
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

    <!--配置日志文件(File)-->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--設置策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件路徑:這里%d{yyyyMMdd}表示按天分類日志-->
            <FileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/${APP_NAME}.log</FileNamePattern>
            <!--日志保留天數-->
            <MaxHistory>15</MaxHistory>
        </rollingPolicy>
        <!--設置格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <!-- 或者使用默認配置 -->
            <!--<pattern>${FILE_LOG_PATTERN}</pattern>-->
            <charset>utf8</charset>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>100MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 多環境配置 按照active profile選擇分支 -->
    <springProfile name="dev">
        <!--root節點 全局日志級別,用來指定最基礎的日志輸出級別-->
        <root level="INFO">
            <appender-ref ref="FILE"/>
            <appender-ref ref="CONSOLE"/>
        </root>

        <!-- 子節點向上級傳遞 局部日志級別-->
        <logger level="WARN" name="org.springframework"/>
        <logger level="WARN" name="com.netflix"/>
        <logger level="DEBUG" name="org.hibernate.SQL"/>
    </springProfile>
    <springProfile name="prod">

    </springProfile>
</configuration>

  啟動項目,去到${user.home}當前服務器用戶主目錄,日志按日期進行產生,如果項目產生的日志文件比較大,還可以按照小時進行.log文件的生成  

 

 

  輸出到Web頁面

  我們已經有日志文件.log了,為什么還要這個功能呢?(滑稽臉)為了偷懶!

  當我們把項目部署到Linux服務器,當你想看日志文件,還得打開xshell連接,定位到log文件夾,麻煩;如果我們把日志輸出到Web頁面,當做超級管理員或者測試賬號下面的一個功能,點擊就開始實時獲取生成的日志並輸出在Web頁面,是不是爽很多呢?

  PS:這個功能可得小心使用,因為日志會暴露很多信息

 

  LoggingWSServer

  使用WebSocket實現實時獲取,建立WebSocket連接后創建一個線程任務,每秒讀取一次最新的日志文件,第一次只取后面200行,后面取相比上次新增的行,為了在頁面上更加方便的閱讀日志,對日志級別單詞進行着色(PS:如何創建springboot的websocket,請戳:SpringBoot系列——WebSocket

package cn.huanzi.qch.springbootlogback;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket獲取實時日志並輸出到Web頁面
 */
@Slf4j
@Component
@ServerEndpoint(value = "/websocket/logging", configurator = MyEndpointConfigure.class)
public class LoggingWSServer {

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

    /**
     * 連接集合
     */
    private static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
    private static Map<String, Integer> lengthMap = new ConcurrentHashMap<String, Integer>();

    /**
     * 連接建立成功調用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        //添加到集合中
        sessionMap.put(session.getId(), session);
        lengthMap.put(session.getId(), 1);//默認從第一行開始

        //獲取日志信息
        new Thread(() -> {
            log.info("LoggingWebSocketServer 任務開始");
            boolean first = true;
            while (sessionMap.get(session.getId()) != null) {
                BufferedReader reader = null;
                try {
                    //日志文件路徑,獲取最新的
                    String filePath = System.getProperty("user.home") + "/log/" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/"+applicationName+".log";

                    //字符流
                    reader = new BufferedReader(new FileReader(filePath));
                    Object[] lines = reader.lines().toArray();

                    //只取從上次之后產生的日志
                    Object[] copyOfRange = Arrays.copyOfRange(lines, lengthMap.get(session.getId()), lines.length);

                    //對日志進行着色,更加美觀  PS:注意,這里要根據日志生成規則來操作
                    for (int i = 0; i < copyOfRange.length; i++) {
                        String line = (String) copyOfRange[i];
                        //先轉義
                        line = line.replaceAll("&", "&amp;")
                                .replaceAll("<", "&lt;")
                                .replaceAll(">", "&gt;")
                                .replaceAll("\"", "&quot;");

                        //處理等級
                        line = line.replace("DEBUG", "<span style='color: blue;'>DEBUG</span>");
                        line = line.replace("INFO", "<span style='color: green;'>INFO</span>");
                        line = line.replace("WARN", "<span style='color: orange;'>WARN</span>");
                        line = line.replace("ERROR", "<span style='color: red;'>ERROR</span>");

                        //處理類名
                        String[] split = line.split("]");
                        if (split.length >= 2) {
                            String[] split1 = split[1].split("-");
                            if (split1.length >= 2) {
                                line = split[0] + "]" + "<span style='color: #298a8a;'>" + split1[0] + "</span>" + "-" + split1[1];
                            }
                        }

                        copyOfRange[i] = line;
                    }

                    //存儲最新一行開始
                    lengthMap.put(session.getId(), lines.length);

                    //第一次如果太大,截取最新的200行就夠了,避免傳輸的數據太大
                    if(first && copyOfRange.length > 200){
                        copyOfRange = Arrays.copyOfRange(copyOfRange, copyOfRange.length - 200, copyOfRange.length);
                        first = false;
                    }

                    String result = StringUtils.join(copyOfRange, "<br/>");

                    //發送
                    send(session, result);

                    //休眠一秒
                    Thread.sleep(1000);
                } catch (Exception e) {
                    //捕獲但不處理
                    e.printStackTrace();
                } finally {
                    try {
                        reader.close();
                    } catch (IOException ignored) {
                    }
                }
            }
            log.info("LoggingWebSocketServer 任務結束");
        }).start();
    }

    /**
     * 連接關閉調用的方法
     */
    @OnClose
    public void onClose(Session session) {
        //從集合中刪除
        sessionMap.remove(session.getId());
        lengthMap.remove(session.getId());
    }

    /**
     * 發生錯誤時調用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    /**
     * 服務器接收到客戶端消息時調用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) {

    }

    /**
     * 封裝一個send方法,發送消息到前端
     */
    private void send(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

  HTML頁面

  頁面收到數據就追加到div中,為了方便新增了幾個功能:

  清屏,清空div內容

  滾動至底部、將div的滾動條滑到最下面

  開啟/關閉自動滾動,div新增內容后自動將滾動條滑到最下面,點一下開啟,再點關閉,默認關閉

  PS:引入公用部分,就是一些jquery等常用靜態資源

<!DOCTYPE>
<!--解決idea thymeleaf 表達式模板報紅波浪線-->
<!--suppress ALL -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>IMS實時日志</title>

    <!-- 引入公用部分 -->
    <script th:replace="head::static"></script>

</head>
<body>
<!-- 標題 -->
<h1 style="text-align: center;">IMS實時日志</h1>

<!-- 顯示區 -->
<div id="loggingText" contenteditable="true"
     style="width:100%;height: 600px;background-color: ghostwhite; overflow: auto;"></div>

<!-- 操作欄 -->
<div style="text-align: center;">
    <button onclick="$('#loggingText').text('')" style="color: green; height: 35px;">清屏</button>
    <button onclick="$('#loggingText').animate({scrollTop:$('#loggingText')[0].scrollHeight});"
            style="color: green; height: 35px;">滾動至底部
    </button>
    <button onclick="if(window.loggingAutoBottom){$(this).text('開啟自動滾動');}else{$(this).text('關閉自動滾動');};window.loggingAutoBottom = !window.loggingAutoBottom"
            style="color: green; height: 35px; ">開啟自動滾動
    </button>
</div>
</body>
<script th:inline="javascript">
    //websocket對象
    let websocket = null;

    //判斷當前瀏覽器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://localhost:10086/websocket/logging");
    } else {
        console.error("不支持WebSocket");
    }

    //連接發生錯誤的回調方法
    websocket.onerror = function (e) {
        console.error("WebSocket連接發生錯誤");
    };

    //連接成功建立的回調方法
    websocket.onopen = function () {
        console.log("WebSocket連接成功")
    };

    //接收到消息的回調方法
    websocket.onmessage = function (event) {
        //追加
        if (event.data) {

            //日志內容
            let $loggingText = $("#loggingText");
            $loggingText.append(event.data);

            //是否開啟自動底部
            if (window.loggingAutoBottom) {
                //滾動條自動到最底部
                $loggingText.scrollTop($loggingText[0].scrollHeight);
            }
        }
    }

    //連接關閉的回調方法
    websocket.onclose = function () {
        console.log("WebSocket連接關閉")
    };
</script>
</html>

 

  效果展示

 

  后記

  有了日志記錄,我們以后寫代碼時就要注意了,應使用下面的正確示例

//錯誤示例,這樣寫只會輸出到控制台,不會輸出到日志中
System.out.println("XXX");
e.printStackTrace();

//正確示例,既輸出到控制台,又輸出到日志
log.info("XXX");
log.error("XXX報錯",e);

 

  SpringBoot日志暫時先記錄到這里,點擊官網了解更多:https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/#boot-features-logging

 

  補充

  2019-07-03補充:我們之前只對日志等級關鍵字進行着色,還是覺得不夠,因此又新增了類名着色跟HTML轉義

   主要修改:

  效果:

 

 

  2019-08-12補充:我發現有時候顯示的時候,換行不太准確,我們原先是在行末追加<br/>,但有時候讀取出來的一行記錄是自動換行后的數據,頁面顯示效果很丑

 

 

  因此我改成用正則([\d+][\d+][\d+][\d+]-[\d+][\d+]-[\d+][\d+] [\d+][\d+]:[\d+][\d+]:[\d+][\d+])去匹配日期,然后再對應的起始下標插入<br/>,從而達到與控制台輸出類似的效果

   匹配、插入結果

 

  頁面效果

 

  異步輸出日志

  異步輸出日志的方式很簡單,添加一個基於異步寫日志的appender,並指向原先配置的appender即可

    <!-- 將文件輸出設置成異步輸出 -->
    <appender name="ASYNC-FILE" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
        <queueSize>256</queueSize>
        <!-- 添加附加的appender,最多只能添加一個 -->
        <appender-ref ref="FILE"/>
    </appender>

    <!-- 將控制台輸出設置成異步輸出 -->
    <appender name="ASYNC-CONSOLE" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
        <queueSize>256</queueSize>
        <!-- 添加附加的appender,最多只能添加一個 -->
        <appender-ref ref="CONSOLE"/>
    </appender>

  原理很簡單,主線程將日志扔到阻塞隊列中,然后IO操作日志寫入文件是通過新起一個線程去完成的

 

  2020-05-26補充

  e.printStackTrace();會打出詳細異常,異常名稱,出錯位置,便於調試用,但直接調用會輸出到std.err,並沒有輸出到日志文件中,因此需要先輸出到流中再轉成字符串

  封裝工具類

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 捕獲報錯日志處理工具類
 */
public class ErrorUtil {

    /**
     * Exception出錯的棧信息轉成字符串
     * 用於打印到日志中
     */
    public static String errorInfoToString(Throwable e) {
        StringWriter sw = null;
        PrintWriter pw = null;
        try {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            // 將出錯的棧信息輸出到printWriter中
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
        } finally {
            if (sw != null) {
                try {
                    sw.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (pw != null) {
                pw.close();
            }
        }
        return sw.toString();
    }
}

 

  使用

try {
            //省略其他代碼
} catch (Throwable e) {
            //之前的操作,輸出控制台
            e.printStackTrace();
            
            //輸出到日志文件中
            log.error(ErrorUtil.errorInfoToString(e));
}

 

  配置訪問日志

  2020-08-04更新

  SpringBoot默認使用內置Tomcat,那么我們如何配置Tomcat的Access Logging呢?

  詳情可查看官方文檔:

  SpringBoot配置介紹:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#howto-configure-accesslogs

  Apache Tomcat配置介紹:https://tomcat.apache.org/tomcat-8.5-doc/config/valve.html#Access_Logging

 

  SpringBoot對Access Log的默認配置

server.tomcat.accesslog.buffered=true # Whether to buffer output such that it is flushed only periodically.
server.tomcat.accesslog.directory=logs # Directory in which log files are created. Can be absolute or relative to the Tomcat base dir.
server.tomcat.accesslog.enabled=false # Enable access log.
server.tomcat.accesslog.file-date-format=.yyyy-MM-dd # Date format to place in the log file name.
server.tomcat.accesslog.pattern=common # Format pattern for access logs.
server.tomcat.accesslog.prefix=access_log # Log file name prefix.
server.tomcat.accesslog.rename-on-rotate=false # Whether to defer inclusion of the date stamp in the file name until rotate time.
server.tomcat.accesslog.request-attributes-enabled=false # Set request attributes for the IP address, Hostname, protocol, and port used for the request.
server.tomcat.accesslog.rotate=true # Whether to enable access log rotation.
server.tomcat.accesslog.suffix=.log # Log file name suffix.

 

  日志格式說明(摘自上方Apache Tomcat配置介紹官方文檔)

Values for the pattern attribute are made up of literal text strings, combined with pattern identifiers prefixed by the "%" character to cause replacement by the corresponding variable value from the current request and response. The following pattern codes are supported:

  • %a - Remote IP address
  • %A - Local IP address
  • %b - Bytes sent, excluding HTTP headers, or '-' if zero 
  • %B - Bytes sent, excluding HTTP headers
  • %h - Remote host name (or IP address if enableLookups for the connector is false)
  • %H - Request protocol
  • %l - Remote logical username from identd (always returns '-')
  • %m - Request method (GET, POST, etc.)
  • %p - Local port on which this request was received. See also %{xxx}p below.
  • %q - Query string (prepended with a '?' if it exists)
  • %r - First line of the request (method and request URI) 
  • %s - HTTP status code of the response
  • %S - User session ID
  • %t - Date and time, in Common Log Format
  • %u - Remote user that was authenticated (if any), else '-'
  • %U - Requested URL path
  • %v - Local server name
  • %D - Time taken to process the request in millis. Note: In httpd %D is microseconds. Behaviour will be aligned to httpd in Tomcat 10 onwards.
  • %T - Time taken to process the request, in seconds. Note: This value has millisecond resolution whereas in httpd it has second resolution. Behaviour will be align to httpd in Tomcat 10 onwards.
  • %F - Time taken to commit the response, in millis
  • %I - Current request thread name (can compare later with stacktraces)
  • %X - Connection status when response is completed:
    • X = Connection aborted before the response completed.
    • + = Connection may be kept alive after the response is sent.
    • - = Connection will be closed after the response is sent.

There is also support to write information incoming or outgoing headers, cookies, session or request attributes and special timestamp formats. It is modeled after the Apache HTTP Server log configuration syntax. Each of them can be used multiple times with different xxx keys:

  • %{xxx}i write value of incoming header with name xxx
  • %{xxx}o write value of outgoing header with name xxx
  • %{xxx}c write value of cookie with name xxx
  • %{xxx}r write value of ServletRequest attribute with name xxx
  • %{xxx}s write value of HttpSession attribute with name xxx
  • %{xxx}p write local (server) port (xxx==local) or remote (client) port (xxx=remote)
  • %{xxx}t write timestamp at the end of the request formatted using the enhanced SimpleDateFormat pattern xxx

All formats supported by SimpleDateFormat are allowed in %{xxx}t. In addition the following extensions have been added:

  • sec - number of seconds since the epoch
  • msec - number of milliseconds since the epoch
  • msec_frac - millisecond fraction

These formats cannot be mixed with SimpleDateFormat formats in the same format token.

Furthermore one can define whether to log the timestamp for the request start time or the response finish time:

  • begin or prefix begin: chooses the request start time
  • end or prefix end: chooses the response finish time

By adding multiple %{xxx}t tokens to the pattern, one can also log both timestamps.

The shorthand pattern pattern="common" corresponds to the Common Log Format defined by '%h %l %u %t "%r" %s %b'.

The shorthand pattern pattern="combined" appends the values of the Referer and User-Agent headers, each in double quotes, to the common pattern.

When Tomcat is operating behind a reverse proxy, the client information logged by the Access Log Valve may represent the reverse proxy, the browser or some combination of the two depending on the configuration of Tomcat and the reverse proxy. For Tomcat configuration options see Proxies Support and the Proxy How-To. For reverse proxies that use mod_jk, see the generic proxy documentation. For other reverse proxies, consult their documentation.

 

  我們只需要配置以下幾個簡單配置即可

#開啟內置Tomcat請求日志 access.log
server.tomcat.accesslog.enabled=true
#日志格式
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b
#日志輸出目錄
server.tomcat.accesslog.directory=${user.home}/log/accesslog/${spring.application.name}
#日志文件名
server.tomcat.accesslog.prefix=access_log
server.tomcat.accesslog.file-date-format=_yyyy-MM-dd
server.tomcat.accesslog.suffix=.log

 

  效果

 

  如何接口統計QPS?

  如上圖中,logging接口,我們只要統計同一秒中,logging接口的請求次數即是該接口的QPS

 

  代碼開源

  代碼已經開源、托管到我的GitHub、碼雲:

  GitHub:https://github.com/huanzi-qch/springBoot

  碼雲:https://gitee.com/huanzi-qch/springBoot

  • 客戶端IP地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM