您好,登錄后才能下訂單哦!
查看log日志,會經(jīng)常使用到tail -f命令實時跟蹤文件變化。也可以用Go語言的代碼來實現(xiàn)同樣的功能,這樣就可以直接用到項目中去了。這里不用重復造輪子,有一個第三方的庫已經(jīng)實現(xiàn)了這個功能:
import "github.com/hpcloud/tail"
HP團隊出的tail庫,常用于日志收集。這里主要就是看看如何使用。
package main
import (
"os"
"fmt"
"github.com/hpcloud/tail"
"time"
)
func main () {
filename := "tailf_test.txt" // 指定查看哪個文件
tails, err := tail.TailFile(filename, tail.Config{
// 下面的2行配置,相當于命令 tail -F 的效果
// 追蹤文件并保持重試,即該文件被刪除或改名后,如果再次創(chuàng)建相同的文件名,會繼續(xù)追蹤。
ReOpen: true,
Follow: true,
Location: &tail.SeekInfo{Offset: 0, Whence: os.SEEK_END}, // 從哪個位置開始讀,這里的設(shè)置是從文件結(jié)尾
MustExist: false, // 如果文件不存在,會失敗。這是設(shè)為fales,允許文件不存在,等文件一創(chuàng)建就會開始追蹤
Poll: true, // 設(shè)為true,檢查文件的變化。false是通過inotify來檢查
})
if err != nil {
fmt.Fprintf(os.Stderr, "tail file: %s , ERROR: %v\n", tails.Filename, err)
}
for {
msg, ok := <-tails.Lines
if ! ok {
fmt.Fprintf(os.Stderr, "文件不存在,嘗試重新打開文件: %s", tails.Filename)
time.Sleep(time.Millisecond * 1000)
continue
}
fmt.Println("msg:", msg.Text)
fmt.Println("time:", msg.Time)
// windows系統(tǒng)里的換行是\r\n,輸出的時候可能只去掉了\n,導致字符串會以\r結(jié)尾
// 如果以\r結(jié)尾,后面還有字符串的話,就會從頭開始把之前的內(nèi)容覆蓋掉
fmt.Printf("%q\n", msg.Text) // 輸出有問題的原因是這樣的,這里可以看出來
// 可以手動把 msg.Text 最后的 \r 去掉,即使沒有,也不影響
fmt.Printf("msg: %s time: %s\n", strings.TrimRight(msg.Text, "\r"), msg.Time)
}
}
這里最后踩了個小坑,應(yīng)該是windows系統(tǒng)才會有的問題。
解析配置文件使用的第三方庫是屬于beego框架里的一個模塊。
beego是一個快速開發(fā)Go應(yīng)用的HTTP框架,他可以用來快速開發(fā)API、Web及后端服務(wù)等各種應(yīng)用,是一個 RESTful的框架。
安裝:
go get github.com/astaxie/beego
beego是基于八大獨立的模塊構(gòu)建的,是一個高度解耦的框架:
接下來只是單獨把某個模塊拿來使用,不學習框架的使用。安裝的話只能完整的全部裝上了。
基本用法:
配置文件如下:
[server]
host = "1.1.1.1"
port = 23
示例代碼:
package main
import (
"github.com/astaxie/beego/config"
"fmt"
"os"
)
func main() {
conf, err := config.NewConfig("ini", "test.conf") // 配置文件格式和文件路徑
if err != nil {
fmt.Fprintf(os.Stderr, "New Confit ERROR: %v\n", err)
return
}
port, err := conf.Int("server::port") // 獲取數(shù)值型數(shù)據(jù)可能會返回錯誤
if err != nil {
fmt.Fprintf(os.Stderr, "get conf ERROR: %v\n", err)
return
}
fmt.Println("port:", port)
host := conf.String("server::host") // 獲取字符串數(shù)據(jù),不會返回錯誤,讀不到會返回空字符串
fmt.Println("host:", host)
ip := conf.String("server::ip") // 讀不到就會返回空
fmt.Println("ip:", ip)
ip2 := conf.DefaultString("server::ip", "1.1.1.2") // 代碼層面指定的默認值
fmt.Println("ip2", ip2)
}
默認值
上面的默認值是在代碼層面實現(xiàn)的。但是ini配置本身是支持默認值的,定義的時候可以寫在最外面,也可以寫在[default] 里面,效果都是一樣的:
key1 = vale1
[default]
key2 = value2
[server]
host = "1.1.1.1"
port = 23
key1 = v1
示例代碼:
package main
import (
"github.com/astaxie/beego/config"
"fmt"
"os"
)
func main() {
conf, err := config.NewConfig("ini", "test.conf") // 配置文件格式和文件路徑
if err != nil {
fmt.Fprintf(os.Stderr, "New Confit ERROR: %v\n", err)
return
}
server, err := conf.GetSection("server") // 可以獲取到所有的參數(shù),返回map
if err != nil {
fmt.Fprintf(os.Stderr, "get section ERROR: %v\n", err)
}
fmt.Println(server)
df, err := conf.GetSection("default") // default里的以及最外層的參數(shù)都會解析存到這個map里
if err != nil {
fmt.Fprintf(os.Stderr, "get section ERROR: %v\n", err)
}
fmt.Println(df)
key1a, key1b := conf.String("key1"), conf.String("default::key1") // 前面是否加上 default:: 都是一樣的
key2 := conf.String("key2")
fmt.Println(key1a, key1b, key2)
// 解析獲取默認值可能就是這么做的吧,網(wǎng)上沒有找到相關(guān)的示例
k1 := conf.DefaultString("server::key1", conf.String("key1"))
k2 := conf.DefaultString("server::key2", conf.String("key2"))
fmt.Println(k1, k2)
}
這個還是beego框架里的一個組件
先把log輸出到終端:
package main
import (
"github.com/astaxie/beego/logs"
)
func main() {
logs.SetLogger(logs.AdapterConsole) // 設(shè)置日志輸出到哪里, 參數(shù)是個常數(shù)。這里的效果是輸出到終端
logs.SetLevel(logs.LevelInfo) // 設(shè)置日志等級
logs.Debug("DEBUG msg") // 這條等級不夠,不會顯示
logs.Info("INFO msg")
}
這里使用了console引擎,就是輸出到終端,底層是到os.Stdout。
要把log輸出到文件,只需要設(shè)置一個新的file引擎:
package main
import (
"github.com/astaxie/beego/logs"
)
func main() {
logs.SetLogger(logs.AdapterFile, `{"filename":"test.log","level":6}`) // logs.LevelInfo = 6
logs.SetLogger(logs.AdapterConsole, `{"level":4,"?color":true}`) // logs.LevelWarning = 4
logs.SetLevel(logs.LevelInfo)
logs.Info("INFO msg")
logs.Warn("WARN msg")
}
SetLogger接收2個參數(shù)。
第一個參數(shù)就是引擎名,這里用到了2個 "console" 和 "file" 。上面的代碼里寫的是常量名。
第二個參數(shù)是可選參數(shù)(上個例子沒有用,這里都用上了),用來表示配置信息,所有配置寫在一個json字符串里。
這里可參考官網(wǎng)的引擎配置設(shè)置:
https://beego.me/docs/module/logs.md
這里只列出2個
console 主要參數(shù)說明:
file 主要參數(shù)說明:
所有的引擎有這些,左邊是常量,右邊是對應(yīng)的名字:
const (
AdapterConsole = "console"
AdapterFile = "file"
AdapterMultiFile = "multifile"
AdapterMail = "smtp"
AdapterConn = "conn"
AdapterEs = "es"
AdapterJianLiao = "jianliao"
AdapterSlack = "slack"
AdapterAliLS = "alils"
)
所有的引擎,都會自動進行注冊。具體就是寫在引擎的代碼的init方法里:
// beego/logs/console.go
func init() {
Register(AdapterConsole, NewConsole)
}
不過很多太多方法都是小寫的,導致接口沒有暴露出來用不了。只有 console 和 conn 引擎可以注冊。
package main
import (
"github.com/astaxie/beego/logs"
"encoding/json"
"fmt"
"os"
)
func main() {
logs.Register("new", logs.NewConsole)
config := make(map[string]interface{}) // 先定義一個map來存放配置參數(shù)
config["level"] = 4 // LevelWarning = 4
config["color"] = true // map的value可能是字符串、×××或bool等不同的類型,所以定義的時候類型是空接口
configStr, err := json.Marshal(config)
if err != nil {
fmt.Fprintf(os.Stderr, "json Marshal ERROR %v\n", err)
return
}
logs.SetLogger("new", string(configStr)) // 用map來設(shè)置參數(shù),到這里再轉(zhuǎn)成json字符串
logs.SetLogger(logs.AdapterConsole) // 再設(shè)置一個默認的console引擎
logs.Info("INFO msg") // 滿足1個logger
logs.Error("ERROR msg") // 兩個logger都會輸出這條
}
使用map來設(shè)置配置信息
配置信息是要傳遞json字符串進去的,但是人工拼接josn也是很不友好的。所以先把配置寫在map里,然后再序列化成json傳遞給函數(shù)。并且map的value是空接口,因為配置可能是字符串、整數(shù)或布爾等不同類型。
無法注冊file引擎
注冊不了新的file引擎貌似沒啥用。源碼沒有把對應(yīng)的方法暴露出來應(yīng)該就沒有辦法了,就是 func newFileWriter() Logger{}
這個函數(shù),函數(shù)名是小寫的。只能去改源碼,還不方便把上面的函數(shù)名改掉,因為在別處還有調(diào)用這個方法。所以最方便的修改方法是給原來的小寫的函數(shù)名定義一個大寫的別名:
func newFileWriter() Logger {
// 省略函數(shù)體部分
}
var NewFileWriter func() Logger = newFileWriter
如果代碼中有這種需要開放接口,又不想修改每一個被應(yīng)用的位置,可以用上面的例子來把接口變成可導出的。
多文件的正確用法
沒有提供注冊file引擎的接口,是因為輸出到多個文件還提供了另外一個multifile的引擎。之前只把注意力集中到了file引擎上,沒有發(fā)現(xiàn)還有其他的可用的引擎。
雖然multifile底層也是通過調(diào)用file來實現(xiàn)的,是在file的基礎(chǔ)上又做了一些封裝。所以如果需要輸出到多個文件,應(yīng)該使用提供的multifile來實現(xiàn)。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。