關注本頭條號,每天堅持更新原創干貨技術文章。
如需學習視頻,請在微信搜索公眾號“智傳網優”直接開始自助視頻學習
1. 前言
本文主要介紹如果高效地使用工具高亮顯示某些輸出結果和關鍵字。
您可以使用正則表達式和特定規則格式突出顯示文本。分層規則格式易于理解,但功能非常強大。該注釋將從標準輸入讀取每一行,每行根據FILE中定義的規則突出顯示,然后打印到標準輸出(屏幕)。
高效地使用工具高亮顯示某些輸出結果和關鍵字
remark命令一般很少人知道它可以把系統日志文件關鍵內容高亮標注顏色并打印到屏幕上,例如像ping,traceroute等等命令的輸出結果。一般情況下,我們需要自己寫正則表達式定義哪些關鍵結果應該高亮。有了remark命令后,你可能不太需要自己手動編寫規則了。
2. 安裝`remark`命令
Debian / Ubuntu Linux 執行以下命令安裝remark:
[root@zcwyou ~]# cd /tmp [root@zcwyou ~]# wget http://savannah.nongnu.org/download/regex-markup/regex-markup_0.10.0-1_amd64.deb [root@zcwyou ~]# sudo dpkg -i regex-markup_0.10.0-1_amd64.deb
RHEL / CentOS / Fedora Linux 64用戶,執行以下命令安裝remark命令:
[root@zcwyou ~]# cd /tmp [root@zcwyou ~]# wget http://savannah.nongnu.org/download/regex-markup/regex-markup-0.10.0-1.x86_64.rpm [root@zcwyou ~]# rpm -ivh regex-markup-0.10.0-1.x86_64.rpm
或者你從源代碼編譯它
[root@zcwyou ~]# cd /tmp [root@zcwyou ~]# wget http://savannah.nongnu.org/download/regex-markup/regex-markup-0.10.0.tar.gz [root@zcwyou ~]# tar -xvf regex-markup-0.10.0.tar.gz [root@zcwyou ~]# cd regex-markup-0.10. [root@zcwyou ~]# ./configure [root@zcwyou ~]# make [root@zcwyou ~]# sudo make install
安裝remark命令自帶高亮語法
3. `remark`語法
command1 | remark /path/to/config
command2 arg1 arg2 | remark /path/to/config
高亮顯示ping的執行結果
[root@zcwyou ~]# ping -c 4 www.linuxrumen.com | remark /usr/share/regex-markup/ping
你可以創建一個bash shell 功能和它入到~/.bashrc file的配置文件中
[root@zcwyou ~]# ping() { /bin/ping $@ | remark /usr/share/regex-markup/ping; }
remark-pin 的規則文件放在/usr/share/regex-markup/ping。它由樣式和宏定義以及匹配語句組成。匹配語句的順序很重要,因為它們是從上到下執行的。樣式和宏在使用之前需要定義。規則文件的語法類似于編程語言(如C和Java)的縮寫,并且使用空格并不重要:
[root@zcwyou ~]# cat /usr/share/regex-markup/ping
它的文件內容一般是這樣的
# Rules to highlight the output of ping(8) include "common" # Special: Color all lines that don't match any of the rules below red /.*/ red /^PING ([-.a-zA-Z0-9]+) \(([-.a-zA-Z0-9]+)\) ([0-9]+)\(([0-9]+)\) bytes of data\.$/ { default 1 blue 2 green 3,4 red break # this is merely to skip the matches below } /^PING ([-.a-zA-Z0-9]+) \(([-.a-zA-Z0-9]+)\): ([0-9]+) data bytes$/ { default 1 blue 2 green 3 red break # this is merely to skip the matches below } /^([0-9]+) bytes from ([-.a-zA-Z0-9]+) \(([-.a-zA-Z0-9]+)\): icmp_seq=([0-9]+) ttl=([0-9]+) time=(.*)$/ { default 1,4,5,6 red 2 blue 3 green break } /^([0-9]+) bytes from ([-.a-zA-Z0-9]+): icmp_seq=([0-9]+) ttl=([0-9]+) time=(.*)$/ { default 1,3,4,5 red 2 green break } /^--- ([-.a-zA-Z0-9]+) ping statistics ---$/ { default 1 blue break } /^([0-9]+) packets transmitted, ([0-9]+) packets received, ([0-9]+)% packet loss$/ { default 1,2,3 red break } /^([0-9]+) packets transmitted, ([0-9]+) received, ([0-9]+)% packet loss, time ([0-9]+ms)$/ { default 1,2,3,4 red break } /^round-trip min\/avg\/max = ([.0-9]+)\/([.0-9]+)\/(.*)$/ { default 1,2,3 red break } /^rtt min\/avg\/max\/mdev = ([.0-9]+)\/([.0-9]+)\/([.0-9]+)\/(.*)$/ { default 1,2,3,4 red break }
默認的樣式定義文件放在/usr/share/regex-markup/common
試試跟traceroute結合
[root@zcwyou ~]# traceroute www.linuxrumen.com | remark /usr/local/share/regex-markup/traceroute
執行以下命令把Linux系統日志高亮顯示
[root@zcwyou ~]# grep something /var/log/syslog | remark /usr/share/regex-markup/syslog [root@zcwyou ~]# tail -f /var/log/syslog | remark /usr/share/regex-markup/syslog
使用remark命令高亮顯示diff結果
先使用diff命令對比file1和file2,然后重定向到remark,由remark高亮顯示結果
[root@zcwyou ~]# diff file1 file2 | remark /usr/share/regex-markup/diff
使用remark命令高亮顯示mark結果
[root@zcwyou ~]# cd /path/to/build [root@zcwyou ~]# make | remark /usr/share/regex-markup/make
4. 如何定制高亮規則?
如果你有這樣的需求,請參考remark命令手冊:
[root@zcwyou ~]# man remark
如何定制高亮規則
本文已同步至博客站,尊重原創,轉載時請在正文中附帶以下鏈接:
https://www.linuxrumen.com/cyml/1201.html
點擊了解更多,快速查看更多的技術文章列表。
日志是我們運維用來分析問題和處理問題的重要維護依據,但是Nginx的日志文件是非常龐大的,而且以文本格式顯示導致Nginx的日志直接分析非常困難。有時候為了初步處理或緊急處理問題,我們也對直接打開Nginx的日志文件進行分析,這里我們就介紹如何對Nginx的默認日志格式進行分析和含義的解讀。
在安裝好Nginx后系統會默認給你開通access.log訪問日志功能,這個配置文件在etc/nginx的文件夾下,打開配置文件后如下面是按行進行的默認格式配置。
log_format main ' $remote_addr - $remote_user [$time_local] "$request" '' $status $body_bytes_sent " $http_referer" '' "$http_user_agent" "$http_x_forwarded_for" ';
Nginx日志的配置項目:access_log /var/log/nginx/access.log main;
日志寫入文本文件是一行行的,這里我們把一行日志,按3個字段進行分行進行分析。
61.135.165.19 - 這里是訪問者的IP地址
- 這里是客戶名稱,但一般獲取不到。
[17/Nov/2018:03:39:32 +0800] 這里是訪問的時間
"GET /ask/0901/images/a13.jpg HTTP/1.0" 這個是這次記錄的訪問對象
200 返回的狀態碼
19916 這條記錄訪問對象的大小
"http://xx.xx.xx/ask/0901/index.htm?jh=haerbinlvyou-YD&gjc=7riyoudongbei&e_keywordid=%7Bkeywordid%7D" 這個是從什么入口訪問的也就是來源。
"Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C;Baiduspider-ads)AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30" 這個是客戶端瀏覽器的一些參數
"10.205.160.32" 這里是代理的地址,一般是沒有的。
remote_addr 遠程請求IP地址
remote_user 客戶端用戶的名稱,沒有時用-符合代替
time_local 本地時間戳
request 請求具體URI文件,例如網頁中的JPG圖片。
status http請求狀態
body_bytes_sent 請求文件大小
http_refer 網頁url跳轉來源,源地址,可以偽造。例如一個HTML網頁然后調用圖片URI資源。
http_user_agent 用戶終端瀏覽器UserAgent的相關信息
http_x_forwarded_for是HTTP的請求端真實的IP,只有在通過了HTTP 代理或者負載均衡服務器時才會添加該項
其他非默認的選項
host 請求host地址
request_time 整個請求的總時間
upstream_addr 后臺提供服務的地址(即轉發處理的目標地址)
upstream_reponse_time請求時,upstream的響應時間
upstream_status upstream狀態
通過如下方式達到日志切割:
# vi logcron.sh log_dir="/data/logs/nginx" date_dir=`date +%Y%m%d` /bin/mkdir -p ${log_dir}/${date_dir} > /dev/null 2>&1 /bin/mv ${log_dir}/access.log ${log_dir}/${date_dir}/access.log
這里只需要定義一個cron,在每天晚上23:59:50執行這個腳本就可以了。
篇幅有限,關于nginx的access.log訪問日志方面就介紹到這了,后面會分享更多關于devops和DBA方面內容,感興趣的朋友可以關注下!
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: [/*]
如何打印日志?
方法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一樣
/**
* 配置內部類
*/
@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();
}
};
}
}
如果不需要進行復雜的日志配置,則在配置文件中進行簡單的日志配置即可,默認情況下,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文件的生成
2021-02-24更新:
如果需要按日志級別分別輸出到對應的日志文件,在appender標簽新增filter標簽進行指定
<!-- 時間滾動輸出 level為 【debug / info / warn / error】 日志 -->
<appender name="【DEBUG / INFO / WARN / ERROR】_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 忽略其他配置 -->
<!-- 此日志文件只記錄 【debug / info / warn / error】 級別的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>【debug / info / warn / error】</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
我們已經有日志文件.log了,為什么還要這個功能呢?(滑稽臉)為了偷懶!
當我們把項目部署到Linux服務器,當你想看日志文件,還得打開xshell連接,定位到log文件夾,麻煩;如果我們把日志輸出到Web頁面,當做超級管理員或者測試賬號下面的一個功能,點擊就開始實時獲取生成的日志并輸出在Web頁面,是不是爽很多呢?
PS:這個功能可得小心使用,因為日志會暴露很多信息
使用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("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\"", """);
//處理等級
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();
}
}
}
頁面收到數據就追加到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();
}
}
也可以使用騷操作簡化代碼
public static String errorInfoToString(Throwable e) {
//try-with-resource語法糖 處理機制
try(StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw)){
e.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
}catch (Exception ignored){
throw new RuntimeException(ignored.getMessage(),ignored);
}
}
使用
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:
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:
All formats supported by SimpleDateFormat are allowed in %{xxx}t. In addition the following extensions have been added:
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:
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
作者:huanzi-qch
出處:https://www.cnblogs.com/huanzi-qch
若標題中有“轉載”字樣,則本文版權歸原作者所有。若無轉載字樣,本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利.
*請認真填寫需求信息,我們會在24小時內與您取得聯系。