SpringBoot不重启修改日志级别【Slf4jj动态日志级别】

SpringBoot不重启修改日志级别【Slf4jj动态日志级别】

前言

需求: 线上日志级别高,而定位问题时需要低级别日志便于分析问题

功能:不重启服务器,提供设置页,手动触发Slf4j 项目日志级别变化

扩展:可将此功能放入后台管理系统中,管理员只需,点选日志级别即可切换服务器的日志级别。
栗子 Like this:
20200519153340287
或者使用命令行

curl 项目访问地址/sys/log/level/error
curl 项目访问地址/sys/log/level/warn
curl 项目访问地址/sys/log/level/info
curl 项目访问地址/sys/log/level/debug
curl 项目访问地址/sys/log/level/trace

查看日志状态
20200519155725394
懒人如何实现?请copy如下代码 - _ -

Controller.java

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.Objects;

/**
 * @ IDE    :IntelliJ IDEA.
 * @ Date   :2019/11/6  19:40
 * @ Desc   :动态修改系统日志等级。
 */
@Slf4j
@RestController
@RequestMapping("/sys/log/level")
public class SysLogLevelController {
    LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();

    /**
     * 测试当前日志等级接口
     * @param packageName           包名
     * @return Result               返回结果
     * 栗子:
     *  curl [baseUrl]/sys/log/level/test
     */
    @GetMapping("/test")
    public Result testLevel(String packageName){
        log.trace("trace");
        log.debug("debug");
        log.info("info");
        log.warn("warn");
        log.error("error");

        StringBuilder msg = new StringBuilder();
        //全局日志等级 + 项目日志等级 + 具体包的日志等级
        msg.append(getLogger("root"));
        msg.append(getLogger(【SpringBoot启动类】.class.getPackage().getName()));
        if (packageName!=null && !packageName.isEmpty()){
            msg.append(getLogger(packageName));
        }
        return Result.getMsgResult(msg.toString());
    }

    /**
     * 修改日志级别接口
     * @param level                 日志级别
     * @return Result               返回结果
     * 栗子:
     *  curl [baseUrl]/sys/log/level/info/[包名]
     *  curl [baseUrl]/sys/log/level/debug/[包名]
     */
    @GetMapping("/{level}/{packageName}")
    public Result changeLevel(@PathVariable String level,@PathVariable String packageName) {
        return Result.getMsgResult(setLogger(packageName,level));
    }

    /**
     * 修改日志级别接口
     * @param level                 日志级别
     * @return Result               返回结果
     * 栗子:
     *  curl [baseUrl]/sys/log/level/info
     *  curl [baseUrl]/sys/log/level/debug
     */
    @GetMapping("/{level}")
    public Result changeLevel(@PathVariable String level) {
        return Result.getMsgResult(setLogger("root",level));
    }


    /**
     * 获取指定包日志级别             封装[设置日志级别+封装返回值信息]
     * @param packageName           包名
     * @return String               日志级别信息
     */
    private String getLogger(String packageName){
        return  packageName + "日志等级为:" + getLevel(packageName);
    }

    /**
     * 设置指定包日志级别             封装[日志级别检测+设置日志级别+封装返回值信息]
     * @param packageName           包名
     * @return String               日志级别信息
     */
    private String setLogger(String packageName,String level){
        boolean isAllowed = isAllowed(level);
        if (isAllowed){
            setLevel(packageName,level);
        }
        return isAllowed
                ? packageName+"日志等级更改为:"+level
                : packageName+"日志等级修改失败,可用值[ERROR,WARN,INFO,DEBUG,TRACE]";
    }

    /**
     * 获取制定包的日志级别
     * @param packageName           包名
     * @return String               日志级别
     */
    private String getLevel(String packageName){
        Logger logger = loggerContext.getLogger(packageName);
        return hasNull(logger,logger.getLevel())
                ? ""
                : logger.getLevel().toString();
    }

    private boolean hasNull(Object... objects) {
        if (Objects.nonNull(objects)) {
            for (Object element : objects) {
                if (null == element) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 设置制定包的日志级别
     * @param packageName           包名
     * @param level                 日志等级
     */
    private void setLevel(String packageName,String level){
        loggerContext.getLogger(packageName).setLevel(Level.toLevel(level));
    }

    //    final String[] levels = {"ERROR","WARN","INFO","DEBUG","TRACE"};
    final String[] levels = {"OFF","FATAL","ERROR","WARN","INFO","DEBUG","TRACE","ALL"};
    /**
     * 判断是否是合法的日志级别
     * @param level                 日志等级
     * @return boolean
     */
    private boolean isAllowed(String level){
        return Arrays.asList(levels).contains(level.toUpperCase());
    }

}

Result.java是web统一返回数据的类,不是必须要的。也可以自己实现。但是这里也提供一下。

Result.java

/**
 * @ IDE    :IntelliJ IDEA.
 * @ Date   :2019/9/27  16:44
 * @ Desc   :web 统一返回工具类
 */
import java.io.Serializable;
import java.time.LocalDateTime;

public class Result<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    private Integer code;
    private String msg;
    private  T data;
    private String time;
    public Result(){
        setTime(LocalDateTime.now().toString());
    }
    public Result(Integer code){
        setCode(code).setTime(LocalDateTime.now().toString());
    }
    public Result(Integer code, String msg){
        setCode(code).setMsg(msg).setTime(LocalDateTime.now().toString());
    }
    public Result(Integer code, String msg, T data){
        setCode(code).setMsg(msg).setData(data).setTime(LocalDateTime.now().toString());
    }
    private static final int SUCCESS_CODE = 200;
    private static final int ERROR_CODE = 500;
    private static final String SUCCESS_MSG = "成功";
    private static final String ERROR_MSG = "失败";
    //成功返回值
    public static Result getSuccessResult() {
        return new Result(SUCCESS_CODE,SUCCESS_MSG);
    }
    public static <T> Result<T> getSuccessResult(T data) {
        return new Result<T>(SUCCESS_CODE,SUCCESS_MSG,data);
    }
    //错误返回值
    public static Result getErrorResult() {
        return new Result(ERROR_CODE,ERROR_MSG);
    }
    public static Result getErrorResult(String msg) {
        return new Result(ERROR_CODE,msg);
    }
    //自定义消息
    public static Result getMsgResult(String msg) {
        return new Result(SUCCESS_CODE,msg);
    }
    public static <T> Result<T> getMsgResult(String msg,T data) {
        return getMsgResult(msg).setData(data);
    }
    //自定义返回Code
    public static Result getCodeResult(Integer code,String msg) {
        return new Result(code,msg);
    }
    public static <T> Result<T> getCodeResult(Integer code,String msg,T data) {
        return getCodeResult(code,msg).setData(data);
    }
    public Integer getCode() {
        return code;
    }
    public Result<T> setCode(Integer code) {
        this.code = code;
        return this;
    }
    public String getMsg() {
        return msg;
    }
    public Result<T> setMsg(String msg) {
        this.msg = msg;
        return this;
    }
    public T getData() {
        return data;
    }
    public Result<T> setData(T data) {
        this.data = data;
        return this;
    }
    public String getTime() {
        return time;
    }
    public Result<T> setTime(String time) {
        this.time = time;
        return this;
    }
}