我們在開發與維護網站的時候,有時可能會有一些原因需要查詢 log,而紀錄 log 的解決方案有很多,例如:Cloudwatch、DataDog… 等都蠻常見的,如果是自架的方案最常見的就是 ELK 了 (ElasticSearch + LogStash + Kibana) ,其使用了 Elastic Search 強大的資料量處理能力以及搜尋功能,並且有高可擴充性以及可客製化的優點而受到大家青睞,本篇將教學如何直接使用 FileBeat 把資料送進 ElasticSearch 而不需要經過 LogStash
相關連結
Elastic Search 官方網站:https://www.elastic.co/
Filebeat 官方網站:https://www.elastic.co/beats/filebeat
在程式中將 request 以 json 格式紀錄下來
最一開始的步驟是我們需要把想要的 log 存下來
這邊使用 NodeJS 以及紀錄 API access log 做範例
如果是其他程式語言或是其他框架
則依據自己需求進行實作
可以直接跳到後面的 filebeat 設定
設定將 request 記下來的 middleware
首先安裝 nest-winston 與 winston
npm install --save nest-winston winston
在 app.modules.ts 中載入 WinstonModule
import * as winston from 'winston'; import { WinstonModule } from 'nest-winston'; @Module({ imports: [ WinstonModule.forRoot({ transports: [ new winston.transports.File({ filename: 'logs/http.log', level: 'http', }), ], }), ], })
接著建立一個 global middleware 把所有的 request 寫入至檔案 logs/http.log 中
import { Inject, Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { Logger } from 'winston'; import * as moment from 'moment-timezone'; @Injectable() export class RequestLoggerMiddleware implements NestMiddleware { constructor( @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, ) {} use(request: Request, response: Response, next: NextFunction): void { const { ip, method, path, body, query } = request; const userAgent = request.get('user-agent') || ''; const time = moment().format(); response.on('close', () => { const { statusCode } = response; this.logger.http('http', { ip, userAgent, method, path, query: JSON.stringify(query), body: JSON.stringify(body), statusCode, time, }); }); next(); } }
export class AppModule { configure(consumer: MiddlewareConsumer) { consumer .apply(RequestLoggerMiddleware) .forRoutes({ path: '*', method: RequestMethod.ALL }); } }
此時將伺服器啟動,如果成功設定後會看到 http.log 中有我們的 request
使用 filebeat 將 log 送進 elastic search
我們已經成功將 log 寫進檔案中了
接下來我們要將寫進檔案內的資料送進 elastic search 中
首先要先設定 filebeat.yml
(這邊只寫我們用到的,詳細可以參考官方文件)
nestjs-filebeat.yml 內容:
- filebeat.inputs:設定檔案的位置與格式
- processors:資料處理,因為 filebeat 本身會送其他資訊給 elastic search,所以我們使用 drop_fields 把沒有需要用到的欄位去掉
- output.elasticsearch:資料輸出到 elastic search 的設定,包括帳號密碼以及建立的 index 名稱
- setup.template.*:我們設定自定義 ElasticSearch 的欄位所要用的 template 格式,如果沒有自定義,elastic search 會使用內建的 template
# 設定 log 檔案位置 filebeat.inputs: - type: log enabled: true codec: json json.message_key: msg json.keys_under_root: true paths: - /path/to/logs/http.log tail_files: true # 資料處理,將不需要的內建欄位去掉 processors: - drop_fields: fields: - input - ecs - log.offset - agent.type - agent.id - ecs.version - log.file.path - agent.ephemeral_id - agent.version - agent.name - agent.hostname - host.name # 指定輸出至 elastic search 的特定 index output.elasticsearch: hosts: ["http://localhost:9205"] username: "elastic" password: "********" index: "nestlog-%{+yyyy.MM.dd}" template.enabled: true setup.template.name: "nestlog" setup.template.fields: "nestjs-fields.yml" setup.template.pattern: "nestlog-*" setup.template.overwrite: false setup.ilm.enabled: false
nestjs-fields.yml 內容:
要注意的地方:
- 資料格式: 假如設定的格式與實際打給 elasticsearch 的不同,此 log 會被直接完整的丟掉,不會進 elastic search
- 分詞器 (analyzer):如果沒有設定分詞器,預設會建立不使用分詞器
(norms: false)
- default_field:預設為 false,如果沒有設定為 true,使用 simple_query_string 會搜尋不到
- key: nestlog title: nestlog fields: - name: statusCode type: keyword required: false description: > Comment made by the user. default_field: true - name: ip type: text required: false analyzer: simple default_field: true - name: userAgent type: text required: false analyzer: simple default_field: true - name: method type: text required: false analyzer: simple default_field: true - name: url type: text required: false analyzer: simple default_field: true - name: query type: text required: false analyzer: simple default_field: true - name: body type: text required: false analyzer: simple default_field: true ignore_above: 30000 - name: time type: date required: false default_field: true
啟動
注意:以上面的範例來說,filebeat 會在啟動的時候檢查是否有 nestlog 這個 template,兩種情況:
- 如果已經存在,則不會蓋過
- 尚未存在,使用 nestjs-filebeat.yml 裡面的設定蓋過
./filebeat -c nestjs-filebeat.yml -e
到 kibana 上查看 Log
開啟 kibana 的網頁
到 Discover 頁面上找到「Create new data view」
輸入要篩選的 pattern,例如我們在 filebeat.yaml 中
output.elasticsearch.index 的格式為 nestlog-%{+yyyy.MM.dd}
那我們就將 Name 設定為 nestlog-*
而 timestamp field 的部分,因為我們 log 中時間的欄位為 time,則選擇我們選擇 time
建立後就能看到我們的 request log 了