spring boot log4j2 自定义级别日志并存储,超详细

由于需要一些业务日志,本来是用的注解,然后用spring aop获取注解的形式来记录,但是由于最开始的时候没有统一controller 方法的参数,参数数据,细致到id不太好记录。于是想到了log4j的形式存储数据库,但log4j的形式记录会记录所有级别的日志,即使指定日志级别,其他框架里面的同级别日志也会记录,很混乱。于是想到了自定义级别来记录存储,这样就解决了其他框架同级别的日志不会同时存储,前台要确定其他的框架没有相同级别的日志。比如我自定义的级别是OPERATING 级别是350

controller方法代码:

@RequestMapping("/exportPage/")
	@ResponseBody
	@MethodLog
	public void exportPage(HttpServletRequest request,HttpServletResponse response,@RequestParam("page")Integer page,@RequestParam("startDate")String startDate,@RequestParam("endDate")String endDate) throws Exception{
		Page<FinanceIncomeEntity> p=new Page<FinanceIncomeEntity>();
		p.setCurrentPage(page);
		ExcelUtils.setResponse(response, service.exportPage(p,startDate,endDate));
		logger.log(OPERATING, "导出财务报表-收益表");
	}

@MethodLog是我自定义的注解,用于设置一个公共的数据来进行log4j2存储,比如没个业务操作都用登录用户信息,不用每次都在logger.log()方法里面写

代码:

package com.pinyu.system.global.ann;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 日志切面注解
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodLog {

    /**
     * 该注解作用于方法上时需要备注信息
     */
    String remark() default "";
    String operType() default "";
}

获取@MethodLog注解是用的spring aop的动态代理 Aspect切面

代码:

package com.pinyu.system.global.aspect;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.pinyu.system.dao.OperationLogDao;
import com.pinyu.system.entity.UserEntity;
import com.pinyu.system.global.GlobalConstants;
import com.pinyu.system.global.ann.MethodLog;
import com.pinyu.system.utils.SessionUtils;

/**
 * @author ypp 创建时间:2018年10月9日 上午10:37:34
 * @Description: TODO(日志切面)
 */
@Component
@Aspect
public class OperationLogAspect {

	protected Logger logger = LogManager.getLogger(OperationLogAspect.class);

	@Autowired
	private OperationLogDao logDao;

	public OperationLogAspect() {
		logger.info("用户操作日志");
	}

	/**
	 * 切点
	 */
	@Pointcut("@annotation(com.pinyu.system.global.ann.MethodLog)")
	public void methodCachePointcut() {
	}

	/**
	 * 切面
	 *
	 * @param point
	 * @return
	 * @throws Throwable
	 */
	@Around("methodCachePointcut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {

		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getRequest();
		String ip = getIp(request);
		UserEntity loginUser = SessionUtils.getLoginUser(request);
		ThreadContext.put("userId",String.valueOf(loginUser.getId()));
		ThreadContext.put("creater", loginUser.getRealName());
		ThreadContext.put("createUserName", loginUser.getUserName());
		ThreadContext.put("ip", ip);
		ThreadContext.put("type", GlobalConstants.LogType.OPERATING.getType());
		Object object = point.proceed();
		return object;

	}

	/**
	 * 获取请求ip
	 *
	 * @param request
	 * @return
	 */
	public static String getIp(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	/**
	 * 获取方法中的中文备注
	 *
	 * @param joinPoint
	 * @return
	 * @throws Exception
	 */
	public static String getMthodRemark(ProceedingJoinPoint joinPoint) throws Exception {

		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();

		Class targetClass = Class.forName(targetName);
		Method[] method = targetClass.getMethods();
		String methode = "";
		for (Method m : method) {
			if (m.getName().equals(methodName)) {
				Class[] tmpCs = m.getParameterTypes();
				if (tmpCs.length == arguments.length) {
					MethodLog methodCache = m.getAnnotation(MethodLog.class);
					if (methodCache != null) {
						methode = methodCache.remark();
					}
					break;
				}
			}
		}
		return methode;
	}
}

