最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang實現(xiàn)實時監(jiān)聽文件并自動切換目錄

 更新時間:2023年12月22日 10:05:25   作者:sky我的世界  
這篇文章主要給大家介紹了golang實現(xiàn)實時監(jiān)聽文件,并自動切換目錄,文中通過代碼示例給大家介紹的非常詳細,對大家的學習或工作有一定的參考價值,需要的朋友可以參考下

應用程序使用golang開發(fā),日志采用zap進行記錄,每天會根據(jù)日期自動創(chuàng)建文件夾存放當天日志記錄(log.log、error.log)如下圖所示,如何實時記錄日志內(nèi)容,進行持久化入庫,并且自動根據(jù)日期切換文件夾監(jiān)聽。

解決方案

采用fsnotify來實現(xiàn),fsnotify 是 Go 語言中的一個庫,用于監(jiān)控文件系統(tǒng)事件,例如文件或目錄的創(chuàng)建、刪除、修改等。它提供了一個跨平臺的文件系統(tǒng)通知接口,允許你監(jiān)聽文件系統(tǒng)的變化并采取相應的措施。

需要注意的是需要設(shè)計數(shù)據(jù)庫或者緩存來存儲解析日志的offset,不然會出現(xiàn)如果程序重新啟動,會重復解析日志文件的問題。

核心代碼

package watch
 
import (
	"bufio"
	"fmt"
	"github.com/fsnotify/fsnotify"
	"go.uber.org/zap"
	"io"
	"os"
	"path/filepath"
	"time"
)
 
// 存儲已處理的位置
func saveProcessedOffset(fullFileName, logLevel string, offset int64) {
	logRunRecord := system.SysRunLogWatchRecord{}
	// 嘗試從數(shù)據(jù)庫中找到匹配的記錄
	global.DB.Where(system.SysRunLogWatchRecord{FullFileName: fullFileName, LogLevel: logLevel}).First(&logRunRecord)
	// 如果找到了匹配的記錄,則更新 offset 值
	if logRunRecord.ID > 0 {
		logRunRecord.ProcessedOffset = offset
		global.DB.Save(&logRunRecord)
	} else {
		// 沒有找到匹配的記錄,插入新記錄
		newLogRecord := system.SysRunLogWatchRecord{
			FullFileName:    fullFileName,
			LogLevel:        logLevel,
			ProcessedOffset: offset,
		}
		global.DB.Create(&newLogRecord)
	}
}
 
// 從存儲中讀取已處理的位置
func readProcessedOffset(fullFileName, logLevel string) int64 {
	var logRunRecord system.SysRunLogWatchRecord
	global.DB.Where("full_file_name = ? and log_level = ?", fullFileName, logLevel).First(&logRunRecord)
	return logRunRecord.ProcessedOffset
}
 
