亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot日志打印實(shí)踐過程

 更新時(shí)間:2024年12月09日 17:20:42   作者:沉河不浮  
文章介紹了如何在SpringBoot項(xiàng)目中使用Logback實(shí)現(xiàn)日志打印,并通過自定義日志打印工具類和日志分文件打印來提高日志排查和監(jiān)控的效率

背景

在項(xiàng)目當(dāng)中,我們經(jīng)常需要打印一些日志埋點(diǎn)信息,這些日志埋點(diǎn)信息,在后續(xù)軟件的運(yùn)維、穩(wěn)定性建設(shè)中發(fā)揮了巨大的作用:

  • 問題追蹤:通過埋點(diǎn)日志中的關(guān)鍵信息,幫助定位系統(tǒng)異常原因
  • 系統(tǒng)監(jiān)控:通過日志,監(jiān)控系統(tǒng)的運(yùn)行情況,包括性能指標(biāo)、訪問頻率、錯(cuò)誤等
  • 數(shù)據(jù)分析:分析用戶行為、系統(tǒng)性能和業(yè)務(wù)趨勢(shì)等
  • 調(diào)試:通過查看日志,幫助開發(fā)人員了解程序在執(zhí)行過程中的狀態(tài)和行為

SpringBoot整合Logback實(shí)現(xiàn)日志打印

SpringBoot默認(rèn)使用Slf4j作為日志門面,并集成Logback作為日志實(shí)現(xiàn)。

要在springboot中實(shí)現(xiàn)日志打印,只需要引入下列依賴:

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

然后在配置文件中,配置對(duì)應(yīng)的日志級(jí)別:

logging:
  level:
    root: INFO

對(duì)某些特定的包,需要指定日志級(jí)別,則配置如下:

logging:
    level:
        com.example.demo: DEBUG

最后,我們創(chuàng)建logback-spring.xml,來自定義日志的配置信息,包括日志輸出文件、日志格式等

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_PATH" value="logs"/>
    <property name="LOG_FILE" value="${LOG_PATH}/spring-boot-logger.log"/>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>common.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/spring-boot-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

然后,我們?cè)谛枰蛴∪罩镜念?,加上Slf4j注解,然后使用log來打印日志信息即可,如下代碼所示:

package com.yang.web.controller;

import com.yang.api.common.ResultT;
import com.yang.api.common.command.RegisterCommand;
import com.yang.api.common.dto.UserDTO;
import com.yang.api.common.facade.UserFacade;
import com.yang.web.request.RegisterRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/user")
@Slf4j
public class UserController {
    @Autowired
    private UserFacade userFacade;

    @GetMapping(value = "/{id}")
    public ResultT<UserDTO> queryById(@PathVariable("id") Integer id) {
        log.info("queryById===========");
        return userFacade.getById(id);
    }

    @PostMapping(value = "/register")
    public ResultT<String> register(@RequestBody RegisterRequest registerRequest) {
        RegisterCommand registerCommand = convert2RegisterCommand(registerRequest);
        return userFacade.register2(registerCommand);
    }

    private RegisterCommand convert2RegisterCommand(RegisterRequest registerRequest) {
        RegisterCommand registerCommand = new RegisterCommand();
        registerCommand.setLoginId(registerRequest.getLoginId());
        registerCommand.setEmail(registerRequest.getEmail());
        registerCommand.setPassword(registerRequest.getPassword());
        registerCommand.setExtendMaps(registerRequest.getExtendMaps());
        return registerCommand;
    }
}

然后訪問queryById,打印結(jié)果如下:

日志打印工具類

在logback-spring.xml中,我們雖然能配置日志打印的格式,但是不夠靈活,因此,我們可以添加一個(gè)日志打印工具類,通過該工具類,來自定義項(xiàng)目中的日志打印格式,以方便后續(xù)更好地通過日志排查、定位問題。

首先創(chuàng)建一個(gè)日志打印抽象類,定義日志打印的格式:

package com.yang.core.infrastructure.log;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

public abstract class AbstractLogPrinter {
    protected String bizCode;
    
    protected List<String> params = new ArrayList<>();

    protected String msg;

    protected Throwable e;

    public AbstractLogPrinter addBizCode(String bizCode) {
        this.bizCode = bizCode;
        return this;
    }

    public AbstractLogPrinter addMsg(String msg) {
        this.msg = msg;
        return this;
    }

    public AbstractLogPrinter addParam(String key, String value) {
        this.params.add(key);
        this.params.add(value);
        return this;
    }

    public AbstractLogPrinter addThrowable(Throwable e) {
        this.e = e;
        return this;
    }

    public abstract void printBizLog();

    public abstract void printErrorLog();

