Logback 整合 RabbitMQ 實現統一日誌輸出

原文地址:Logback 整合 RabbitMQ 實現統一日誌輸出 博客地址:http://www.extlight.comjava

1、前言

公司項目作了集羣實現請求分流,因爲線上或多或少會出現請求失敗或系統異常,爲了查看失敗請求的日誌信息,咱們得將全部服務的日誌文件都打開來進行問題的定位分析,操做起來很是麻煩。所以,咱們開發組決定設計一套日誌查看系統來解決上述問題。spring

2、實現思路

默認的,應用服務日誌信息會保存在本地服務器的目錄中,爲了方便查看日誌咱們應該把多臺服務器日誌統一輸出到一個日誌文件中。服務器

因爲項目使用的 Logback 日誌框架和 RabbitMQ 消息隊列,這二者正好能夠進行整合。app

所以,咱們能夠將項目代碼中的日誌輸出到 RabbitMQ 隊列中,經過 Logstash 讀取隊列數據,最後再輸出到一個日誌文件中。框架

3、準備環境

測試環境:IP 爲 192.168.2.13 的 CentOS 7 系統spring-boot

3.1 RabbitMQ 配置

首先須要搭建 RabbitMQ 環境,能夠參考本站《CentOS 7.2 安裝 RabbitMQ》進行搭建。測試

搭建完成後,登陸 RabbitMQ 的管理界面,須要操做以下步驟:this

  • 建立一個名爲 log_queue 的隊列
  • 建立一個名爲 rabbit.log 的交換器(direct 類型)
  • 將 log_queue 隊列綁定到 rabbit.log 交換機上

操做演示圖:編碼

3.2 Logstash 配置文件

本站也有 Logstash 相關的博文,讀者可移至 《Logstash 基礎入門》 查看。spa

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"
       auto_delete => false
  }

}

output {

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

    }
}

注意: multiline 是 Logstash 的插件,須要手動安裝。

配置表示 Logstash 服務從 RabbitMQ 讀取日誌信息,輸出到指定的目錄文件中。

4、編碼

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 日誌文件

名爲 logback-spring.xml

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

    <!--定義日誌文件的存儲地址 勿在 LogBack 的配置中使用相對路徑-->  
    <property name="LOG_HOME" value="d:/" />   
    
    <!-- 控制檯輸出 -->     
    <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地址 -->
		<addresses>192.168.2.13:5672</addresses>
		<username>light</username>
		<password>light</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.3 測試類

自定義異常:

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);
		}
		
	}
}

5、代碼測試

執行啓動類:

@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>
相關文章
相關標籤/搜索