// WatchSysRuntimeLogIncrement 檢測日志
func WatchSysRuntimeLogIncrement(logPath, logLevel string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		global.LOG.Error("fsnotify watch error:", zap.Error(err))
	}
	defer watcher.Close()
	ticker := time.NewTicker(1 * time.Minute)
	global.LOG.Info("啟動一個定時器,每分鐘檢查一次時間并切換目錄")
	defer ticker.Stop()
 
	directory := getCurrentLogDirectory(logPath)
	initCurrentErrorLog(fmt.Sprintf("%s/%s.log", directory, logLevel))
	err = watcher.Add(directory)
	if err != nil {
		global.LOG.Error("watcher Add error", zap.Error(err))
	}
	var currentReadFile *os.File
	// 在循環(huán)外打開文件
	logFilePath := fmt.Sprintf("%s/%s.log", directory, logLevel)
	// 當前正在解析的日志文件
	currentReadFile, err = os.Open(logFilePath)
	if err != nil {
		global.LOG.Error("無法打開文件:", zap.Error(err))
		return
	}
	var processedOffset int64
	for {
		select {
		case <-ticker.C:
			currentDate := time.Now().Format("2006-01-02")
			newDirectory := fmt.Sprintf("%s%s", logPath, currentDate)
			if newDirectory != directory {
				if _, err := os.Stat(fmt.Sprintf("%s/%s.log", newDirectory, logLevel)); err == nil {
					watcher.Remove(directory)
					directory = newDirectory
					err := watcher.Add(newDirectory)
					if err != nil {
						global.LOG.Error("無法監(jiān)控新目錄:", zap.Error(err))
					}
					global.LOG.Info("開始監(jiān)控新文件:" + newDirectory)
					//監(jiān)控新文件的時候,關(guān)閉舊文件
					currentReadFile.Close()
					logFilePath := fmt.Sprintf("%s/%s.log", directory, logLevel)
					currentReadFile, err = os.Open(logFilePath)
					processedOffset = 0
					global.LOG.Info("文件已經(jīng)發(fā)生變化,新文件為:" + logFilePath)
				} else {
					global.LOG.Info("新文件不存在,繼續(xù)監(jiān)控舊文件")
					processedOffset = readProcessedOffset(fmt.Sprintf("%s/%s.log", directory, logLevel), logLevel)
				}
			} else {
				processedOffset = readProcessedOffset(fmt.Sprintf("%s/%s.log", directory, logLevel), logLevel)
			}
		case event, ok := <-watcher.Events:
			if !ok {
				return
			}
			if event.Op&fsnotify.Write == fsnotify.Write {
				global.LOG.Info("解析文件:" + logFilePath)
				if err != nil {
					global.LOG.Error("無法打開文件:", zap.Error(err))
					continue
				}
				currentReadFile.Seek(processedOffset, io.SeekStart)
				scanner := bufio.NewScanner(currentReadFile)
				for scanner.Scan() {
					line := scanner.Text()
					runLog := system.SystemRunningLog{
						Description: logLevel,
						ModuleName:  logPath,
						Operation:   line,
					}
					err = global.DB.Create(&runLog).Error
					if err != nil {
						global.LOG.Error("存儲運行日志錯誤", zap.Error(err))
					}
				}
				if err := scanner.Err(); err != nil {
					global.LOG.Error("讀取文件時發(fā)生錯誤", zap.Error(err))
				}
				// 更新已處理的位置
				processedOffset, err = currentReadFile.Seek(0, io.SeekEnd)
				if err != nil {
					global.LOG.Error("無法獲取文件偏移量:", zap.Error(err))
				}
				// 將已處理的位置存儲到文件中
				saveProcessedOffset(fmt.Sprintf("%s/%s.log", directory, logLevel), logLevel, processedOffset)
			}
		case err, ok := <-watcher.Errors:
			if !ok {
				return
			}
			global.LOG.Error("錯誤事件", zap.Error(err))
		}
	}
}
 
// 獲取當前日期并構(gòu)建日志目錄路徑
func getCurrentLogDirectory(logPath string) string {
	currentDate := time.Now().Format("2006-01-02")
	return fmt.Sprintf("%s%s", logPath, currentDate)
}
 
// 初始文件
func initCurrentErrorLog(errorLogPath string) {
	// 判斷文件是否存在
	_, err := os.Stat(errorLogPath)
 
	if os.IsNotExist(err) {
		// 文件不存在,創(chuàng)建文件夾和文件
		err := os.MkdirAll(filepath.Dir(errorLogPath), os.ModePerm)
		if err != nil {
			global.LOG.Error("os.MkdirAll error:", zap.Error(err))
			return
		}
 
		file, err := os.Create(errorLogPath)
		if err != nil {
			global.LOG.Error("os.Create error:", zap.Error(err))
			return
		}
		defer file.Close()
 
		global.LOG.Info("error.log 文件已經(jīng)存在,開始監(jiān)控:" + errorLogPath)
	} else if err == nil {
		global.LOG.Info("error.log 文件已經(jīng)存在,開始監(jiān)控:" + errorLogPath)
	} else {
		global.LOG.Error("os.IsNotExist error:", zap.Error(err))
		return
	}
}

其中WatchSysRuntimeLogIncrement方法傳入日志路徑和需要解析的日志文件名稱(例如info)后綴默認.log,執(zhí)行此方法即可實現(xiàn)邏輯