    public abstract String getSeparator();

    public String commonContent() {
        StringBuilder stringBuilder = new StringBuilder();
        String separator = getSeparator();
        stringBuilder.append("bizCode").append(":")
                .append(this.bizCode).append(separator);
        if (!CollectionUtils.isEmpty(params)) {
            for (int i = 0; i < params.size(); i += 2) {
                stringBuilder.append(params.get(i))
                        .append(":")
                        .append(params.get(i + 1))
                        .append(separator);
            }
        }
        if (StringUtils.isNotEmpty(msg)) {
            stringBuilder.append("msg").append(":")
                    .append(msg).append(separator);
        }
        return stringBuilder.toString();
    }
}

然后創(chuàng)建日志打印實(shí)現(xiàn)類,在實(shí)現(xiàn)類中,定制實(shí)現(xiàn)日志打印的級(jí)別、分隔符等內(nèi)容

package com.yang.core.infrastructure.log;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PlatformLogPrinter extends AbstractLogPrinter {

    public void printBizLog() {
        log.info(commonContent());
    }

    public void printErrorLog() {
        if (e != null) {
            log.error(commonContent(), e);
        } else {
            log.error(commonContent());
        }
    }

    @Override
    public String getSeparator() {
        return "<|>";
    }
}

同時(shí),為了方便打印日志,創(chuàng)建一個(gè)日志打印創(chuàng)建者

package com.yang.core.infrastructure.log;

public class PlatformLogger {

    public static AbstractLogPrinter build() {
        return new PlatformLogPrinter();
    }
}

上述內(nèi)容準(zhǔn)備完畢后,我們?cè)赾ontroller中,使用PlatformLogger來打印日志,修改后的代碼如下:

package com.yang.web.controller;

import com.yang.api.common.ResultT;
import com.yang.api.common.command.RegisterCommand;
import com.yang.api.common.dto.UserDTO;
import com.yang.api.common.facade.UserFacade;
import com.yang.core.infrastructure.log.PlatformLogger;
import com.yang.web.request.RegisterRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/user")
public class UserController {
    @Autowired
    private UserFacade userFacade;

    @GetMapping(value = "/{id}")
    public ResultT<UserDTO> queryById(@PathVariable("id") Integer id) {
        PlatformLogger.build()
                .addBizCode("queryById")
                .addParam("id", id.toString())
                .addMsg("query by id")
                .printBizLog();
        return userFacade.getById(id);
    }
    
    @GetMapping(value = "/error/{id}")
    public ResultT testError(@PathVariable("id") Integer id) {
        try {
            int i = 1 / 0;
        } catch (Throwable t) {
            PlatformLogger.build()
                    .addBizCode("testError")
                    .addParam("id", id.toString())
                    .addMsg("test error print")
                    .addThrowable(t)
                    .printErrorLog();
        }
        return ResultT.fail();
    }

    @PostMapping(value = "/register")
    public ResultT<String> register(@RequestBody RegisterRequest registerRequest) {
        RegisterCommand registerCommand = convert2RegisterCommand(registerRequest);
        return userFacade.register2(registerCommand);
    }

    private RegisterCommand convert2RegisterCommand(RegisterRequest registerRequest) {
        RegisterCommand registerCommand = new RegisterCommand();
        registerCommand.setLoginId(registerRequest.getLoginId());
        registerCommand.setEmail(registerRequest.getEmail());
        registerCommand.setPassword(registerRequest.getPassword());
        registerCommand.setExtendMaps(registerRequest.getExtendMaps());
        return registerCommand;
    }
}

啟動(dòng)項(xiàng)目,分別訪問queryById和testError,打印日志內(nèi)容如下:

日志分文件打印

一般情況下,我們的項(xiàng)目會(huì)分為不同的模塊,每一個(gè)模塊承擔(dān)不同的職責(zé),比如bussiness模塊,主要是負(fù)責(zé)業(yè)務(wù)邏輯代碼的實(shí)現(xiàn),業(yè)務(wù)邏輯編排等;web模塊主要負(fù)責(zé)http請(qǐng)求的接收,參數(shù)的校驗(yàn),入?yún)⑥D(zhuǎn)化為業(yè)務(wù)層入?yún)⒌?;而core模塊主要負(fù)責(zé)基礎(chǔ)能力實(shí)現(xiàn),比如持久化數(shù)據(jù)庫、領(lǐng)域服務(wù)實(shí)現(xiàn)等。

對(duì)于不同的模塊,我們希望將日志輸出到不同的文件當(dāng)中,從而協(xié)助我們后續(xù)定位問題以及建設(shè)不同模塊下的監(jiān)控,包括基礎(chǔ)服務(wù)監(jiān)控、業(yè)務(wù)成功率監(jiān)控等。

