Logback 整合 RabbitMQ 实现统一日志输出

本文介绍了一种利用RabbitMQ和Logstash实现的日志聚合方案,旨在解决多服务器环境下日志查看困难的问题。通过将各服务器日志信息统一输出至单一日志文件,简化了故障排查流程。

一、前言

    公司项目做了集群实现请求分流,由于线上或多或少会出现请求失败或系统异常,为了查看失败请求的日志信息,我们得将所有服务的日志文件都打开来进行问题的定位分析,操作起来非常麻烦。因此,我们开发组决定设计一套日志查看系统来解决上述问题。

二、实现思路

    默认的,应用服务日志信息会保存在本地服务器的目录中,为了方便查看日志我们应该把多台服务器日志统一输出到一个日志文件中。

    由于项目使用的 Logback 日志框架和 RabbitMQ 消息队列,这两者正好可以进行整合。

    因此,我们可以将项目代码中的日志输出到 RabbitMQ 队列中,通过 Logstash 读取队列数据,最后再输出到一个日志文件中。

三、准备环境

测试环境:IP 为 192.168.2.13 的 CentOS 7 系统

# 3.1 RabbitMQ 配置

    首先需要搭建 RabbitMQ 环境,可以参考本站《CentOS 7.2 安装 RabbitMQ》进行搭建。

    搭建完成后,登录 RabbitMQ 的管理界面,需要操作如下步骤:

  • 创建一个名为 log_queue 的队列
  • 创建一个名为 rabbit.log 的交换器(direct 类型)
  • 将 log_queue 队列绑定到 rabbit.log 交换机上

操作演示图:

# 3.2 Logstash 配置文件

    本站也有 Logstash 相关的博文,读者可移至 《Logstash 基础入门》 查看。

input {

rabbitmq {
type =>"all"
durable => true
exchange => "rabbit.log"
exchange_type => "direct"
key => "info"
host => "192.168.2.13"
port => 5672
user => "light"
password => "light"
queue => "log_queue"
}

}

output {

file {
path => "/usr/test-log/test-%{+YYYY-MM-dd}.log"
codec => multiline {
pattern => "^\d"
negate => true
what => "previous"
}

}
}

注意: multiline 是 Logstash 的插件,需要手动安装。

配置表示 Logstash 服务从 RabbitMQ 读取日志信息,输出到指定的目录文件中。

四、编码

# 4.1 依赖

列出主要依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>

# 4.2 application.properties 文件

spring.rabbitmq.host=192.168.2.13
spring.rabbitmq.port=5672
spring.rabbitmq.username=light
spring.rabbitmq.password=light

# 4.3 日志文件

名为 logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">

<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="d:/" />

<springProperty name="rabbitmqHost" source="spring.rabbitmq.host"/>
<springProperty name="rabbitmqPort" source="spring.rabbitmq.port"/>
<springProperty name="rabbitmqUsername" source="spring.rabbitmq.username"/>
<springProperty name="rabbitmqPassword" source="spring.rabbitmq.password"/>

<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出,%d:日期;%thread:线程名;%-5level:级别,从左显示5个字符宽度;%msg:日志消息;%n:换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>

<appender name="RABBITMQ"
class="org.springframework.amqp.rabbit.logback.AmqpAppender">
<layout>
<pattern><![CDATA[ %d %p %t [%c] - <%m>%n ]]></pattern>
</layout>
<!--rabbitmq地址 -->
<host>${rabbitmqHost}</host>
<port>${rabbitmqPort}</port>
<username>${rabbitmqUsername}</username>
<password>${rabbitmqPassword}</password>
<declareExchange>true</declareExchange>
<exchangeType>direct</exchangeType>
<exchangeName>rabbit.log</exchangeName>
<routingKeyPattern>info</routingKeyPattern>
<generateId>true</generateId>
<charset>UTF-8</charset>
<durable>true</durable>
<deliveryMode>NON_PERSISTENT</deliveryMode>
<autoDelete>false</autoDelete>
</appender>

