哈嘍大家好,我是鹹魚 最近我們需要把 Nginx 的日誌接入到自研的日誌採集平臺上,但是這個平臺只支持 JSON 格式,所以需要把 Nginx 日誌格式改成 JSON 格式 例如下麵這樣的效果 剛開始在主配置文件 nginx.conf 中定義了一個名叫 json 的日誌格式欄位 驗證的時候其他內容沒 ...
哈嘍大家好,我是鹹魚
最近我們需要把 Nginx 的日誌接入到自研的日誌採集平臺上,但是這個平臺只支持 JSON 格式,所以需要把 Nginx 日誌格式改成 JSON 格式
例如下麵這樣的效果
剛開始在主配置文件 nginx.conf
中定義了一個名叫 json
的日誌格式欄位
驗證的時候其他內容沒啥問題,但是時間是2023-09-12T13:54:22+08:00
這樣子的,不太符合預期
鹹魚想著把 $time_iso8601
變數中的年月日時分秒分別提取出來然後用變數去接受它,如下所示:
我自定義了一個時間格式 $year-$month-$day $hour:$minutes:$seconds:000
,然後接著用了一個 if 語句用於檢查請求的時間是否匹配 ISO8601 時間格式(例如:2023-09-12T13:54:22+08:00
)
如果匹配,它將執行其中的代碼塊。在代碼塊中,使用正則表達式提取時間的年、月、日、小時、分鐘和秒,並將它們賦值給變數 $year
、$month
、$day
、$hour
、$minutes
和 $seconds
log_format json '{ "SYS_NO":"my_nginx",
"OS_TIME":"$year-$month-$day $hour:$minutes:$seconds",
"LOG_TYPE":"",
"RESULT":"$status",
"SPENT_TIME":"$request_time",
"CLIENT_IP":"$remote_addr",
"SERVER_IP":"$server_addr",
"O_CHANNEL":"$http_user_agent",
"UID":"$http_host",
"VALUE1":$body_bytes_sent,
"INFO1":"$http_referer",
}';
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})") {
set $year $1;
set $month $2;
set $day $3;
set $hour $4;
set $minutes $5;
set $seconds $6;
}
但是 nginx -t
檢測的時候我發現 if 語句不能夠放在 http
塊內,否則會報錯
在 Nginx 中,if 語句主要用於在
server
或location
塊內設置條件,以便根據請求的屬性來執行不同的配置。if 語句通常應該包含在server
或location
塊內,而不是直接放在http
塊中
如果將 if 語句放在一個一個 server 塊中,這不得累死我(有很多個 server 塊),而且後期維護也不方便
所以如何將自定義變數在全局配置中生效則成為了一個問題
map
map 指令是由 ngx_http_map_module
模塊提供的,是 Nginx 配置文件中的一種用於創建變數映射的指令
模塊鏈接:Module ngx_http_map_module (nginx.org)
它允許我們將一個或多個輸入值映射到一個輸出值,類似於字典或哈希表的概念。map
指令通常用於根據特定條件為請求設置自定義變數,或者執行基於請求屬性的條件控制
比較常見的 map 用法是通過 map 來實現允許多個功能變數名稱跨域訪問的問題
語法如下:
map $variable $new_variable {
value1 result1;
value2 result2;
...
default default_result;
}
其中:
$variable
輸入變數,通常是 nginx 的內置變數。$new_variable
輸出變數,它將根據輸入值的映射設置為特定的結果。value1
,value2
, ... 輸入值,可以列出多個值。result1
,result2
, ... 與相應輸入值相關聯的輸出結果。default
一個可選項,表示如果沒有匹配的輸入值,將使用預設結果
需要註意的是,map 只能放在 http 塊中
有了 map,我們就可以輕易的把 $time_iso8601
變數中的 ISO 8601 格式的時間戳轉換為指定的時間格式,然後存儲到自定義變數 $log_time
中
# nginx.conf
map $time_iso8601 $log_time {
default "";
"~^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})T(?<hour>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})" "${year}-${month}-${day} ${hour}:${minutes}:${seconds}";
}
我們可以看到:
- 輸入變數為
$time_iso8601
,輸出變數為$log_time
; default ""
:如果沒有匹配任何條件,將使用空字元串作為$log_time
的值"~^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})T(?<hour>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})"
:這是一個正則表達式條件,它用於匹配 ISO 8601 格式的時間戳。該正則表達式包含了多個捕獲組(使用?<name>
語法),用於從時間戳中提取年、月、日、小時、分鐘和秒的值"${year}-${month}-${day} ${hour}:${minutes}:${seconds}"
:這是與正則表達式條件匹配時設置的輸出值。當正則表達式條件匹配時,它會將捕獲組中提取的年、月、日、小時、分鐘和秒的值組合成一個自定義的時間格式,然後將該值設置為$log_time
變數的值
例如,如果 $time_iso8601
的值是 "2023-09-09T14:30:00",則 $log_time
將被設置為 "2023-09-09 14:30:00"
然後我們再把 $log_time
放進我們自定義的日誌格式裡面,完整配置如下
log_format json '{ "SYS_NO":"my_nginx",
"OS_TIME":"$log_time",
"LOG_TYPE":"",
"RESULT":"$status",
"SPENT_TIME":"$request_time",
"CLIENT_IP":"$remote_addr",
"SERVER_IP":"$server_addr",
"O_CHANNEL":"$http_user_agent",
"UID":"$http_host",
"VALUE1":$body_bytes_sent,
"INFO1":"$http_referer",
}';
map $time_iso8601 $log_time {
default "";
"~^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})T(?<hour>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})" "${year}-${month}-${day} ${hour}:${minutes}:${seconds}";
}
最後我們驗證一下