本文目錄一覽:
- 1、golang常用的包—-寫日誌 log包
- 2、Golang將日誌同時輸出到控制台和文件
- 3、golang logger輸出格式怎麼修改
- 4、Supervisor與Logrotate
- 5、Golang的log包哪個好用
golang常用的包—-寫日誌 log包
golang提供了一個簡單的日誌輸出包log,常用用法,已經基本滿足日常的日誌輸出需求
1、日誌級別有3個,info,fatal,panic
2、 日誌文件的輸出
輸出的結果 a.log
[Debug]main.go:45: debug info is ,check list ,hello
[Debug]main.go:46: debug info is ,check list ,hello000111
[Info]2020/09/09 Info check
標準輸出:
[Info]2020/09/09 Info check stdout
樣例二:
輸出結果為:
[Info]2020/09/09 Check init info
[Info]2020/09/09 go to function Check
Golang將日誌同時輸出到控制台和文件
日常開發當中需要將golang的log包列印的日誌同時輸出到控制台和文件,應該如何解決這個問題?
log包可以通過SetOutput()方法指定日誌輸出的方式(Writer),但是只能指定一個輸出的方式(Writer)。我們利用io.MultiWriter()將多個Writer拼成一個Writer使用的特性,把log.Println()輸出的內容分流到控制台和文件當中。
原文地址
golang logger輸出格式怎麼修改
1.Logger結構
首先來看下類型Logger的定義:
type Logger struct {
mu sync.Mutex // ensures atomic writes; protects the following fields
prefix string // prefix to write at beginning of each line
flag int // properties
out io.Writer // destination for output
buf []byte // for accumulating text to write
}
主要有5個成員,其中3個我們比較熟悉,分別是表示Log前綴的 “prefix”,表示Log頭標籤的 “flag” ,以及Log的輸出目的地out。 buf是一個位元組數組,主要用來存放即將刷入out的內容,相當於一個臨時緩存,在對輸出內容進行序列化時作為存儲目的地。 mu是一個mutex主要用來作線程安全的實習,當有多個goroutine同時往一個目的刷內容的時候,通過mutex保證每次寫入是一條完整的信息。
2.std及整體結構
在前一篇文章中我們提到了log模塊提供了一套包級別的簡單介面,使用該介面可以直接將日誌內容列印到標準錯誤。那麼該過程是怎麼實現的呢?其實就是通過一個內置的Logger類型的變數 “std” 來實現的。該變數使用:
var std = New(os.Stderr, “”, LstdFlags)
進行初始化,默認輸出到系統的標準輸出 “os.Stderr” ,前綴為空,使用日期加時間作為Log抬頭。
當我們調用 log.Print的時候是怎麼執行的呢?我們看其代碼:
func Print(v …interface{}) {
std.Output(2, fmt.Sprint(v…))
}
這裡實際就是調用了Logger對象的 Output方法,將日誌內容按照fmt包中約定的格式轉義後傳給Output。Output定義如下 :
func (l *Logger) Output(calldepth int, s string) error
其中s為日誌沒有加前綴和Log抬頭的具體內容,xxxxx 。該函數執行具體的將日誌刷入到對應的位置。
3.核心函數的實現
Logger.Output是執行具體的將日誌刷入到對應位置的方法。
該方法首先根據需要獲得當前時間和調用該方法的文件及行號信息。然後調用formatHeader方法將Log的前綴和Log抬頭先格式化好 放入Logger.buf中,然後再將Log的內容存入到Logger.buf中,最後調用Logger.out.Write方法將完整的日誌寫入到輸出目的地中。
由於寫入文件以及拼接buf的過程是線程非安全的,因此使用mutex保證每次寫入的原子性。
l.mu.Lock()
defer l.mu.Unlock()
將buf的拼接和文件的寫入放入這個後面,使得在多個goroutine使用同一個Logger對象是,不會弄亂buf,也不會雜糅的寫入。
該方法的第一個參數最終會傳遞給runtime.Caller的skip,指的是跳過的棧的深度。這裡我記住給2就可以了。這樣就會得到我們調用log 是所處的位置。
在golang的注釋中說鎖住 runtime.Caller的過程比較重,這點我還是不很了解,只是從代碼中看到其在這裡把鎖打開了。
if l.flag(Lshortfile|Llongfile) != 0 {
// release lock while getting caller info – it『s expensive.
l.mu.Unlock()
var ok bool
_, file, line, ok = runtime.Caller(calldepth)
if !ok {
file = “???”
line = 0
}
l.mu.Lock()
}
在formatHeader裡面首先將前綴直接複製到Logger.buf中,然後根據flag選擇Log抬頭的內容,這裡用到了一個log模塊實現的 itoa的方法,作用類似c的itoa,將一個整數轉換成一個字元串。只是其轉換後將結果直接追加到了buf的尾部。
縱觀整個實現,最值得學習的就是線程安全的部分。在什麼位置合適做怎樣的同步操作。
4.對外介面的實現
在了解了核心格式化和輸出結構後,在看其封裝就非常簡單了,幾乎都是首先用Output進行日誌的記錄,然後在必要的時候 做os.exit或者panic的操作,這裡看下Fatal的實現。
func (l *Logger) Fatal(v …interface{}) {
l.Output(2, fmt.Sprint(v…))
os.Exit(1)
}
// Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1).
func (l *Logger) Fatalf(format string, v …interface{}) {
l.Output(2, fmt.Sprintf(format, v…))
os.Exit(1)
}
// Fatalln is equivalent to l.Println() followed by a call to os.Exit(1).
func (l *Logger) Fatalln(v …interface{}) {
l.Output(2, fmt.Sprintln(v…))
os.Exit(1)
}
這裡也驗證了我們之前做的Panic的結果,先做輸出日誌操作。再進行panic。
Supervisor與Logrotate
在golang的gin項目中使用supervisor守護進程,用子進程配置將標準輸出日誌轉移到指定目錄下,然後使用阿里雲的日誌服務將標準輸出日誌轉移到線上做一些分析和預警。
項目上線之後一切正常,可是周日夜裡三點左右阿里雲的日誌服務採集不到日誌,一頓pv為0的告警過來,趕緊打開電腦,線上服務正常,鬆一口氣,supervisor狀態也正常,觀察了一會業務數據正常就安然入睡了,心想可能是因為配置項有缺陷吧,回頭好好整整supervisor的配置再觀察一波。
早上起來打開伺服器,cd /var/log/supervisor/,發現存在兩個日誌文件,分別是xxxx.log-20201223和xxxx.log,xxxx.log的大小為0,xxxx.log-20201223還在繼續寫入請求日誌,許可權問題?chmod 777之後發現新的文件還是不寫入日誌,重啟 supervisor之後發現日誌能正常寫入了。。。一開始懷疑是supervisor日誌切割備份有問題,將配置stdout日誌文件大小的stdout_logfile_maxbytes配置項,默認 50MB改成0,代表無限大,stdout日誌文件備份數的stdout_logfile_backups配置項,默認10改為0,代表不備份,重啟supervisor,心想不切割總不會再出現切割之後不往新文件寫內容的問題了,真乃明智之選。:)
一周過去,0pv的告警如期而至,雖然不影響線上業務,如鯁在喉讓我久久不能釋懷。全網翻,百度谷歌,中文英文,去github上翻issue等等,看到一個歷史issue1090,Better support for logrotate,感覺和日誌轉儲相關,於是查了下logrotate相關資料,logrotate程序是一個日誌文件管理工具。用於分割日誌文件,刪除舊的日誌文件,並創建新的日誌文件,起到「轉儲」作用。centos系統默認安裝,於是找到對應的配置文件,果不其然裡面就有supervisor,默認配置如下:
/var/log/supervisor/*.log {
missingok
weekly
notifempty
nocompress
},看到weekly感覺離這個問題的答案不遠了,於是去查找linux的logrotate往舊文件寫入的問題,在一篇logrotate writing to old app.log.1 instead of app.log的文章中找到需要配置參數copytruncate,是用於還在打開中的日誌文件,把當前日誌備份並截斷;是先拷貝再清空的方式,拷貝和清空之間有一個時間差,可能會丟失部分日誌數據。增加完配置之後,為了快速驗證結果,修改weekly為daily,第二天日誌正常切割了,新的文件也正常寫入了標準日誌。至此問題解決。
linux的logrotate對於運維來說可能是常識,作為開發剛接觸運維,只能慢慢積累了,工作之餘看看相關的運維知識,盡量少採坑。
Golang的log包哪個好用
建議使用Log,這是android專門用來開發列印日誌使用的。輸出的日誌可以在Logcat中查看。 Android開發中,所的有輸出都在logcat中 包含System.out輸出和printStackTrace()輸出都在Logcat中,Android開發,建議使用android提供的Log
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/200769.html