前言:
本章主要在原有項目上添加了日志配置,對SpringBoot默認的logback的配置進行了自定義修改,并詳細闡述了xml文件配置要點(只對日志配置感興趣的小伙伴可選擇直接跳到第三節),并使用Feign代替原有RestTemplate完成微服務間調用,以及通過修改Feign的日志輸出介紹了Feign配置的修改。
本章代碼基于第4章項目,前置源碼可在第4章博客下載,博客鏈接如下:
從零搭建微服務項目(第4章——Nacos環境隔離和配置拉取)-CSDN博客https://blog.csdn.net/wlf2030/article/details/145532798?sharetype=blogdetail&sharerId=145532798&sharerefer=PC&sharesource=wlf2030&spm=1011.2480.3001.8118簡要介紹前置項目流程:order-service以及user-service兩服務分別連接order-db以及user-db兩數據庫,order-db中僅有user-id,user-info存在user-db中,為提供完整order-info,order-service通過nacos發現user-service服務地址并使用RestTemplate調用其端口拿取user-info結合從order-db中拿取的信息返回給前端。
本項目源碼鏈接如下:
wlf728050719/SpringCloudBase5https://github.com/wlf728050719/SpringCloudBase5以及本專欄會持續更新微服務項目,每一章的項目都會基于前一章項目進行功能的完善,歡迎小伙伴們關注!同時如果只是對單章感興趣也不用從頭看,只需下載前一章項目即可,每一章都會有前置項目準備部分,跟著操作就能實現上一章的最終效果,當然如果是一直跟著做可以直接跳過這一部分。
一、前置項目準備
1.從github下載前一章的項目解壓,重命名為Base5打開。
2.重命名模塊為Base5
3.父工程pom.xml中<name>改成Base5,
4.選擇環境為dev,并重新加載maven
5.啟動nacos(安裝和啟動見第三章)
6.進入nacos網頁 配置管理->配置列表確認有這些yaml文件
(如果不是一直跟著專欄做自然是沒有的,需要看第四章的環境隔離和配置拉取,記得把父工程pom文件中namespace的值與nacos中命名空間生成的保持一致)
7.配置數據源,更換兩服務的resources下yml文件的數據庫配置,數據庫sql見第一章數據庫準備部分。
.測試數據庫連接 屬性->點擊數據源->測試連接->輸入用戶名密碼
8.添加運行配置 服務->加號->運行配置類型->spring boot
啟動服務
測試order-service
測試user-service
二、Feign基本使用
1.order-service的pom中添加feign依賴(建議使用下面release版本)
版本不對,可能會在啟動order-service時出現如下報錯,即找不到org.springframework.cloud.openfeign.FeignClientFactory的Bean
2.order-service的application開啟feign客戶端注解,并刪除掉原有RestTemplate的bean
替換后內容如下:
package cn.bit.orderservice;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication
@MapperScan("cn.bit.orderservice.mapper")
@EnableFeignClients
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}
}
3.創建client接口
接口內容如下:能夠看出feign即通過把url封裝為接口方便調用,可以理解為每個FeignClient即對應一個微服務,但通過封裝讓業務看起來像是服務的內部調用。
package cn.bit.orderservice.client;import cn.bit.orderservice.bean.dto.UserBaseInfoDTO;
import cn.bit.orderservice.bean.vo.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient("user-service")
public interface UserClient {@GetMapping("/user/baseInfo/{id}")R<UserBaseInfoDTO> getUserBaseInfo(@PathVariable("id") Integer id);
}
4.更改order-service的serviceImpl實現
原有調用方式如下:(通過url調用服務)
替換后內容如下:(通過定義好的feignclient調用服務)
package cn.bit.orderservice.service.impl;import cn.bit.orderservice.bean.dto.UserBaseInfoDTO;
import cn.bit.orderservice.bean.po.OrderPO;
import cn.bit.orderservice.bean.vo.OrderInfoVO;
import cn.bit.orderservice.client.UserClient;
import cn.bit.orderservice.mapper.OrderMapper;
import cn.bit.orderservice.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate UserClient userClient;@Overridepublic OrderInfoVO getOrderInfoById(int orderId) {OrderPO orderPO = orderMapper.getOrderPOById(orderId);if (orderPO == null)return null;OrderInfoVO orderInfoVO = new OrderInfoVO();orderInfoVO.setOrderId(orderPO.getId());orderInfoVO.setAmount(orderPO.getAmount());orderInfoVO.setCreateTime(orderPO.getCreateTime());orderInfoVO.setGetLocation(orderPO.getGetLocation());try {// 獲取買家信息UserBaseInfoDTO userBaseInfo1 = userClient.getUserBaseInfo(orderPO.getBuyerId()).getData();orderInfoVO.setBuyerName(userBaseInfo1.getUsername());// 獲取賣家信息UserBaseInfoDTO userBaseInfo2 = userClient.getUserBaseInfo(orderPO.getSellerId()).getData();orderInfoVO.setSellerName(userBaseInfo2.getUsername());} catch (Exception e) {System.out.println("user-service connect error");e.printStackTrace();}return orderInfoVO;}
}
5.啟動服務后測試
關閉user-service,無法獲取用戶信息且控制臺有異常輸出。
三、日志配置
為體現feign配置需要引入日志輸出,之前的控制臺的彩色輸出是因為Spring Cloud默認日志實現是LogBack,其自動引入了對應的依賴,并有一套默認的配置。
如果需要自行配置日志格式,操作如下:
在order-service的resources目錄下創建 logback-spring.xml文件,內容如下:
(本節最后有更完善的配置xml,本xm僅用于了解日志配置相關知識)
<?xml version="1.0" encoding="UTF-8"?>
<configuration><property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${LOG_PATTERN}</pattern></encoder></appender><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>logs/order-service.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>logs/order-service.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder><pattern>${LOG_PATTERN}</pattern></encoder></appender><root level="info"><appender-ref ref="CONSOLE"/><appender-ref ref="FILE"/></root><logger name="cn.bit.orderservice" level="debug" additivity="false"><appender-ref ref="CONSOLE"/></logger>
</configuration>
其中LOG_PATTERN屬性定義一種日志輸出的格式
(1)appender name=console指定控制臺輸出采用哪種格式,此處采用上述定義的格式。
(2)appender name=file 指定日志文件記錄的目錄以及輸出的格式。
(3)appender的name的命名隨意,只要對應即可,關鍵實現是其后面的class屬性決定了功能
(4)root level=info指定了根日志級別是info級別,除了所有被指定設置的logger其他所有日志級別在info及以上的都會執行console以及file,即控制臺打印加日志文件記錄。
(5)logger通過name指定cn.bit.orderservice包下日志級別為debug即以上的日志語句會打印在控制臺。且設置其不會傳播到父logger,其父logger為根,因此debug日志只會被控制臺輸出而不會被記錄在日志文件中。
日志嚴重等級從高到低如下:
????????OFF:關閉所有日志記錄。
????????FATAL:記錄嚴重錯誤事件,這些事件可能導致程序中斷。
????????ERROR:記錄錯誤事件,但不會導致程序中斷。
????????WARN:記錄潛在有害的情況。
????????INFO:記錄一般信息,用于描述程序運行過程中的關鍵事件。
????????DEBUG:記錄詳細的調試信息,用于診斷問題。
????????TRACE:記錄最詳細的調試信息,用于跟蹤程序執行過程。
### 驗證開始(該部分可自行選擇跳過)
1.在order-service上添加上述xml文件,user-service不添加即使用默認配置
2.在order-controller的接口添加一句log的各種輸出;會自動添加@Slf4j注解
3.啟動服務,并調用一次接口,能夠看到user-service的輸出為彩色,order-service的輸出為白色。說明每個模塊用不同的配置(按照項目目前結構需要每個模塊單獨寫log配置文件,后續有網關模塊即可統一配置)
4.根目錄有生成的logs目錄和對應日志文件,驗證上述(2)。
5.對比控制臺輸出和日志文件末尾,只有控制臺記錄了debug,info,warning,驗證上述(4)(5)。
這時候肯定就有小伙伴有疑問,既然cn.bit.orderservice包已經被設定了只輸出不記錄為什么log文件里還有那么多記錄。這時就會發現我著重加粗的是日志語句,不是從這個包輸出的日志信息被單獨配置了,而是這個包定義的日志語句被單獨配置。這么說似乎還是有點抽象,下面來用案例說明。
用ctrl+左鍵跳轉到這個HikariDataSource類,這是導入的類,不是項目編寫的。
其構造函數中有logger的輸出語句,也對應了上述的控制臺輸出
這時后在logback的配置文件為該類單獨配置,即這個日志什么也不干。
然后刪除剛才生成的log目錄,再重新啟動調用接口。前后對比就會發現上面的日志在控制臺和文件中都不見了。
我個人的理解是,在編譯時,編譯器根據配置文件中特殊配置的logger,找到對應的類,掃描其所有logger,然后或面向切面或代理模式對這些logger進行功能增強,其他未特殊配置的類的logger就是默認的功能。不過只是個人理解,具體實現還得扒源碼。
6.把logback.xml替換成下面內容, 即console變成console1,格式變成年月日,然后日志路徑改成log12345
<?xml version="1.0" encoding="UTF-8"?>
<configuration><property name="LOG_PATTERN" value="%d{yyyy年MM月dd日 HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/><appender name="CONSOLE1" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${LOG_PATTERN}</pattern></encoder></appender><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>logs12345/order-service.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>logs/order-service.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder><pattern>${LOG_PATTERN}</pattern></encoder></appender><root level="info"><appender-ref ref="CONSOLE1"/><appender-ref ref="FILE"/></root><logger name="cn.bit.orderservice" level="debug" additivity="false"><appender-ref ref="CONSOLE1"/></logger><logger name="com.zaxxer.hikari.HikariDataSource" level="info" additivity="false"></logger>
</configuration>
7.啟動后輸出,驗證上述(1)(2)(3)。
### 驗證結束
這時候會有人問,博主博主,你的日志xml講解確實很多但還是太吃理解了,有沒有更簡單強勢的xml推薦呢?
有的,兄弟!(bushi)
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false"><property name="logLevel" value="INFO"/><property name="logPath" value="logs/order-service"/><property name="maxHistory" value="60"/><property name="queueSize" value="512"/><!-- 彩色日志格式 --><property name="CONSOLE_LOG_PATTERN"value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/><conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/><conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/><conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/><!-- 控制臺輸出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${CONSOLE_LOG_PATTERN}</pattern></encoder></appender><!-- DEBUG 日志文件輸出 --><appender name="FILE_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logPath}/debug.log</file><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>${logPath}/debug-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern><maxFileSize>128MB</maxFileSize><maxHistory>${maxHistory}</maxHistory><totalSizeCap>10GB</totalSizeCap></rollingPolicy><encoder><pattern>%date [%thread] %-5level [%logger{50}] - %msg%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>DEBUG</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- INFO 日志文件輸出 --><appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logPath}/info.log</file><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>${logPath}/info-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern><maxFileSize>128MB</maxFileSize><maxHistory>${maxHistory}</maxHistory><totalSizeCap>10GB</totalSizeCap></rollingPolicy><encoder><pattern>%date [%thread] %-5level [%logger{50}] - %msg%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- WARN 日志文件輸出 --><appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logPath}/warn.log</file><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>${logPath}/warn-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern><maxFileSize>128MB</maxFileSize><maxHistory>${maxHistory}</maxHistory><totalSizeCap>10GB</totalSizeCap></rollingPolicy><encoder><pattern>%date [%thread] %-5level [%logger{50}] - %msg%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>WARN</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- ERROR 日志文件輸出 --><appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logPath}/error.log</file><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><fileNamePattern>${logPath}/error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern><maxFileSize>128MB</maxFileSize><maxHistory>${maxHistory}</maxHistory><totalSizeCap>10GB</totalSizeCap></rollingPolicy><encoder><pattern>%date [%thread] %-5level [%logger{50}] - %msg%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 異步日志記錄 --><appender name="ASYNC_LOG_DEBUG" class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold><queueSize>${queueSize}</queueSize><neverBlock>true</neverBlock><appender-ref ref="FILE_DEBUG"/></appender><appender name="ASYNC_LOG_INFO" class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold><queueSize>${queueSize}</queueSize><neverBlock>true</neverBlock><appender-ref ref="FILE_INFO"/></appender><appender name="ASYNC_LOG_WARN" class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold><queueSize>${queueSize}</queueSize><neverBlock>true</neverBlock><appender-ref ref="FILE_WARN"/></appender><appender name="ASYNC_LOG_ERROR" class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold><queueSize>${queueSize}</queueSize><neverBlock>true</neverBlock><appender-ref ref="FILE_ERROR"/></appender><root level="${logLevel}"><appender-ref ref="STDOUT"/><appender-ref ref="ASYNC_LOG_DEBUG"/><appender-ref ref="ASYNC_LOG_INFO"/><appender-ref ref="ASYNC_LOG_WARN"/><appender-ref ref="ASYNC_LOG_ERROR"/></root><logger name="cn.bit.orderservice" level="debug" additivity="false"><appender-ref ref="STDOUT"/><appender-ref ref="ASYNC_LOG_DEBUG"/><appender-ref ref="ASYNC_LOG_INFO"/><appender-ref ref="ASYNC_LOG_WARN"/><appender-ref ref="ASYNC_LOG_ERROR"/></logger></configuration>
使用時只需要注意下面兩點:
全局日志等級,這里設置為info,在沒有特殊指定的情況下會把info及以上等級日志輸出和輸出到文件中。
單獨配置項目文件即cn.bit.orderservice包中的日志,這里相等于把等級下調到了debug。
這些在控制臺和日志文件中就會有導入包的info及以上和項目中的debug及以上信息。
其實在nacos的yml或本地application.yml文件中也能使用logger.level對單獨的包進行配置,但似乎并不能設置additivity屬性阻止其傳遞給父logger.配置前綴如下:
四、Feign自定義配置
1.在order-service下創建配置類
類內容如下:(這里對feign的日志輸出進行配置)
package cn.bit.orderservice.config;import org.springframework.context.annotation.Bean;
import feign.Logger;
public class FeignConfiguration {@Beanpublic Logger.Level feignLoggerLevel() {return Logger.Level.FULL;}
}
2.在order-service的application上啟動配置(這種寫法會使該模塊下所有client都默認使用該配置)
或在UserClient上單獨添加配置,具體如下:(因為后面會有多個client擴充,因此項目采用全局配置)
3.在前面日志配置好的情況下啟動。
能夠看到詳細的feign信息。
換成basic
僅輸出基本信息
最后:
很抱歉在第五章才加上日志配置的部分,這部分其實應該放在第0章,畢竟日志的重要性不言而喻。不過目前日志配置仍然是每個模塊配置一個,實際在網關模塊學習后只需要配置一個,并且能夠通過使用@{project.artifactId}來使每個模塊的日志輸出到不同目錄,目前仍然需要手動指定每個模塊的輸出路徑。日志配置也會在后面的章節有所更改。