日志框架
Kitex 支持默认 logger 实现和注入自定义 logger 以及重定向默认 logger 输出。
可以用 klog.SetLogger 来替换掉默认的 logger 实现。
obs-opentelemetry 扩展下提供了基于 logrus 和 zap 的日志实现
采用logrus + go-file-rotatelogs + lfshook 实现日志分割(go的logger实例支持logrus接口)
logrus 支持日志分割,需要借助go-file-rotatelogs包来配合,go-file-rotatelogs 实现了 io.Writer 接口,并且提供了文件的切割功能,其实例可以作为 logrus 的目标输出,两者能无缝集成,这也是 file-rotatelogs 的设计初衷
Logger初始化
注意保证日志存放目录存在,最好使用绝对目录,相对目录目前实验不成功
package logger
import (
"github.com/cloudwego/kitex/pkg/klog"
"io"
"path"
"time"
kitexlogrus "github.com/kitex-contrib/obs-opentelemetry/logging/logrus"
rotatelogs "github.com/lestrrat/go-file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
"os"
)
func Initlog() {
//os.MkdirAll("../../log/", 0755)
logrus.SetReportCaller(true)
// 设置日志输出控制台样式,自定义输出样式
logrus.SetFormatter(&MyFormatter{})
// 按按分钟分割,触发机制是有才会分割,然后根据当前是否存在该文进行选择写入还是新建
logFileName := path.Join("/root/dockerfile/go/go/go_workspace/kitex-examples-main/bizdemo_copy/easy_note/log", "output") + ".%Y%m%d%H%M.log"
// 配置日志分割
logFileCut := LogFileCut(logFileName)
writers := []io.Writer{
logFileCut,
os.Stdout}
// 输出到控制台,方便定位到那个文件
fileAndStdoutWriter := io.MultiWriter(writers...)
klog.Info("init log end")
//将logrus的唯一实例作为日志实例,继承了logrus并且开发了新方法
klog.SetLogger(kitexlogrus.NewLogger())
//设置日志级别
klog.SetLevel(klog.LevelDebug)
//设置控制台和日志文件双重输出
klog.SetOutput(fileAndStdoutWriter)
}
// 配置日志切割
// LogFileCut 日志文件切割
func LogFileCut(fileName string) *rotatelogs.RotateLogs {
logier, err := rotatelogs.New(
// 切割后日志文件名称
fileName,
//rotatelogs.WithLinkName(Current.LogDir), // 生成软链,指向最新日志文件
rotatelogs.WithMaxAge(30*24*time.Hour), // 文件最大保存时间
rotatelogs.WithRotationTime(time.Minute), // 日志切割时间间隔
//rotatelogs.WithRotationCount(3),
//rotatelogs.WithRotationTime(time.Minute), // 日志切割时间间隔
)
if err != nil {
panic(err)
}
lfHook := lfshook.NewHook(lfshook.WriterMap{
logrus.InfoLevel: logier,
logrus.FatalLevel: logier,
logrus.DebugLevel: logier,
logrus.WarnLevel: logier,
logrus.ErrorLevel: logier,
logrus.PanicLevel: logier,
},
// 设置分割日志样式
&MyFormatter{})
logrus.AddHook(lfHook)
return logier
}
自定义日志打印形式
package logger
import (
"bytes"
"fmt"
"github.com/sirupsen/logrus"
"path/filepath"
)
type MyFormatter struct{}
func (m *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
var b *bytes.Buffer
if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}
timestamp := entry.Time.Format("2006-01-02 15:04:05")
var newLog string
//HasCaller()为true才会有调用信息
if entry.HasCaller() {
fName := filepath.Base(entry.Caller.File)
newLog = fmt.Sprintf("[xxx-app] [%s] [%s] [%s:%d] [msg=%s]\n",
timestamp, entry.Level, fName, entry.Caller.Line, entry.Message)
} else {
newLog = fmt.Sprintf("[xxx-app] [%s] [%s] [msg=%s]\n", timestamp, entry.Level, entry.Message)
}
b.WriteString(newLog)
return b.Bytes(), nil
}
自制日志收集模块
代码地址git@github.com:simon12138-code/log_collector.git
logagent
根据etcd指令收集对应文件的日志并且通过kafka传入消息队列中
.
├── conf
│ └── app.conf #存入初始话配置,etcd配置,本项目配置,kafka配置,监控日志配置等等
├── config
│ ├── config.go #配置热部署,tailconf中的修改内容并且刷新
│ └── config_notify.go
├── config_log.go
├── data.go #kafka消息格式
├── etcd.go #监听etcd对应key的消息,是否有更新日志的指令
├── go.mod
├── go.sum
├── ip.go #监听本地的所有IP帮助生成etcd的键
├── kafka.go #消息发送模块
├── limit.go #限制发送次数
├── logs
│ ├── logcollect.2023-07-29.001.log
│ └── logcollect.log #本服务的日志
├── main.go #主启动类
├── README.md
├── server.go #主要实现逻辑,监听etcd,读取配置文件
logtransfer
.
├── conf
│ └── app.conf #配置文件
├── config #热配置
│ ├── config.go
│ └── config_notify.go
├── es.go #esclient
├── etcd.go #etcd监听器
├── go.mod
├── go.sum
├── ip.go #ip扫描
├── kafka.go #kafka消费者
├── logs
│ ├── transfer.2023-07-29.001.log
│ └── transfer.log
└── main.go
test_etcd
发出对应收集日志指令,格式如下
type logConfig struct {
Topic string `json:"topic"`
LogPath string `json:"log_path"`
Service string `json:"service"`
SendRate int `json:"send_rate"`
}
cfg := logConfig{Topic: "log", LogPath: "/root/dockerfile/go/go/go_workspace/kitex-examples-main/bizdemo_copy/easy_note/log/output.202307281459.log", Service: "user", SendRate: 10000}
cfglist := []logConfig{cfg}
jsonBytes, err := json.Marshal(cfglist)
fmt.Println("connect succ")
defer cli.Close()
//设置1秒超时,访问etcd有超时控制
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
_, err = cli.Put(ctx, "/logagent/192.168.112.100/log_config", string(jsonBytes))
test_kafka
充当消费者检查对应的消息是否能够消费