捕获项目中的异常信息,并处理、入库

公司的项目是一个爬虫的管理中心,需要对爬虫进行监控,监控它们是不是在正常运行。

从网上搜到的解决方案都是 spring boot 下的,由于公司的项目是老项目,所以不适用。于是想到了从log4j下手,通过自定义Appender,拦截日志,并从日志中提取出我需要的信息入库。然后调用父类的同方法,保证日志以前能干的事不会少了。

由于自定义所考虑的东西太多,所以继承了 DailyRollingFileAppender 减少工作量。

第一反应的写法:

  1. 每当产生一条日志时,根据配置的日志级别进行调用
  2. LoggingEvent 对象内包含了日志内容,如果是异常日志,还会包含整个的异常栈的信息。
  3. 通过LoggingEvent对象的getLevel拿到我们想要的日志级别。
  4. 从日志中解析我们需要的内容
  5. 将日志保存到数据库中
  6. 调用父类的doAppend方法,保证之前逻辑的执行。
        @Override
	public void doAppend(LoggingEvent event) {
		if (isAsSevereAsThreshold(event.getLevel())) {
			if(service == null) {
				init();
			}
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			Date date = new Date(event.getTimeStamp());
			ThrowableInformation throwableInfo = event.getThrowableInformation();
			ExceptionBean eb = parseException(throwableInfo);
			eb.setMessage(event.getMessage().toString());
			
			eb.setTime(df.format(date));
			eb.setLevel(event.getLevel().toString());
			service.save(eb);
		}
		super.doAppend(event);
	}

存在的问题:

由于这个自定义的类是直接通过log4j的配置文件,被加载到内存的,不是spring直接管理的。所以入库需要的资源不能自动注入,也就没有办法入库了。

解决方案:

虽然这个自定义类不是spring直接管理的,但是我可以拿到spring的上下文信息啊

拿到了spring的上下文信息之后,通过其获取我需要的 Bean 并存起来,不就可以实现入库了吗

SpringContextUtil类代码:

public class SpringContextUtil {

	private static ApplicationContext applicationContext = null;
	
	private synchronized static void init() {
		if(applicationContext == null) { 
			ServletContext sc = ContextLoader.getCurrentWebApplicationContext().getServletContext();
			applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
		}
	}

	public static Object getBean(String beanName) {
		return applicationContext.getBean(beanName);
	}

	public static Object getBean(Class c) {
		if(applicationContext == null) {
			init();
		}
		return applicationContext.getBean(c);
	}
}

再次遇到问题:

由于我在 Appender 初始化的时候就去调用 SpringContextUtil 去获取 Service 层的 Bean,看起来是没有问题的。但是系统在启动的时候也是要打日志的啊,这个时候 SpringContext 还没有初始化,于是便出问题了。

最后的解决方案是,用一个懒加载的办法去获取 Bean 这样就避免了初始化 Bean 的时候出现的问题。

初始化 Service Bean 的代码:

private synchronized void init() {
	if(service == null) {
		service = (ExceptionService) SpringContextUtil.getBean(ExceptionService.class);
	}
}

这段代码调用是在最开始那段代码,为了防止出现并发问题,所以在外层也做了 null 值判断。

水平有限,若有错误,请指正。

### Logback 参数配置及说明 Logback 的参数配置主要包括以下几个方面: #### 1. 日志级别 Logback 支持五种日志级别,按照优先级从低到高依次为 `TRACE`、`DEBUG`、`INFO`、`WARN` 和 `ERROR`。这些级别的设置决定了哪些日志消息会被记录下来[^4]。 ```xml <configuration> <root level="DEBUG"> <appender-ref ref="STDOUT"/> </root> </configuration> ``` 上述代码片段展示了如何设置根日志器的日志级别为 `DEBUG` 关联一个名为 `STDOUT` 的输出设备。 #### 2. Appender 配置 Appender 定义了日志的输出目标以及格式化方式。常见的 appender 类型包括控制台输出 (`ConsoleAppender`)、文件输出 (`FileAppender`) 和滚动文件输出 (`RollingFileAppender`) 等[^2]。 ##### 控制台输出 (ConsoleAppender) 以下是使用 ConsoleAppender 将日志打印至标准输出的一个例子: ```xml <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> ``` 此配置指定了日志的时间戳、线程名、日志等级、Logger 名称和实际的消息内容。 #### 3. PatternLayoutEncoder PatternLayoutEncoder 负责定义日志的具体格式。它支持多种占位符来定制输出样式[^3]。 | 占位符 | 描述 | |--------|------| | `%date` 或者 `%d` | 输出当前时间,默认格式为 ISO8601 | | `%thread` | 当前线程名称 | | `%level` | 日志事件的优先级 | | `%logger{n}` | Logger 名字,n 表示截取长度 | | `%message` 或 `%msg` | 实际的日志消息 | #### 4. RollingPolicy 和 TriggeringPolicy 对于长期运行的应用程序来说,建议采用滚动策略管理日志文件大小或生命周期。下面是一个基于时间和文件大小双重条件触发滚动的例子: ```xml <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- daily rollover --> <fileNamePattern>logs/archived/app.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>10MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> ``` 该配置实现了按日期归档旧日志的同时还设置了单个文件的最大尺寸限制为 10 MB。 #### 5. 自定义转换器 如果内置 pattern 不满足需求,则可以创建自己的转换器类注册给 logback 使用。 ```java public class CustomConverter extends ClassicConverter { @Override public String convert(ILoggingEvent event) { return "CustomField:" + System.currentTimeMillis(); } } ``` 接着,在 XML 中声明新的 converter: ```xml <conversionRule conversionWord="customTimestamp" converterClass="com.example.CustomConverter"/> <pattern>%customTimestamp %msg%n</pattern> ``` 这样就可以在日志中嵌入自定义字段了。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值