使用 VictoriaLogs 存储和查询服务器日志

欢迎访问我的个人小站 莹的网络日志 ,不定时更新文章和技术博客~

目前为止,我查询服务器日志的方式都是小作坊式做法,先是连进服务器找到日志文件,要么使用 vim 打开文件搜索要么就是用 grep。当前我只有一个服务器进程,操作起来还好,但是如果需要增加服务器进程数量进行负载均衡的话就非常麻烦,急需一个日志采集系统在统一的地方集中管理和查找日志。恰好公司最近将日志采集从 elk 切换到了 victoria-logs,我也稍微有些好奇当前的日志生态大致是怎样的。

victoria-logs

事实上,提到日志采集系统或者说框架,我第一个想到的就是 elk,也是目前这个领域应用最为广泛的,elastic 负责数据存储和搜索,logstash 负责日志数据采集,kibana 负责图形化展示。之前使用的时候觉得真是超级好用,然而缺点就是太重了,这方面是我无法接受的,还记得前段时间研究搜索引擎时,刚用 docker 启动 elastic search 就占用了 1GB 内存,直接惊掉下巴,这根本不是我租的那个蝇量级服务器能够承担的。

用不了 elk 只能退而求其次,首先分析一下我的需求,其实就是需要一个集中统一管理日志的地方,方便查日志,如此一来 kibana 所代表的角色就可有可无了,或者用服务器目前已经部署的 grafana 代替。日志数据采集是必须的,总要有一个搬运工将程序日志收集到数据库中,但并不复杂,毕竟我的服务器程序非常简单,打印的日志也都是固定格式,开发个日志采集也不怎么费事。那么重担就落在寻找 elastic 的替代品身上了。

在一番搜索过后,摘选出了两个比较信得过的技术栈 victoria-logs 和 loki,victoria-logs 是因为公司项目本身就在用,借机熟悉一下不是坏事,loki 则是有 grafana 背书,我还是比较信任 grafana 的。

bash 复制代码
docker run -d \
    --name=loki \
	--network monitoring \
    -p 3100:3100 \
    grafana/loki:latest \
    -config.file=/etc/loki/local-config.yaml
bash 复制代码
docker run -d \
	--name victoria-logs \
	--network monitoring \
	-p 9428:9428 \
	-v /srv/data/victoria-logs:/victoria-logs-data \
	victoriametrics/victoria-logs:latest \
	-storageDataPath=/victoria-logs-data \
	-retentionPeriod=30d
bash 复制代码
docker stats
text 复制代码
CONTAINER ID   NAME            CPU %     MEM USAGE / LIMIT     MEM %     NET I/O        BLOCK I/O   PIDS
b9635e741ba7   victoria-logs   0.05%     5.469MiB / 7.654GiB   0.07%     872B / 126B    0B / 0B     9
af17f30b01a4   loki            1.38%     61.79MiB / 7.654GiB   0.79%     1.3kB / 126B   0B / 0B     13

稍微对比了下 docker 镜像启动后空档运行的资源占用,看来是 victoria-logs 小胜一筹。就没对比满负荷的资源占用了,毕竟现在网站日访问量都不知道能不能到三位数,很难说会有服务器满载的情况,总之降本增效不一定增效,但一定降本了~

VictoriaLogs 文档 中也介绍了 victoria-logs 自身的一些优点和特色,还有一些从 elastic 和 loki 迁移过来的用户的分享,虽然 Github VictoriaMetrics/VictoriaLogs 的 Star 数量仅有几百个,有点惨不忍睹。不过我这个也不是企业级项目,够用就行,如果后面业务量增加的话再切换成 elk。

vector

日志采集的技术方案选择相对就很随意了,选择了比较流行的 vector,同样使用 docker 部署,需要注意其中两个地方需要容器挂载,一个是宿主机的日志目录,对应的是服务器程序输出文件的位置,一个是配置文件。

bash 复制代码
docker run -d \
    --name vector \
    --network monitoring \
    -v /etc/vector/vector.yaml:/etc/vector/vector.yaml:ro \
    -v /var/log/blog:/var/log/blog:ro \
    timberio/vector:nightly-alpine \
    --config /etc/vector/vector.yaml

配置文件可以用 toml 也可以用 yaml,或者是 json,本来我问 ChatGPT 给出的示例都是 toml 的,也打算用 toml,但是后面看文档中大部分示例都是用的 yaml,本着方便 copy 的原则我这里也选用了 yaml 格式的配置,虽然实际上都差不多。文档参考:Vector quickstart