        ThreadContext.put("userId",String.valueOf(loginUser.getId()));
        ThreadContext.put("creater", loginUser.getRealName());
        ThreadContext.put("createUserName", loginUser.getUserName());
        ThreadContext.put("ip", ip);
        ThreadContext.put("type", GlobalConstants.LogType.OPERATING.getType());

这几行代码是设置公共信息,用户的信息,type是日志的一个分类类型,自己根据自己业务需要决定就行

比如:ThreadContext.put("userId",String.valueOf(loginUser.getId()));

在配置文件取出来就是:<Column name="user_id" pattern="%X{userId}" />

log4j2.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 -->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 -->
<Configuration status="WARN" monitorInterval="30">
	<!--全局属性 -->
	<Properties>
		<Property name="LOG_FILE_PATH">D:/apache-tomcat-8.5.33/logs</Property>
		<!-- linux日志存放路径 <Property name="LOG_FILE_PATH">/usr/logs</Property> -->
		<Property name="PATTERN_FORMAT">%d{yyyy-MM-dd HH:mm:ss} %-5level %class{36} %L
			%M - %msg%xEx%n</Property>
	</Properties>
	<CustomLevels>
	    <CustomLevel name="OPERATING" intLevel="350" />
	 </CustomLevels>
	<Appenders>
		<!--输出到控制台 -->
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout pattern="${PATTERN_FORMAT}" />
		</Console>


		<!--输出到文件 用来定义超过指定大小自动删除旧的创建新的的Appender. -->
		<RollingFile name="RollingInfoFile" fileName="${LOG_FILE_PATH}/pinyu.log"
			filePattern="${LOG_FILE_PATH}/$${date:yyyyMM}/info-%d{yyyyMMdd}-%i.log.gz">
			<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
			<Filters>
				<ThresholdFilter level="warn" onMatch="DENY"
					onMismatch="NEUTRAL" />
				<ThresholdFilter level="debug" onMatch="ACCEPT"
					onMismatch="DENY" />
			</Filters>

			<PatternLayout>
				<pattern>${PATTERN_FORMAT}</pattern>
			</PatternLayout>

			<Policies>
				<!-- rollover on startup, daily and when the file reaches 10 MegaBytes -->
				<OnStartupTriggeringPolicy />
				<SizeBasedTriggeringPolicy size="100 MB" />
				<TimeBasedTriggeringPolicy />
			</Policies>
		</RollingFile>

		<!--保存到数据库配置文件 -->
		<JDBC name="DatabaseAppender" tableName="sys_logs">
			<ConnectionFactory
				class="com.pinyu.system.global.config.log4j2.ConnectionFactory"
				method="getDatabaseConnection" />
			<!-- 方法名 -->
			<Column name="function" pattern="%M" />
			<!-- 日志级别 -->
			<Column name="level" pattern="%level" />
			<Column name="logger" pattern="%logger" />
			<!-- 类 -->
			<Column name="class" pattern="%C" />
			<!-- 时间 -->
			<Column name="create_date" pattern="%d{yyyy-MM-dd hh:mm:ss}" />
			<!-- 日志内容 -->
			<Column name="message" pattern="%message" />
			
			<Column name="user_id" pattern="%X{userId}" />
			<Column name="creater" pattern="%X{creater}" />
			<Column name="ip" pattern="%X{ip}" />
			<Column name="create_user_name" pattern="%X{createUserName}" />
			<Column name="type" pattern="%X{type}" />
		</JDBC>
	</Appenders>

	<Loggers>

		<!--过滤掉spring和mybatis的一些无用的DEBUG信息 -->
		<Logger name="org.springframework" level="INFO" />
		<Logger name="org.mybatis" level="INFO" />

		<!-- LOG "com.luis*" at TRACE level -->
		<Logger name="com.luis" level="INFO" />

