最近我們需要把 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

但是nginx -t
檢測的時候我發現 if 語句不能夠放在http
塊內,否則會報錯
在 Nginx 中,if 語句主要用於在
server
或location
塊內設置條件,以便根據請求的屬性來執行不同的配置。if 語句通常應該包含在server
或location
塊內,而不是直接放在http
塊中
如果將 if 語句放在一個一個 server 塊中,這不得累死我(有很多個 server 塊),而且後期維護也不方便
所以如何將自定義變數在全局配置中生效則成為了一個問題
map
map 指令是由ngx_http_map_module
模組提供的,是 Nginx 配置檔案中的一種用於創建變數對映的指令
模組連結:https://nginx.org/en/docs/http/ngx_http_map_module.html
它允許我們將一個或多個輸入值對映到一個輸出值,類似於字典或雜湊表的概念。map
指令通常用於根據特定條件為請求設置自定義變數,或者執行基於請求屬性的條件控制
比較常見的 map 用法是通過 map 來實現允許多個域名跨域訪問的問題
語法如下:

其中:
$variable
輸入變數,通常是 nginx 的內建變數。$new_variable
輸出變數,它將根據輸入值的對映設置為特定的結果。value1
,value2
, … 輸入值,可以列出多個值。result1
,result2
, … 與相應輸入值相關聯的輸出結果。default
一個可選項,表示如果沒有匹配的輸入值,將使用默認結果
需要注意的是,map 只能放在 http 塊中
有了 map,我們就可以輕易的把$time_iso8601
變數中的 ISO 8601 格式的時間戳轉換為指定的時間格式,然後儲存到自定義變數$log_time
中

我們可以看到:
- 輸入變數為
$time_iso8601
,輸出變數為$log_time
; default ""
:如果沒有匹配任何條件,將使用空字串作為$log_time
的值"~^(?
:這是一個正規表示式條件,它用於匹配 ISO 8601 格式的時間戳。該正規表示式包含了多個捕獲組(使用\d{4})-(? \d{2})-(? \d{2})T(? \d{2}):(? \d{2}):(? \d{2})" ?
語法),用於從時間戳中提取年、月、日、小時、分鐘和秒的值"${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
放進我們自定義的日誌格式裡面,完整配置如下

最後我們驗證一下
