2 minutes
go_zero+loki轻量日志收集系统
背景
由于系统上会有很多服务,每个服务都会产生日志,如果每个服务都单独收集日志,那么会非常麻烦,所以需要一套轻量级的日志收集系统。
像elk这些日志收集系统我觉得都太笨重了,搭建起来很麻烦,所以选择loki作为日志收集系统,loki是一个开源的日志聚合系统,它使用了一种名为“日志流”的概念来处理日志数据,可以将日志数据存储在内存中,然后定期将数据持久化到磁盘上,loki的架构非常简单,只需要一个loki服务和一个grafana服务即可,loki服务负责接收日志数据,并将数据存储在内存中
安装
这里我们采用docker 安装,我觉得docker真的超级好用,不用去搭建过多的环境 docker-compose.yaml 加了version 会报过时这个错误,还没有去查看具体的原因,不加是没有什么问题的
这个yml 文件在官网也找的到,我只是做一个入门的介绍,具体可以参考官网
networks:
loki:
services:
loki:
image: grafana/loki:2.9.2
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
promtail:
image: grafana/promtail:2.9.2
volumes:
- /var/log:/var/log
command: -config.file=/etc/promtail/config.yml
networks:
- loki
grafana:
environment:
- GF_PATHS_PROVISIONING=/etc/grafana/provisioning
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
entrypoint:
- sh
- -euc
- |
mkdir -p /etc/grafana/provisioning/datasources
cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
orgId: 1
url: http://loki:3100
basicAuth: false
isDefault: true
version: 1
editable: false
EOF
/run.sh
image: grafana/grafana:latest
ports:
- "3000:3000"
networks:
- loki
先给大家上个效果图
loki 内存占用
grafana 内存占用
promtail 内存占用
grafana 面板图
这样就能清晰的看我们的日志了
接入
go_zero 接入
幸好go_zero 的logx 有设置输入流的方法 logx.setWriter(writer)
现在我们需要实现一个writer,将日志输出到loki
package log
import (
"github.com/afiskon/promtail-client/promtail"
"github.com/zeromicro/go-zero/core/logx"
"log"
"os"
"time"
)
var loki promtail.Client
func register(url, source_name, job_name string, sendLevel, PrintLevel promtail.LogLevel) promtail.Client {
labels := "{source=\"" + source_name + "\",job=\"" + job_name + "\"}"
conf := promtail.ClientConfig{
PushURL: url + "/api/prom/push",
Labels: labels,
BatchWait: 5 * time.Second,
BatchEntriesNumber: 10000,
SendLevel: sendLevel,
PrintLevel: PrintLevel,
}
loki, err := promtail.NewClientProto(conf)
if err != nil {
log.Printf("promtail.NewClient: %s\n", err)
os.Exit(1)
}
loki.Infof("loki up sucessfully!")
return loki
}
type LogWrite struct {
// 注册loki
loki promtail.Client
}
// NewLogWrite 创建日志写入器
// lokiUrl 地址
// sourceName 源名称
// jobName 任务名称
// sendLevel 发送级别
// PrintLevel 打印级别
func NewLogWrite(lokiUrl, sourceName, jobName string, sendLevel, PrintLevel promtail.LogLevel) *LogWrite {
return &LogWrite{
loki: register(lokiUrl, sourceName, jobName, sendLevel, PrintLevel),
}
}
func (l *LogWrite) Alert(v any) {
l.loki.Infof("err: %v", v)
}
func (l *LogWrite) Close() error {
l.loki.Shutdown()
return nil
}
func (l *LogWrite) Debug(v any, fields ...logx.LogField) {
if len(fields) > 0 {
l.loki.Debugf("err: %v, file:%s", v, fields[0].Value)
} else {
l.loki.Debugf("err: %v", v)
}
}
func (l *LogWrite) Error(v any, fields ...logx.LogField) {
if len(fields) > 0 {
l.loki.Errorf("err: %v, file:%s", v, fields[0].Value)
} else {
l.loki.Errorf("err: %v", v)
}
}
func (l *LogWrite) Info(v any, fields ...logx.LogField) {
if len(fields) > 0 {
l.loki.Infof("info: %v, file:%s", v, fields[0].Value)
} else {
l.loki.Infof("info: %v", v)
}
}
func (l *LogWrite) Severe(v any) {
l.loki.Errorf("err: %v", v)
}
func (l LogWrite) Slow(v any, fields ...logx.LogField) {
if len(fields) > 0 {
l.loki.Warnf("err: %v, file:%s", v, fields[0].Value)
} else {
l.loki.Warnf("err: %v", v)
}
}
func (l *LogWrite) Stack(v any) {
l.loki.Infof("err: %v", v)
}
func (l *LogWrite) Stat(v any, fields ...logx.LogField) {
l.loki.Infof("err: %v", v)
}
一定要把setWriter 放在s.stop() 后面,不然输入流会关闭,无法生效