以上就是golang實現(xiàn)實時監(jiān)聽文件并自動切換目錄的詳細內(nèi)容,更多關(guān)于golang實時監(jiān)聽文件的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang時間/時間戳的獲取與轉(zhuǎn)換實例代碼

    golang時間/時間戳的獲取與轉(zhuǎn)換實例代碼

    說實話,golang的時間轉(zhuǎn)化還是很麻煩的,最起碼比php麻煩很多,下面這篇文章主要給大家介紹了關(guān)于golang時間/時間戳的獲取與轉(zhuǎn)換的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-11-11
  • 淺析Go語言中閉包的使用

    淺析Go語言中閉包的使用

    閉包是一個函數(shù)和其相關(guān)的引用環(huán)境組合的一個整體。本文主要為大家介紹一下Go語言中閉包的使用,文中的示例代碼講解詳細,對我們學習Go語言有一定幫助,需要的可以參考一下
    2022-12-12
  • 解決golang時間字符串轉(zhuǎn)time.Time的坑

    解決golang時間字符串轉(zhuǎn)time.Time的坑

    這篇文章主要介紹了解決golang時間字符串轉(zhuǎn)time.Time的坑,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go語言部署Kubernetes的實現(xiàn)示例

    Go語言部署Kubernetes的實現(xiàn)示例

    本文介紹了將Go應用部署到Kubernetes的全過程,實現(xiàn)自動擴展、滾動更新、健康檢查、監(jiān)控,并提供了最佳實踐和完整示例,具有一定的參考價值,感興趣的可以了解一下
    2026-05-05
  • Golang之reflect包的使用方法詳解

    Golang之reflect包的使用方法詳解

    Golang的Reflect包(reflect)是一個強大的內(nèi)置包,它提供了在運行時進行程序反射的功能,幫助我們編寫更加靈活、通用且動態(tài)的代碼,為Golang開發(fā)者帶來了更多的可能性,感興趣的同學可以參考一下
    2023-06-06
  • 一文詳解Go語言切片是如何擴容的

    一文詳解Go語言切片是如何擴容的

    切片是一個擁有相同類型元素的可變長度的序列,它是基于數(shù)組類型做的一層封裝。它非常靈活,支持自動擴容。所以本文就來看看Go語言切片是如何擴容的吧
    2023-04-04
  • go語言使用scp的方法實例分析

    go語言使用scp的方法實例分析

    這篇文章主要介紹了go語言使用scp的方法,實例分析了go語言調(diào)用scp命令的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03
  • Golang實現(xiàn)字符串倒序的幾種解決方案

    Golang實現(xiàn)字符串倒序的幾種解決方案

    給定一個字符串,按單詞將該字符串逆序是我們大家在開發(fā)中可能會遇到的一個需求,所以下面這篇文章主要給大家介紹了關(guān)于Golang如何實現(xiàn)字符串倒序的幾種解決方案,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-10-10
  • Go語言開發(fā)redis封裝及簡單使用詳解

    Go語言開發(fā)redis封裝及簡單使用詳解

    這篇文章主要為大家介紹了Go語言開發(fā)redis的封裝及簡單使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2021-11-11
  • Go如何實現(xiàn)Websocket服務以及代理

    Go如何實現(xiàn)Websocket服務以及代理

    這篇文章主要介紹了Go如何實現(xiàn)Websocket服務以及代理方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04

最新評論

平谷区| 东明县| 读书| 咸宁市| 科尔| 苏州市| 弋阳县| 卢龙县| 南城县| 临颍县| 拉孜县| 长葛市| 拉萨市| 榕江县| 朔州市| 勐海县| 宁津县| 大港区| 汝城县| 阿拉善盟| 永吉县| 永昌县| 福安市| 金溪县| 连南| 宁国市| 中西区| 婺源县| 南漳县| 类乌齐县| 玉山县| 安岳县| 祁东县| 高密市| 台江县| 淳化县| 夏河县| 斗六市| 江源县| 万荣县| 伊通|