bash 复制代码
vim /etc/vector/vector.yaml

配置整体分三块,而且是非常标准的上中下,第一块 sources 是数据输入,比如我的需求是监听文件更新,所以类型选择 type。第二块 transforms 是数据处理,比如做一些简单的转换。第三块 sinks 是数据输出,这里我需要把处理好的数据发送到 victoria-logs 中去。方便的是 victoria-logs 本身就兼容 elasticsearch 的 api,已有的技术栈可以无痛迁移,具体参考VictoriaLogs / Data Ingestion / Vector Setup

yaml 复制代码
sources:
  blog_server_logs:
    type:   "file"
    include: 
      - "/var/log/blog/*.log"
    read_from: "beginning"

transforms:
  blog_server_json_logs:
    type: remap
    inputs:
      - blog_server_logs
    source: |
      ._msg = .message
      .message = parse_json(.message) ?? {}
      .level = .message.level
      .timestamp = .message.ts

sinks:
  victorialogs:
    inputs:
      - "blog_server_json_logs"
    type: elasticsearch
    endpoints:
      - http://victoria-logs:9428/insert/elasticsearch/
    compression: gzip
    healthcheck:
      enabled: false
    query:
      _msg_field: message
      _time_field: timestamp
      _stream_fields: host,container_name

vector 打印采集到的数据

遇到服务一部署就行正常运行,如果不是经验丰富,那一定是幸运女神的眷顾。而我这次就没这么幸运了,那边服务器日志正常增加,这边 victoria-logs 却是什么数据都没有,查看 vector 的日志只有监听文件成功的日志打印,看起来一切正常,真让人摸不着头脑。

text 复制代码
2025-09-28T14:59:15.296799Z  INFO source{component_kind="source" component_id=blog_server_logs component_type=file}:file_server: vector::internal_events::file::source: Found new file to watch. file=/var/log/blog/blog_backend.log

所幸 vector 还提供了更多的输出方式,比如打印日志,将配置表添加如下字段,类型选择 console。

yaml 复制代码
sinks:
  print:
    inputs:
      - "blog_server_logs"
    type: "console"
    encoding:
      codec: "json"

如果成功就会在 vector 的日志中看到打印的内容

bash 复制代码
docker logs vector --tail 50

可惜的是我依旧没有看到打印,悬着的心终于死了。经过一番摸索,终于拨开云雾见天明,发现是在多次重试的过程中删除过日志文件,但是 vector 保存了上次收集日志的位置,可能是防止重启或者什么特殊情况导致重复消费。而我在调试的过程中,有过重启服务器和删除日志的操作,但是 vector 检查点不重置,新生成的日志小于检查点的位置,vector 会认为所有日志都消费过了,直到新日志超过检查点。解决办法就是删除镜像重新运行 vector,或者更换一下日志文件的位置。

zap 日志输出格式

最初的最初,我的想法是只要收集到日志就好了,不做处理直接存起来后面查,但事与愿违一下就遇到了拦路虎, victoria-logs 解析 json 失败,报错信息如下。

swift 复制代码
2025-09-28T07:38:48.422Z        warn    VictoriaLogs/app/vlinsert/jsonline/jsonline.go:54       remoteAddr: "172.18.0.6:50528"; requestURI: /insert/jsonline; cannot process jsonline request; error: unexpected tail: "T15:38:46+08:00 \x1b[34mINFO\x1b[0m Vodka inte...\": 200, \"body_size\": 2658, \"latency\": 4}"; line contents: "2025-09-28T15:38:46+08:00 \x1b[34mINFO\x1b[0m Vodka internal Request {\"method\": \"GET\", \"path\": \"/metrics\", \"client_ip\": \"172.18.0.2\", \"proto\": \"HTTP/1.1\", \"status\": 200, \"body_size\": 2658, \"latency\": 4}"