		<!-- LOG everything at INFO level -->
		<Root level="ALL">
			<AppenderRef ref="Console" />
			<AppenderRef ref="RollingInfoFile" />
			<AppenderRef ref="RollingWarnFile" />
			<AppenderRef ref="RollingErrorFile" />
			<AppenderRef ref="DatabaseAppender" level="operating"/>
		</Root>
<!-- 		<Root level="OPERATION"> -->
<!-- 		</Root> -->
	</Loggers>

</Configuration>

<CustomLevels>
        <CustomLevel name="OPERATING" intLevel="350" />
     </CustomLevels>

这几行代码是自定义级别

<JDBC name="DatabaseAppender" tableName="sys_logs">
            <ConnectionFactory
                class="com.pinyu.system.global.config.log4j2.ConnectionFactory"
                method="getDatabaseConnection" />

</JDBC>

这里面的信息解释:

name就是一个名字,在下面ROOT标签里面的<AppenderRef ref="DatabaseAppender" level="operating"/>会用到它,级别定高点,不然比operating级别更高的依然会记录到数据库

tableName就是数据库表名

class是一系列的数据库连接这些

method是获取数据库连接、连接池等

class代码 ConnectionFactory:   用的dbcp的连接池

package com.pinyu.system.global.config.log4j2;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnection;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.springframework.stereotype.Component;

import com.pinyu.system.utils.SystemPropertiesUtils;


/** 
* @author ypp
* 创建时间:2018年11月1日 下午2:33:44 
* @Description: TODO(log4j2日志存储到数据库) 
*/
@Component
public class ConnectionFactory {
	
		private static interface Singleton {  
	       final ConnectionFactory INSTANCE = new ConnectionFactory();  
	    }  
	   
	   private final DataSource dataSource;  
	   
	   private ConnectionFactory() {  
	             try {  
	           Class.forName(SystemPropertiesUtils.getDataSourceDriverClassName());  
	       } catch (ClassNotFoundException e) {  
	           e.printStackTrace();  
	           System.exit(0);  
	       }  
	              
	       Properties properties = new Properties();  
	       properties.setProperty("user", SystemPropertiesUtils.getDataSourceUserName());  
	       properties.setProperty("password", SystemPropertiesUtils.getDataSourcePassword());   
	   
	       GenericObjectPool<PoolableConnection>pool = new GenericObjectPool<PoolableConnection>();  
	       DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(  
	    		   SystemPropertiesUtils.getDataSourceUrl(),properties  
	       );  
	       new PoolableConnectionFactory(connectionFactory, pool, null, null, false, true);  
	       this.dataSource = new PoolingDataSource(pool);  
	    }  
	   
	   public static Connection getDatabaseConnection() throws SQLException {  
	       return Singleton.INSTANCE.dataSource.getConnection();  
	   }  
}

最后代码中记录自定义级别的日志:

package com.pinyu.system.web.controller.finance;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.pinyu.system.entity.finance.FinanceIncomeEntity;
import com.pinyu.system.global.ann.MethodLog;
import com.pinyu.system.service.finance.FinanceIncomeService;
import com.pinyu.system.utils.excel.ExcelUtils;
import com.pinyu.system.web.controller.common.BaseController;
import com.pinyu.system.web.page.Page;

/**
 * @author ypp 创建时间:2018年10月29日 下午1:28:57
 * @Description: TODO(财务报表-收益)
 */
@Controller
@RequestMapping("/financeIncome")
public class FinanceIncomeController extends BaseController{
	
	protected Logger logger = LogManager.getLogger(FinanceIncomeController.class);