因此,我們?cè)诓煌哪K下,分別實(shí)現(xiàn)不同的日志打印工具類:

package com.yang.web.log;

import com.yang.core.infrastructure.log.AbstractLogPrinter;

public class WebLogger {
    public static AbstractLogPrinter build() {
        return new WebLogPrinter();
    }
}


package com.yang.web.log;

import com.yang.core.infrastructure.log.AbstractLogPrinter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class WebLogPrinter extends AbstractLogPrinter {
    @Override
    public void printBizLog() {
        log.info(commonContent());
    }

    @Override
    public void printErrorLog() {
        if (this.e != null) {
            log.error(commonContent(), e);
        } else {
            log.error(commonContent());
        }
    }

    @Override
    public String getSeparator() {
        return "<|>";
    }
}


package com.yang.business.log;

public class BusinessLogger {
    public static BusinessLogPrinter build() {
        return new BusinessLogPrinter();
    }
}


package com.yang.business.log;

import com.yang.core.infrastructure.log.AbstractLogPrinter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BusinessLogPrinter extends AbstractLogPrinter {
    @Override
    public void printBizLog() {
        log.info(commonContent());
    }

    @Override
    public void printErrorLog() {
        if (this.e != null) {
            log.error(commonContent(), e);
        } else {
            log.error(commonContent());
        }
    }

    @Override
    public String getSeparator() {
        return "<|>";
    }
}

然后我們修改logback-spring.xml文件,將不同的日志打印工具類,輸出到不同的日志文件中

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_PATH" value="logs"/>
    <property name="LOG_FILE" value="${LOG_PATH}/spring-boot-logger.log"/>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>common.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/spring-boot-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="PLATFORM_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>platform.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/platform-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="BUSINESS_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>business.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/business-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="WEB_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>web.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/web-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>

    <!-- 工具類PlatformLogPrinter的logger -->
    <logger name="com.yang.core.infrastructure.log.PlatformLogPrinter" level="INFO" additivity="false">
        <appender-ref ref="PLATFORM_FILE" />
    </logger>

    <!-- 工具類BusinessLogPrinter的logger -->
    <logger name="com.yang.business.log.BusinessLogPrinter" level="INFO" additivity="false">
        <appender-ref ref="BUSINESS_FILE" />
    </logger>

    <!-- 工具類WebLogPrinter的logger -->
    <logger name="com.yang.web.log.WebLogPrinter" level="INFO" additivity="false">
        <appender-ref ref="WEB_FILE" />
    </logger>
</configuration>

最后,分別在web模塊、business模塊和core模塊下,添加埋點(diǎn)日志

// WEB模塊
@GetMapping(value = "/{id}")
    public ResultT<UserDTO> queryById(@PathVariable("id") Integer id) {
        WebLogger.build()
                .addBizCode("userController_queryById")
                .addParam("id", id.toString())
                .addMsg("query by id")
                .printBizLog();
        return userFacade.getById(id);
    }

// Business模塊
@Override
    public ResultT<UserDTO> getById(Integer id) {
        UserQueryDomainRequest userQueryDomainRequest = new UserQueryDomainRequest.UserQueryDomainRequestBuilder()
                .queryMessage(id.toString())
                .userQueryType(UserQueryType.ID)
                .build();
        UserQueryDomainResponse userQueryDomainResponse = userDomainService.query(userQueryDomainRequest);
        List<UserAccount> userAccountList = userQueryDomainResponse.getUserAccountList();
        UserDTO userDTO = null;
        if (!CollectionUtils.isEmpty(userAccountList)) {
            UserAccount userAccount = userAccountList.get(0);
            userDTO = userDTOConvertor.convert2DTO(userAccount);
        }
        BusinessLogger.build()
                .addBizCode("userFacade_getById")
                .addParam("id", id.toString())
                .addParam("userDTO", JSONObject.toJSONString(userDTO))
                .addMsg("get by id")
                .printBizLog();
        return ResultT.success(userDTO);
    }


