概述
- 基于前文,我們已經了解并搭建完成ELK的所有環境了,現在我們來結合應用程序來使用ELK
- 參考前文:https://active.blog.csdn.net/article/details/138898538
封裝日志模塊
- 在通用工具模塊: gitee.com/go-micro-services/common 這個包是通用的工具包
- 新增 zap.go
package commonimport ("fmt""os""path/filepath""go.uber.org/zap""go.uber.org/zap/zapcore""gopkg.in/natefinch/lumberjack.v2" )// MyType 是我們想要提供的類型 type ZapLogger struct {logger *zap.SugaredLogger }// NewMyType 是一個構造函數,用于創建 MyType 的新實例 func NewZapLogger(filePath string, FilePerms int) *ZapLogger {log, _ := ZapLoggerInit(filePath, FilePerms)return &ZapLogger{logger: log} }func (zl *ZapLogger) Debug(args ...interface{}) {zl.logger.Debug(args) }func (zl *ZapLogger) Debugf(template string, args ...interface{}) {zl.logger.Debugf(template, args...) }func (zl *ZapLogger) Info(args ...interface{}) {zl.logger.Info(args...) }func (zl *ZapLogger) Infof(template string, args ...interface{}) {zl.logger.Infof(template, args...) }func (zl *ZapLogger) Warn(args ...interface{}) {zl.logger.Warn(args...) }func (zl *ZapLogger) Warnf(template string, args ...interface{}) {zl.logger.Warnf(template, args...) }func (zl *ZapLogger) Error(args ...interface{}) {zl.logger.Error(args...) }func (zl *ZapLogger) Errorf(template string, args ...interface{}) {zl.logger.Errorf(template, args...) }func (zl *ZapLogger) DPanic(args ...interface{}) {zl.logger.DPanic(args...) }func (zl *ZapLogger) DPanicf(template string, args ...interface{}) {zl.logger.DPanicf(template, args...) }func (zl *ZapLogger) Panic(args ...interface{}) {zl.logger.Panic(args...) }func (zl *ZapLogger) Panicf(template string, args ...interface{}) {zl.logger.Panicf(template, args...) }func (zl *ZapLogger) Fatal(args ...interface{}) {zl.logger.Fatal(args...) }func (zl *ZapLogger) Fatalf(template string, args ...interface{}) {zl.logger.Fatalf(template, args...) }// 這個供外部調用 func ZapLoggerInit(filePath string, FilePerms int) (*zap.SugaredLogger, error) {fileName, fileErr := createFileWithPerms(filePath, os.FileMode(FilePerms))if fileErr != nil {// 使用 %v 來打印 error 類型的變量fmt.Printf("Error: %v\n", fileErr)return nil, fileErr}fmt.Printf("日志文件路徑: %s\n", fileName)syncWriter := zapcore.AddSync(&lumberjack.Logger{Filename: fileName, //文件名稱MaxSize: 521, // MB// MaxAge: 0,MaxBackups: 0, //最大備份LocalTime: true,Compress: true, //是否啟用壓縮})// 編碼encoder := zap.NewProductionEncoderConfig()// 時間格式encoder.EncodeTime = zapcore.ISO8601TimeEncodercore := zapcore.NewCore(// 編碼器zapcore.NewJSONEncoder(encoder),syncWriter,zap.NewAtomicLevelAt(zap.DebugLevel))log := zap.New(core,zap.AddCaller(),zap.AddCallerSkip(1))return log.Sugar(), nil }// 創建一個多層目錄下的文件,并設置權限, 如果文件已存在,則返回文件的路徑;如果不存在,則創建并返回文件路徑 func createFileWithPerms(filePath string, perms os.FileMode) (string, error) {// 檢查文件或目錄是否存在fileInfo, err := os.Lstat(filePath)if err == nil {// 文件或目錄已存在if fileInfo.IsDir() {// 如果已存在的是目錄,返回錯誤return "", fmt.Errorf("無法創建文件 '%s',因為該路徑已存在且是一個目錄", filePath)}// 如果已存在的是文件,則返回文件路徑return filePath, nil}if !os.IsNotExist(err) {// 如果發生其他錯誤(如權限問題),則返回錯誤return "", fmt.Errorf("檢查文件 '%s' 時發生錯誤: %v", filePath, err)}// 創建多級目錄err = os.MkdirAll(filepath.Dir(filePath), os.ModePerm) // 使用 os.ModePerm 允許所有權限,但文件權限會由下面的 os.Create 設置if err != nil {return "", fmt.Errorf("無法創建目錄 '%s': %v", filepath.Dir(filePath), err)}// 創建文件file, err := os.Create(filePath)if err != nil {return "", fmt.Errorf("無法創建文件 '%s': %v", filePath, err)}// 關閉文件,因為我們只需要路徑defer file.Close()// 設置文件權限err = file.Chmod(perms)if err != nil {return "", fmt.Errorf("無法設置文件 '%s' 的權限: %v", filePath, err)}// 返回新創建文件的路徑return filePath, nil }
- 上面這個模塊,封裝了打印日志的各種方法,基于其定義可以看出是基于實例的
- 也就是說,不是靜態的方法,而是可以 new 出很多實例的,這樣可以讓我們使用場景更加豐富
- 代碼倉庫:https://gitee.com/go-micro-services/common
網關和服務應用程序準備
1 )概述
- ELK簡單來說,一般部署在我們的網關上,還是和之前一樣,基于 gin 框架
- 如果部署在各個微服務中,那環節就比較麻煩,而且資源耗費較多
- 在我們的網關上來使用,還用之前的場景,基于網關上的某一個api來獲取購物車中的數據
- 例如:
- /api/findAllTestElk1?user_id=1 基于這個路由,使用默認日志實例來輸出, 正確日志
- /api/findAllTestElk1?user_id=x 同上,觸發錯誤日志
- /api/findAllTestElk2?user_id=1 基于這個路由,自定義新的日志實例來輸出
- /api/findAllTestElk2?user_id=x 同上,觸發錯誤日志
- 基于以上,可以在不同模塊實例化不同的日志文件
2 )utils包
-
utils/log.go
package utilsimport ("gitee.com/go-micro-services/common" )// 通用配置 var ZapLogger *common.ZapLogger// 日志默認設置 func initLog() {ZapLogger = common.NewZapLogger("logs/app.log", 0777) // 這里兩個參數可以配置到 conf/app.ini 中 }
- 這里使用 common 工具包,來實例化一個默認的日志實例
-
utils/common.go
func init() {initLog() // 添加這個 }
- 可見,在 init 函數中添加
initLog
函數
- 可見,在 init 函數中添加
3 ) 定義路由
package routersimport ("gitee.com/go-micro-services/api/controllers/api""github.com/gin-gonic/gin"
)func RoutersInit(r *gin.Engine) {rr := r.Group("/api"){rr.GET("/findAll", api.ApiController{}.FindAll)rr.GET("/findAllTestElk1", api.Log1Controller{}.FindAllTestElk)rr.GET("/findAllTestElk2", api.Log2Controller{}.FindAllTestElk)}
}
- 可見這里定義了三個路由,我們主要關注后面兩個
- 下面來看對應的控制器
4 )控制器
-
controllers/api/log1.go
package apiimport ("context""fmt""strconv""gitee.com/go-micro-services/api/utils"cart "gitee.com/go-micro-services/cart/proto/cart""github.com/gin-gonic/gin""github.com/prometheus/common/log" )type Log1Controller struct{}// 這個方法用于測試ELK func (con Log1Controller) FindAllTestElk(c *gin.Context) {log.Info("接受到 /api/findAllTestElk1 訪問請求")// 1. 獲取參數user_id_str := c.Query("user_id")userId, err := strconv.ParseInt(user_id_str, 10, 64)if err != nil {utils.ZapLogger.Error("參數異常")c.JSON(200, gin.H{"message": "參數異常","success": false,})return}fmt.Println(userId)// 2. rpc 遠程調用:獲取購物車所有商品cartClient := cart.NewCartService(utils.CartServices, utils.SrvClient)cartAll, err := cartClient.GetAll(context.TODO(), &cart.CartFindAll{UserId: userId})utils.ZapLogger.Info(cartAll)fmt.Println("-----")c.JSON(200, gin.H{"data": cartAll,"success": true,}) }
-
controllers/api/log2.go
package apiimport ("context""fmt""strconv""gitee.com/go-micro-services/api/utils"cart "gitee.com/go-micro-services/cart/proto/cart""gitee.com/go-micro-services/common""github.com/gin-gonic/gin""github.com/prometheus/common/log" )type Log2Controller struct{}// 通用配置 var ZapLogger *common.ZapLoggerfunc init() {ZapLogger = common.NewZapLogger("logs/app2.log", 0777) // 這里相關參數可以配置到 conf/app.ini 中 }// 這個方法用于測試ELK func (con Log2Controller) FindAllTestElk(c *gin.Context) {log.Info("接受到 /api/findAllTestElk2 訪問請求")// 1. 獲取參數user_id_str := c.Query("user_id")userId, err := strconv.ParseInt(user_id_str, 10, 64)if err != nil {ZapLogger.Error("參數異常")c.JSON(200, gin.H{"message": "參數異常","success": false,})return}fmt.Println(userId)// 2. rpc 遠程調用:獲取購物車所有商品cartClient := cart.NewCartService(utils.CartServices, utils.SrvClient)cartAll, err := cartClient.GetAll(context.TODO(), &cart.CartFindAll{UserId: userId})ZapLogger.Info(cartAll)fmt.Println("-----")c.JSON(200, gin.H{"data": cartAll,"success": true,}) }
-
上面兩個控制器的內容,基本一致,我們的目的是用來測試不同的日志實例的測試
- 第一個控制器使用的是默認初始化時的日志對象
- 第二個控制器使用的是重新初始化后的日志實例,這樣我們可以自定義不同的日志生成路徑
-
代碼倉庫
- 網關: https://gitee.com/go-micro-services/api
- 購物車服務: https://gitee.com/go-micro-services/cart
5 )啟動網關和購物車服務