<logger name="com.light.rabbitmq" level="info" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="RABBITMQ"/>
</logger>

<!-- 日志输出级别,level 默认值 DEBUG,root 其实是 logger,它是 logger 的根 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="RABBITMQ" />
</root>

</configuration>

配置中的 exchangeType 和 exchangeName 就是我们上边创建的交换机的类型和名称。

# 4.4 测试类

自定义异常:


public class CustomException extends RuntimeException{

private static final long serialVersionUID = 1L;

private int code;

private String msg;

public CustomException(int code, String msg) {
super(msg);
this.code = code;
this.msg = msg;
}

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

}

模拟打印日志:

@Component
public class DemoTask {

private static Logger logger = LoggerFactory.getLogger(DemoTask.class);

private int num = 1;

@Scheduled(fixedRate = 3000)
public void writeLog() {

try {
if (num % 5 == 0) {
throw new CustomException(500, "自定义异常错误");
}
logger.info("==={}===={}","hello rabbitmq", System.currentTimeMillis());
num++;
} catch (CustomException e) {
e.printStackTrace();
logger.error("=={}==", e);
}

}
}

五、代码测试

执行启动类:

@EnableScheduling
@SpringBootApplication
public class RabbitmqTestApplication {

public static void main(String[] args) {
SpringApplication.run(RabbitmqTestApplication.class, args);
}

}

执行结果如下图:

代码运行的日志信息已经输出到指定日志文件中了。

补充

由于多台服务器的日志都打印到同一个文件,为了区分日志来源,我们还得需要打印出日志信息对应的主机 ip 地址。具体实现步骤如下:

  • 自定义日志转换器

需要继承 ClassicConverter 类

public class CustomLogConverter extends ClassicConverter {

public String convert(ILoggingEvent event) {

try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return null;
}
}
  • 修改 logback-spring.xml 文件

以下只张贴关键配置信息

<conversionRule conversionWord="ip" converterClass="com.light.rabbitmq.log.CustomLogConverter" />

<appender name="RABBITMQ"
class="org.springframework.amqp.rabbit.logback.AmqpAppender">
<layout>
<pattern><![CDATA[%ip %date{yyyy-MM-dd HH:mm:ss} | %highlight(%-5level) | %yellow(%thread) | %green(%logger) | %msg%n ]]></pattern>
</layout>
</appender>

 