这要追溯到 golang 的 zap 日志打印库,我的设置是打印 level 时前后加上固定的转义字符比如 \x1b[34mINFO\x1b[0m,让 INFO 或者 ERROR 在控制台中显示蓝色和红色,然而这个转义字符给 json 解析造成了很大麻烦。

解决方法之一是在 vector 的数据处理部分添加大量逻辑替换或者复杂正则匹配,方法二则是更改服务器日志打印参数,开发环境便于肉眼观察可以打印颜色,但是生产环境就要规规矩矩只输出常规字符。这里我参考了Get started with ECS Logging Go (Zap)

go 复制代码
// 带有转义色彩控制字符
config := zapcore.EncoderConfig{
  EncodeLevel:      zapcore.CapitalColorLevelEncoder,
  // ...
}
// 以便于查日志的控制台格式输出
encoder = zapcore.NewConsoleEncoder(encoderConfig)

// 不带转义色彩控制字符
config := zapcore.EncoderConfig{
  EncodeLevel:      zapcore.LowercaseLevelEncoder,
  // ...
}
// 以 json 的格式输出
encoder = zapcore.NewJSONEncoder(encoderConfig)
  • 原本的格式
text 复制代码
2025-10-01T00:43:56+08:00 ^[[31mERROR^[[0m serv.go:86 failed to initialize database, got error Error 1045 (28000): Access denied for user 'nobody'@'localhost' (using password: YES)
  • 更改后的格式
text 复制代码
{"level":"error","ts":1759252206.642765,"caller":"serv.go:86","msg":"failed to initialize database, got error Error 1045 (28000): Access denied for user 'nobody'@'localhost' (using password: YES)"}

最好还是采用 zap 官方设置,比如 NewDevelopmentConfig 和 NewProductionConfig,如果需要的话再在这个基础上修改某些字段的值。

vector remap language

好了,现在日志输出非常规范了,vector 直接可以解析,但是仍需要一些处理,把 json 文本解析成字段方便后面 victoria-logs 统计和显示,如果是自定义格式的日志,就要使用正则匹配,这时候就需要 vrl 了,可以查看官方文档Vector Remap Language (VRL),其中有一些范例可以参考。

bash 复制代码
docker logs vector --tail 50

使用 zap 打印日志比如:

go 复制代码
fields := []zap.Field{
  zap.String("method", c.Request.Method),
  zap.String("path", c.Request.URL.Path),
  zap.String("client_ip", c.ClientIP()),
  zap.String("proto", c.Request.Proto),
}
logger.Error("Vodka Internal Server Error", fields...)

输出的日志格式是:

json 复制代码
{"level":"info","ts":1759286696.6425128,"msg":"Vodka internal Request","method":"GET","path":"/metrics","client_ip":"172.18.0.2","proto":"HTTP/1.1","status":200,"body_size":2544,"latency":2}

编写如下 vrl:

yaml 复制代码
transforms:
  blog_server_json_logs:
    type: remap
    inputs:
      - blog_server_logs
    source: |
      ._msg = .message
      .message = parse_json(.message) ?? {}
      .level = .message.level

得到的 vector 处理后的数据:

json 复制代码
{"_msg":"{\"level\":\"info\",\"ts\":1759288966.6411865,\"msg\":\"Vodka internal Request\",\"method\":\"GET\",\"path\":\"/metrics\",\"client_ip\":\"172.18.0.2\",\"proto\":\"HTTP/1.1\",\"status\":200,\"body_size\":2264,\"latency\":1}","file":"/var/log/blog/blog_backend.log","host":"f33342bfbf69","level":"info","message":{"body_size":2264,"client_ip":"172.18.0.2","latency":1,"level":"info","method":"GET","msg":"Vodka internal Request","path":"/metrics","proto":"HTTP/1.1","status":200,"ts":1759288966.6411865},"source_type":"file","timestamp":"2025-10-01T03:22:47.353385037Z"}

victoria-logs 和 grafana

victoria-logs 本身有一个网页的界面,视觉上蛮舒服的。

victoria-logs

grafana 没有内置 victoria-logs,需要额外下载插件,然后添加 datasource,不需要太多配置,只需设置下 victoria-logs 的地址,效果如下。

grafana-victoria-logs

Elastic+Logstash+Kibana 技术栈可以简称成 ELK,那么 VictoriaLogs+Vector+Grafana 可不可以称作 VVG,不过听起来却是没有 ELK 好听。

相关推荐
weixin_462446234 分钟前
使用 Go 实现 SSE 流式推送 + 打字机效果(模拟 Coze Chat)
开发语言·后端·golang
JIngJaneIL23 分钟前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
小信啊啊1 小时前
Go语言切片slice
开发语言·后端·golang
Victor3563 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易3 小时前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧3 小时前
Range循环和切片
前端·后端·学习·golang
WizLC3 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Victor3563 小时前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法3 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
白宇横流学长4 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端