	/**
	 * 导出本页
	 * @param page
	 * @return
	 * @throws Exception 
	 */
	@RequestMapping("/exportPage/")
	@ResponseBody
	@MethodLog
	public void exportPage(HttpServletRequest request,HttpServletResponse response,@RequestParam("page")Integer page,@RequestParam("startDate")String startDate,@RequestParam("endDate")String endDate) throws Exception{
		Page<FinanceIncomeEntity> p=new Page<FinanceIncomeEntity>();
		p.setCurrentPage(page);
		ExcelUtils.setResponse(response, service.exportPage(p,startDate,endDate));
		logger.log(OPERATING, "导出财务报表-收益表");
	}

}

OPERATING也就是我自己定义的级别,可以看到log4j2.xml配置文件里面数据库存储的也是存储的这个级别的,这个级别我是抽到BaseController里面的,因为基本很多controller都会用到这个

如果需要BaseController里面代码,可以直接用就行,不需要其他的只看Level那一行代码就可以了

BaseController:

package com.pinyu.system.web.controller.common;

import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.Level;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContext;

import com.pinyu.system.entity.UserEntity;
import com.pinyu.system.utils.SessionUtils;

/** 
* @author ypp
* 创建时间:2018年11月1日 下午1:51:59 
* @Description: TODO(用一句话描述该文件做什么) 
*/
public abstract class BaseController {
	
	protected static final Level OPERATING = Level.forName("OPERATION", 350);
	
	/**
     * 要跳转的页面
     */
    protected ModelAndView go(String path) {
        return new ModelAndView(path);
    }
    
    /**
     * 获取登录用户信息
     * @return
     */
    public UserEntity getUser(HttpServletRequest request) {
    	return SessionUtils.getLoginUser(request);
    }
    
    /**
     * 获取登录用户信息
     * @return
     */
    public String logInfo(HttpServletRequest request) {
    	UserEntity user = getUser(request);
    	return "用户id:"+user.getId()+",姓名:"+user.getRealName()+",";
    }
    
    
    /**
     * 获取国际化信息
     * 
     * @param key
     * @return
     */
    public String getI18nMsg(HttpServletRequest request,String key) throws Exception {
        // 从后台代码获取国际化信息
        String value = new RequestContext(request).getMessage(key);
        return value != null ? value : "";
    }
    
    /**
     * 请求方式判断
     * 
     * @param request
     * @return
     */
    public boolean isAjaxRequest(HttpServletRequest request) {
        if (!(request.getHeader("accept").indexOf("application/json") > -1
                || (request.getHeader("X-Requested-With") != null
                        && request.getHeader("X-Requested-With").indexOf("XMLHttpRequest") > -1)
                || "XMLHttpRequest".equalsIgnoreCase(request.getParameter("X_REQUESTED_WITH")))) {
            return false;
        }
        return true;
    }
    
    /**
     * 设置 cookie
     * 
     * @param cookieName
     * @param value
     * @param age
     */
    protected void setCookie(HttpServletResponse response,String cookieName, String value, int age) {
        Cookie cookie = new Cookie(cookieName, value);
        cookie.setMaxAge(age);
        // cookie.setHttpOnly(true);
        response.addCookie(cookie);
    }
    
    /**
     * 方法名称: getUUID<br>
     * 描述:获得唯一标识(目前用于验证表单提交唯一性)<br>
     * 作者: <br>
     * 修改日期:2014年10月13日上午3:15:24<br>
     */
    protected String getUUID() {
        return UUID.randomUUID().toString();
    }
    
    /**
     * 获取请求完整路径
     * @param request
     * @return
     */
    public String getUrl(HttpServletRequest request){
        String url = request.getRequestURI();
        String params = "";
        if(request.getQueryString()!=null){
            params = request.getQueryString().toString();
        }
        if(!"".equals(params)){
            url = url+"?"+params;
        }
        return url;
    }
    