<think>嗯,用户问的是Logback在分布式环境下如何实现统一日志收集的配置和方案。首先,我需要回忆一下Logback的相关知识,以及分布式日志收集的常见方法。根据用户提供的引用,尤其是引用[1]、[2]、[4]和[5],ELK Stack、Fluentd、云服务这些方案都有提到。用户可能希望了解具体的配置步骤和集成方法。 首先,Logback本身支持通过SocketAppender或SSLSocketAppender发送日志到远程服务器,比如Logstash。引用[4]中提到将日志通过Logstash传到Kafka,再传给Elasticsearch,这可能是一个常见的架构。需要配置Logback的appender指向Logstash的地址和端口,同时可能需要添加依赖,比如logstash-logback-encoder。 然后,引用[3]提到了EFK(Elasticsearch+Fluentd+Kibana),使用Fluentd作为日志收集工具。这时候可能需要用Fluentd的插件来接收日志,或者通过Logback的HTTP Appender发送到Fluentd。此外,云服务如AWS CloudWatch也是一个选项,用户可能需要配置对应的Appender,比如AWS提供的SDK。 另外,统一日志格式很重要,引用[5]提到统一日志管理,需要确保所有实例的日志格式一致,可能使用JSON格式方便后续处理。异常处理部分也需要统一,避免直接打印,而是通过日志框架记录。 可能还需要考虑日志缓冲和异步处理,避免网络问题影响应用性能。使用AsyncAppender来异步发送日志,同时设置合理的缓冲大小和超时时间。 用户的问题可能涉及具体步骤,比如如何修改logback.xml,添加哪些依赖,以及如何与不同的中间件集成。需要分步骤说明,比如配置Logstash、Kafka、Elasticsearch等组件的连接方式,以及对应的Logback配置示例。 最后,可能需要提到监控和优化,比如日志索引策略、安全性设置(SSL/TLS),以及如何通过Kibana进行可视化。确保覆盖用户可能关心的点,比如性能、可靠性和扩展性。</think>### Logback分布式环境统一日志收集实现方案 在分布式系统中,实现统一日志收集需结合日志采集工具和中心化存储平台。以下是基于Logback的三种主流方案及详细配置步骤: --- #### 一、核心配置原则 1. **统一日志格式** 所有节点需采用JSON格式输出日志,便于后续解析和检索: ```xml <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> <encoder class="net.logstash.logback.encoder.LogstashEncoder"> <customFields>{"service":"${spring.application.name}"}</customFields> </encoder> </appender> ``` 添加依赖: ```xml <dependency> <groupId>net.logstash.logback</groupId> <artifactId>logstash-logback-encoder</artifactId> <version>7.2</version> </dependency> ``` 2. **异步写入机制** 通过异步Appender防止网络延迟影响主线程: ```xml <appender name="ASYNC_LOGSTASH" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="LOGSTASH"/> <queueSize>1024</queueSize> <discardingThreshold>0</discardingThreshold> </appender> ``` --- #### 二、具体实现方案 ##### 方案1:Logback直连Logstash(ELK方案)[^4] 1. **Logback配置** ```xml <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> <destination>logstash-server:5044</destination> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> </appender> <root level="INFO"> <appender-ref ref="ASYNC_LOGSTASH"/> </root> ``` 2. **Logstash管道配置** ```ruby input { tcp { port => 5044 codec => json_lines } } output { elasticsearch { hosts => ["elasticsearch:9200"] index => "logs-%{+YYYY.MM.dd}" } } ``` ##### 方案2:通过Kafka中转[^4] 1. **Logback配置** 使用Kafka Appender: ```xml <appender name="KAFKA" class="com.github.danielwegener.logback.kafka.KafkaAppender"> <kafkaProducerConfig>bootstrap.servers=kafka:9092</kafkaProducerConfig> <topic>app-logs</topic> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> </appender> ``` 2. **Kafka+Logstash集成** Logstash从Kafka消费日志: ```ruby input { kafka { bootstrap_servers => "kafka:9092" topics => ["app-logs"] } } ``` ##### 方案3:Fluentd中转(EFK方案)[^3] 1. **HTTP Appender配置** ```xml <appender name="FLUENTD" class="ch.qos.logback.access.net.FluentAccessLogger"> <remoteHost>fluentd-server</remoteHost> <port>24224</port> <tag>app.logs</tag> <encoder> <pattern>{"message":"%m","level":"%p","service":"${spring.application.name}"}</pattern> </encoder> </appender> ``` --- #### 三、高级优化措施 1. **日志染色追踪** 集成TraceID实现请求链路追踪: ```java MDC.put("traceId", UUID.randomUUID().toString()); ``` 2. **动态配置管理** 结合Spring Cloud Config实现日志级别热更新: ```yaml logging: config: http://config-server/logback-${spring.profiles.active}.xml ``` 3. **安全传输** 启用TLS加密传输: ```xml <sslConfiguration> <trustStore file="truststore.jks" password="changeit"/> </sslConfiguration> ``` --- #### 四、部署验证流程 1. 启动EFK集群(参考Docker部署[^3]): ```yaml services: fluentd: image: fluent/fluentd:v1.14 volumes: - ./fluent.conf:/fluentd/etc/fluent.conf ``` 2. 日志查询验证: ```bash curl -XGET 'http://kibana:5601/app/discover#/' ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值