// core模塊
public UserQueryDomainResponse query(UserQueryDomainRequest userQueryDomainRequest) {
        UserQueryType userQueryType = userQueryDomainRequest.getUserQueryType();
        UserDO userDO = null;
        switch (userQueryType) {
            case ID:
                userDO = queryById(Integer.valueOf(userQueryDomainRequest.getQueryMessage()));
                break;
            case EMAIL:
                userDO = queryByEmail(userQueryDomainRequest.getQueryMessage());
                break;
            case LOGIN_ID:
                userDO = queryByLoginId(userQueryDomainRequest.getQueryMessage());
                break;
        }
        if (userDO == null) {
            return new UserQueryDomainResponse();
        }
        UserAccount userAccount = new UserAccount();
        userAccount.setId(userDO.getId());
        userAccount.setLoginId(userDO.getLoginId());
        userAccount.setEmail(userDO.getEmail());
        userAccount.setFeatureMap(FeatureUtils.convert2FeatureMap(userDO.getFeatures()));
        userAccount.setCreateTime(userDO.getCreateTime());
        userAccount.setUpdateTime(userDO.getUpdateTime());
        UserQueryDomainResponse userQueryDomainResponse = new UserQueryDomainResponse();
        List<UserAccount> userAccounts = new ArrayList<>();
        userAccounts.add(userAccount);
        userQueryDomainResponse.setUserAccountList(userAccounts);

        PlatformLogger.build()
                .addBizCode("userDomainService_query")
                .addParam("queryMsg", userQueryDomainRequest.getQueryMessage())
                .addParam("queryType", userQueryDomainRequest.getUserQueryType().name())
                .printBizLog();
        return userQueryDomainResponse;
    }

啟動(dòng)項(xiàng)目,訪問queryById接口,可以看到在web.log,business.log和platform.log下分別打印了不同的日志信息

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解決Nacos集群?jiǎn)?dòng)失敗:java版本問題

    解決Nacos集群?jiǎn)?dòng)失敗:java版本問題

    這篇文章主要介紹了解決Nacos集群?jiǎn)?dòng)失敗:java版本問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • SpringBoot實(shí)現(xiàn)文件下載的限速功能

    SpringBoot實(shí)現(xiàn)文件下載的限速功能

    在SpringBoot項(xiàng)目中,實(shí)現(xiàn)文件下載的限速功能可以有效控制服務(wù)器帶寬的占用,并防止單個(gè)用戶消耗過多的資源,本文將通過具體的代碼示例和詳細(xì)的流程解釋,介紹如何在SpringBoot項(xiàng)目中實(shí)現(xiàn)文件下載的限速功能,需要的朋友可以參考下
    2024-07-07
  • Java泛型與數(shù)據(jù)庫應(yīng)用實(shí)例詳解

    Java泛型與數(shù)據(jù)庫應(yīng)用實(shí)例詳解

    這篇文章主要介紹了Java泛型與數(shù)據(jù)庫應(yīng)用,結(jié)合實(shí)例形式詳細(xì)分析了java繼承泛型類實(shí)現(xiàn)增刪改查操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-08-08
  • JAVA偏向鎖的原理與實(shí)戰(zhàn)

    JAVA偏向鎖的原理與實(shí)戰(zhàn)

    這篇文章主要為大家詳細(xì)介紹了JAVA偏向鎖的原理與實(shí)戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • Java 中一個(gè)類提供一個(gè)默認(rèn)對(duì)象的多種方法

    Java 中一個(gè)類提供一個(gè)默認(rèn)對(duì)象的多種方法

    這篇文章主要介紹了Java 中一個(gè)類提供一個(gè)默認(rèn)對(duì)象的多種方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • 詳述 DB2 分頁查詢及 Java 實(shí)現(xiàn)的示例

    詳述 DB2 分頁查詢及 Java 實(shí)現(xiàn)的示例

    本篇文章主要介紹了詳述 DB2 分頁查詢及 Java 實(shí)現(xiàn)的示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的定時(shí)器代碼解析

    Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的定時(shí)器代碼解析

    這篇文章主要介紹了Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的定時(shí)器代碼解析,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • Java8新特性:lambda表達(dá)式總結(jié)

    Java8新特性:lambda表達(dá)式總結(jié)

    這篇文章主要介紹了Java8新特性:lambda表達(dá)式總結(jié),本文總結(jié)了多種語法格式和使用方法,包含了函數(shù)式接口和內(nèi)置的四大核心函數(shù)式接口的用法實(shí)例,需要的朋友可以參考下
    2021-06-06
  • Java中的異步與線程池解讀

    Java中的異步與線程池解讀

    這篇文章主要介紹了Java中的異步與線程池,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • java編程實(shí)現(xiàn)獲取服務(wù)器IP地址及MAC地址的方法

    java編程實(shí)現(xiàn)獲取服務(wù)器IP地址及MAC地址的方法

    這篇文章主要介紹了java編程實(shí)現(xiàn)獲取機(jī)器IP地址及MAC地址的方法,實(shí)例分析了Java分別針對(duì)單網(wǎng)卡及多網(wǎng)卡的情況下獲取服務(wù)器IP地址與MAC地址的相關(guān)技巧,需要的朋友可以參考下
    2015-11-11

最新評(píng)論