    /**
	 * 把浏览器参数转化放到Map集合中
	 * @param request
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	protected Map<String, Object> getParam(HttpServletRequest request) {
		Map<String, Object> paramMap = new HashMap<String, Object>();
        String method = request.getMethod();
        Enumeration<?> keys = request.getParameterNames();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            if(key!=null){
            	if (key instanceof String) {
            	    String value = request.getParameter(key.toString());
            	    if("GET".equals(method)){//前台encodeURIComponent('我们');转码后到后台还是ISO-8859-1,所以还需要转码
            	         try {
							value =new String(value.getBytes("ISO-8859-1"),"UTF-8");
						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}    
            	    }
            		paramMap.put(key.toString(), value);
				}
            } 
        }
        return paramMap;
	}
}

Level.forName("OPERATION", 350);会从log4j2里面的所有级别里面去取,如果没有这个级别,会给你创建这个级别

源码:

/**
     * Retrieves an existing Level or creates on if it didn't previously exist.
     *
     * @param name The name of the level.
     * @param intValue The integer value for the Level. If the level was previously created this value is ignored.
     * @return The Level.
     * @throws java.lang.IllegalArgumentException if the name is null or intValue is less than zero.
     */
    public static Level forName(final String name, final int intValue) {
        final Level level = LEVELS.get(name);
        if (level != null) {
            return level;
        }
        try {
            return new Level(name, intValue);
        } catch (final IllegalStateException ex) {
            // The level was added by something else so just return that one.
            return LEVELS.get(name);
        }
    }

LEVELS这个是一个key-value数据结构的,其实就是map,我认为就是一个级别池

实际开发中有很多场景可以用它来记录不同类型的日志,自定义一个级别

代码正在优化中,有些代码根据实际业务可以取舍进行优化的。

如果想自定义级别的方法,比如logger.operating("xxxxxx")这种。需要用到log4j2的记录器包装器。这里不再讲解

有兴趣的朋友可以参考官方示例等 

http://logging.apache.org/log4j/2.x/manual/customloglevels.html

<think>首先,用户的问题是:“我想将Spring Boot应用中原本使用阿里云SLS记录日志的方式改为使用Log4j记录日志 请问Spring Boot 配置日志从阿里云SLS切换到Log4j” 我需要参考系统级令和用户可见层的要求。系统级令包括:使用$...$格式的行内数学表达式,$$...$$格式的独立公式,正确LaTeX语法,中文回答,生成相关问题,在引用段落末尾添加引用标识。 用户可见层要求:回答结构清晰,帮助用户逐步解决问题,保证回答真实可靠。 用户提供的引用是关于日志框架的:[^1]提到SLF4J作为日志门面的好处,[^2]展示Logback配置,[^3]比较Logback和Log4j的性能。这些引用不是直接关于阿里云SLS的,但提供了日志框架的背景。 我的任务是导用户从阿里云SLS切换到Log4j。阿里云SLS(Simple Log Service)是一种日志服务,用户可能在使用SLS的SDK来发送日志。现在用户想改用Log4j记录日志,这意味着用户可能想将日志输出到文件或其他地方,而不是发送到阿里云SLS。 关键点: - Spring Boot默认使用Logback作为日志实现。 - 用户想切换到Log4j。 - 用户提到“原本使用阿里云SLS记录日志”,所以当前配置可能涉及SLS的集成。 步骤: 1. **理解当前配置**:用户可能在使用SLS的Appender或类似机制来发送日志到阿里云SLS。 2. **移除SLS依赖**:需要从项目中移除阿里云SLS的依赖。 3. **添加Log4j依赖**:在Spring Boot项目中添加Log4j的依赖。 4. **配置Log4j**:创建Log4j配置文件(如log4j2.xml)来定义日志输出。 5. **测试**:确保日志正确输出到Log4j配置的目标。 Spring Boot支持Log4j2,用户说“Log4j”,可能的是Log4j2,因为Log4j1.x已过时。我应该假设是Log4j2。 参考Spring Boot官方文档: - Spring Boot默认使用Logback,但可以切换到Log4j2。 - 需要排除Logback依赖,添加Log4j2依赖。 在pom.xml中: - 排除spring-boot-starter-logging(它包含Logback)。 - 添加spring-boot-starter-log4j2。 然后配置log4j2.xml文件。 用户提到“阿里云SLS”,所以可能需要移除SLS相关的依赖和配置。 生成相关问题: - 根据系统令,我需要生成相关问题,放在回答末尾。 回答结构: 1. **介绍**:简要解释切换步骤。 2. **步骤**:逐步导如何操作。 3. **代码示例**:提供pom.xml和配置文件的示例。 4. **注意事项**:提醒潜在问题。 5. **相关问题**:生成相关问题。 使用中文回答。 添加引用:由于用户提供了引用,我应该自然地引用它们。例如,在讨论日志框架时,引用[^1]关于SLF4J的好处,引用[^3]关于Logback vs Log4j。 最终回答应包括: - 移除SLS依赖。 - 添加Log4j2依赖。 - 配置Log4j2。 - 测试日志输出。 生成相关问题: 1. 如何优化Log4j2的配置? 2. Spring Boot中如何集成其他日志服务? 3. 日志框架切换后如何确保兼容性?</think>### Spring Boot 配置日志从阿里云SLS切换到Log4jSpring Boot应用中,将日志记录方式从阿里云SLS(Simple Log Service)切换到Log4j(通常Log4j2,因为Log4j1.x已过时)需要修改依赖和配置文件。阿里云SLS通常通过其SDK或Appender集成,而Log4j2是高性能的日志框架。Spring Boot默认使用Logback,但可以通过依赖调整切换到Log4j2。以下是详细步骤,确保操作可靠(基于Spring Boot 2.x/3.x)。 #### 步骤1: 移除阿里云SLS依赖和配置 首先,移除项目中与阿里云SLS相关的依赖和配置。阿里云SLS通常通过`aliyun-log-producer`或类似库实现日志发送。在`pom.xml`中删除这些依赖,检查是否有SLS的Appender配置(如XML或YAML文件)。 ```xml <!-- 在pom.xml中移除SLS依赖,例如: --> <dependencies> <!-- 删除类似以下依赖 --> <!-- <dependency> <groupId>com.aliyun.openservices</groupId> <artifactId>aliyun-log-producer</artifactId> <version>0.3.9</version> </dependency> --> </dependencies> ``` 同时,删除或注释掉SLS的配置文件(如`application.yml`中的SLS相关设置): ```yaml # 示例:移除SLS配置 # aliyun: # sls: # endpoint: your-endpoint # project: your-project # logstore: your-logstore ``` #### 步骤2: 添加Log4j2依赖 Spring Boot默认使用Logback,需排除其依赖添加Log4j2 starter。在`pom.xml`中修改: ```xml <dependencies> <!-- 排除默认的Logback依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加Log4j2依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> </dependencies> ``` #### 步骤3: 配置Log4j2 创建Log4j2配置文件(如`src/main/resources/log4j2.xml`),定义日志输出目标(例如控制台或文件)。以下是一个基础配置示例: ```xml <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <!-- 控制台输出 --> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </Console> <!-- 文件输出 --> <File name="File" fileName="logs/application.log" append="true"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </File> </Appenders> <Loggers> <!-- 根日志级别设置为INFO --> <Root level="info"> <AppenderRef ref="Console"/> <AppenderRef ref="File"/> </Root> <!-- 自定义日志级别 --> <Logger name="com.example" level="debug" additivity="false"> <AppenderRef ref="File"/> </Logger> </Loggers> </Configuration> ``` #### 步骤4: 测试日志输出 运行应用验证日志是否按Log4j2配置输出: - 在代码中添加日志语句: ```java import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DemoController { private static final Logger logger = LoggerFactory.getLogger(DemoController.class); public void testLog() { logger.info("Log4j2测试: 日志已切换到文件和控制台"); } } ``` - 检查日志文件(如`logs/application.log`)和控制台输出。如果日志正确写入,表示切换成功。 #### 注意事项 - **SLF4J兼容性**:Log4j2通过SLF4J API工作,确保代码中使用`org.slf4j.Logger`接口,无需修改业务代码[^1]。 - **性能考虑**:Log4j2在吞吐量和异步日志方面优于Logback,适合高发场景[^3]。 - **潜在问题**: - 如果依赖冲突,运行`mvn dependency:tree`检查。 - 确保没有残留的SLS Appender配置。 - Spring Boot 3.x需使用Log4j2 2.17+以避免安全漏洞。 通过以上步骤,您可以无缝切换日志框架。切换后,日志将本地化存储,不再发送到阿里云SLS。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值