JAVA也能用上Seq啦

前言

在.NET生态中,Serilog 凭借其强大的结构化日志记录功能和与Seq的无缝集成,已经成为许多开发者的首选日志记录工具。Seq 作为一个日志检索和仪表板工具,能够将日志中的插值转换为结构化数据,极大地方便了开发者快速检索日志、定位问题并进行简单的统计分析。这种便捷性让人难以割舍。

背景

最近需要搞一个JAVA项目,当开发环境转移到Java生态,尤其是采用Spring Boot框架时,许多开发者发现自己不得不面对一个新的挑战。Spring Boot的默认日志框架Logback,在处理日志结构化方面并不如Serilog那样给人以深刻印象。习惯了Seq带来的便捷,我自然希望在Java环境中也能找到类似的解决方案。

幸运的是,Seq提供了通过GELF(GrayLog Extended Log Format)接收日志的能力,这为Java生态中的日志结构化提供了可能。在Spring Boot 3.2中,通过引入logback-gelf的JAR包,开发者可以实现将日志以GELF格式通过UDP发送到Seq,尽管这种方式对结构化支持并不是非常友好。

前置安装

  • 安装Seq & GELF服务端

     
      
    version: '3'
    services:
    seq-input-gelf:
    image: datalust/seq-input-gelf:latest
    depends_on:
    - seq
    ports:
    - "12201:12201/udp"
    environment:
    SEQ_ADDRESS: "http://seq:5341"
    restart: unless-stopped
    seq:
    image: datalust/seq:latest
    ports:
    - "5341:80"
    environment:
    ACCEPT_EULA: Y
    restart: unless-stopped
    volumes:
    - ./seq-data:/data
  • 安装Gelf Input

image

Spring boot 3 配置

  • 安装logback-gelf包(gradle)

     
      
    implementation 'de.siegmar:logback-gelf:6.0.0'
  • 在classpath处添加 logback-spring.xml

     
      
    <configuration>
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    <!-- 设置队列的最大容量,默认值为 256 -->
    <queueSize>512</queueSize>
    <!-- 设置当队列满时是否丢弃新的日志事件,默认为 false -->
    <discardingThreshold>0</discardingThreshold>
    <!-- 引用其他的 appender,例如控制台 appender -->
    <appender-ref ref="GELF" />
    </appender>
    <appender name="Console"
    class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <Pattern>
    [%white(%d{HH:mm:ss.SSS}) %highlight(%-5level)] [%blue(%t)] %yellow(%C{1}): %msg%n%throwable
    </Pattern>
    </encoder>
    </appender>
    <appender name="GELF" class="de.siegmar.logbackgelf.GelfUdpAppender">
    <!--GELF Server-->
    <graylogHost>{{替换为Seq-Input-GELF的UDP的IP}}</graylogHost>
    <graylogPort>12201</graylogPort>
    <maxChunkSize>508</maxChunkSize>
    <compressionMethod>GZIP</compressionMethod>
    <messageIdSupplier class="de.siegmar.logbackgelf.MessageIdSupplier"/>
    <encoder class="de.siegmar.logbackgelf.GelfEncoder">
    <includeRawMessage>false</includeRawMessage>
    <includeKeyValues>true</includeKeyValues>
    <includeMarker>false</includeMarker>
    <includeMdcData>true</includeMdcData>
    <includeCallerData>false</includeCallerData>
    <includeRootCauseData>false</includeRootCauseData>
    <includeLevelName>false</includeLevelName>
    <shortMessageLayout class="ch.qos.logback.classic.PatternLayout">
    <pattern>%msg%n</pattern>
    </shortMessageLayout>
    <fullMessageLayout class="ch.qos.logback.classic.PatternLayout">
    <pattern>%msg%n</pattern>
    </fullMessageLayout>
    <numbersAsString>false</numbersAsString>
    <!--增加app_name 区分服务-->
    <staticField>app_name:java-demo</staticField>
    <!--<staticField>os_arch:${os.arch}</staticField>-->
    <!--<staticField>os_name:${os.name}</staticField>-->
    <!--<staticField>os_version:${os.version}</staticField>-->
    </encoder>
    </appender>
    <!-- LOG everything at INFO level -->
    <root level="info">
    <appender-ref ref="ASYNC" />
    <appender-ref ref="Console" />
    </root>
    </configuration>

完成上面简单配置,启动项目,如果配置没问题运气好的话在Seq上会看到日志,从配置上看GELF采用异步Appender写入,并有队列缓冲区限制,所以不用考虑性能问题,但存在一个问题,当你用插值写入时Seq并未将插值转化为K/V形式保存,解下来解决下这个。

实现插值格式化写入

熟悉Serilog的朋友们都知道,在.Net平台上记录日志是可以这样的

 
// Seq 会保存一个{"lang":'C#'}的 K/V
logger.LogInformation("Hello {lang}","C#");
// Serilog在格式化时会把config序列化
logger.LogInformation("Web config: {@config}",config);

这个还是很优雅的,反观slf4j ,只能使用{},在Seq上怎么传入Key呢,笔者想到的是自己做个约定把。
可以在写日志时对于参数化format约定为"[边界][参数key]={}"。举个例子

 
Logger logger = LoggerFactory.getLogger(LogseqsampleApplication.class);
logger.info("Hello {}","World");
logger.info("Hello world={}","World");
logger.info("log config={}",Map.of("k1","v1"));
logger.info("log config={}",Map.of("key",new String[]{"value1","value2"}));
logger.info("log config={}",Map.of("key",Map.of("k1","v1")));

可以看出 config或者world参数前都有空格,即边界符。
边界符可以为以下字符

 
private static final char[] Delimiter = {',',' ','.','。'};

看起来还不错,既然我们定义了一些默认规则,那就只能自己写解析了,翻看下源码,发现可以在Encoder这里做重写,不错。

实现Encoder

直接给个传送门com.leesiper.logseqsample.utils.SeqEncoder中找到,可自行查看,有了Encoder,
替换下logback-spring.xml中的

 
<encoder class="de.siegmar.logbackgelf.GelfEncoder">
替换为(可自行调整class)
<encoder class="com.leesiper.logseqsample.utils.SeqEncoder">

再次启动项目,看看Seq中是不是写入了日志,展开后会看到参数化转换成了K/V,可直接进行检索了。Seq不像ELasticsearch,写入新的字段后需要重建索引,Seq是动态创建的,底层是自主研发的,有着简单易用易部署的特点,确实不错

image

这一发现对于习惯了.NET生态中Serilog和Seq搭配使用的开发者来说,无疑是一个好消息。它意味着,即使在转向Java生态时,也无需放弃熟悉的日志记录习惯和工具。通过适当的配置和一些定制化开发,开发者可以在Spring Boot项目中实现与.NET生态相似的日志记录体验,继续享受快速检索和日志问题定位的便利。

总之,通过探索和创新,Java生态中的开发者同样能够享受到Serilog和Seq带来的高效日志处理体验。这不仅展示了技术跨界整合的可能性,也再次证明了开发者社区在面对挑战时不断探索和创新的精神。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野生的狒狒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值