- 可見,兩個服務已經啟動起來了
下載和運行 FileBeat 程序
- 訪問:https://www.elastic.co/cn/downloads/past-releases/filebeat-7-9-3/
- 注意:這里的版本要和之前ELK環境搭建時的版本對應

- 選擇合適的版本,在服務器要選擇服務器版本,這里我選擇Mac版本來測試
- 下載完成后,里面有很多配置好的文件,我們主要關注兩個: 二進制文件
filebeat
和filebeat.yml
- 將這兩個文件拷貝進入網關項目
- 編輯 filebeat.yml
# 輸入 filebeat.inputs:- type: logenabled: truepaths:- ./logs/*.log #輸出 output.logstash:hosts: ["localhost:5044"]
- 這里,可以配置不同的文件來區分各個部署環境,因為環境不同,參數也不同
- 也可以使用環境變量,部署時進行注入,我這里只是做了一個演示
- 啟動命令 $
./filebeat -e -c ./filebeat.yml
- -e: 啟動在終端輸出采集信息
- -c: 指定yml啟動配置文件
- 當然這些個啟動命令,后期也可以在Dockerfile, DockerCompose 或 K8s中定義,不再贅述
登錄并配置 Kibana 來查看日志
1 ) 概述
- 如果是本機搭建的,訪問: http://localhost:5601
- 這里的 5601 就是之前搭建ELK時暴露出來的
- 基于前文配置的用戶名和密碼進行登錄
2 ) 登錄之后,點擊右側的 Discover

3 ) 創建和配置索引




4 )回到 Discover 查看日志

截止目前為止,所有ELK環境已經全部打通 ~