当前位置:网站首页>Coding technique - Global log switch
Coding technique - Global log switch
2022-07-27 16:11:00 【Qihai Jianren】
Development process , Business log is very important and necessary , It can often help us troubleshoot problems , Such as abnormal reasons 、 In and out 、 Whether there is dirty data 、 What step has been taken ; But the more logs, the better , For large flow interfaces , You need to be careful when logging , A large number of logs will lead to frequent IO、 Affect machine load ;
" Based on experience ,4 nucleus 8G Machine ,1 Hours Log volume Not more than 20W "
—— Experienced soaring logs, resulting in machine load reaching 80 A module of SE Experience given
Besides , When the middleware is abnormal , Such as ZK Can't connect 、MQ When link exceptions occur , Rebooting the machine will also produce a crazy amount of logs , It may cause the service to still fail to restart successfully ; Now , If there is a global log switch , Can mask some specified Class Printed logs , You can temporarily solve the problem of soaring logs ;
Here are respectively Log4j and Logback Tool class implementation under the logging framework , The principle is to get the specified class Logger object , Set its log level to shielding , This attribute is judged in real time when printing logs , Therefore, there is no need to restart the machine to take effect ; Triggers are configuration center change time messages , It can be ZK、Apollo etc. ;
1. Log4J Log switch
package com.AA.service.helper.logger;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.AA.client.ConfigManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author AA
* @description Global log switching tool
* @date 2022/3/25
*/
@Slf4j
@Component
public class LoggerSwitchHelper implements InitializingBean {
/**
* Mask logs only for specified classes
*/
private static final String LOGGER_BLACKLIST = "logger.switch.blacklist";
/**
* Temporarily save the last closed log object switch , It is used to restore the logs under the original class after modifying the configuration
*/
private static Map<String, Level> LAST_LOGGER_BLACKLIST_MAP = new ConcurrentHashMap<>();
@Override
public void afterPropertiesSet() throws Exception {
String conf = ConfigManager.getString(LOGGER_BLACKLIST, "");
refreshLoggerSwitch(conf);
ConfigManager.addListener((item, type) -> {
if (StringUtils.equalsIgnoreCase(item.getName(), LOGGER_BLACKLIST)) {
refreshLoggerSwitch(item.getValue());
}
});
}
/**
* Refresh configuration
*/
private synchronized void refreshLoggerSwitch(String conf) {
try {
log.info("refreshLoggerSwitch. [conf={}]", conf);
// Temporarily record the current blacklist
Map<String, Level> current_logger_blacklist = Maps.newConcurrentMap();
if (StringUtils.isNotEmpty(conf)) {
List<String> loggerBlackList = Arrays.asList(StringUtils.split(conf, ","));
if (CollectionUtils.isNotEmpty(loggerBlackList)) {
for (String loggerName : loggerBlackList) {
org.apache.log4j.Logger logger = LogManager.getLogger(loggerName);
if (null != logger) {
if (null != logger.getLevel()) {
current_logger_blacklist.put(loggerName, logger.getLevel());
}
// The level of the current log object is empty And there is a parent log Then initialize its current level to the level of the parent log object
else if (logger.getAdditivity()) {
current_logger_blacklist.put(loggerName, logger.getParent().getLevel());
}
logger.setLevel(Level.OFF);
log.info("logger_turn_off. [loggerName={}]", loggerName);
} else {
log.info("logger_not_found. [loggerName={}]", loggerName);
}
}
}
}
// For the last closed class , The log opened this time needs to be restored
if (MapUtils.isNotEmpty(LAST_LOGGER_BLACKLIST_MAP)) {
for (Map.Entry<String, Level> entry : LAST_LOGGER_BLACKLIST_MAP.entrySet()) {
String loggerName = entry.getKey();
if (!current_logger_blacklist.containsKey(loggerName)) {
// Remove from snapshot
LAST_LOGGER_BLACKLIST_MAP.remove(entry.getKey());
org.apache.log4j.Logger logger = LogManager.getLogger(loggerName);
if (null != logger) {
// According to the snapshot Set to the log level before closing
logger.setLevel(entry.getValue());
log.info("logger_remove_fr_blackList_reopen. [loggerName={} level={}]", loggerName, entry.getValue());
}
}
}
}
// Save snapshots
current_logger_blacklist.forEach((loggerName, level) -> {
LAST_LOGGER_BLACKLIST_MAP.putIfAbsent(loggerName, level);
});
log.info("save_last_loggerCfg. [last_loggerCfg={}]", JSON.toJSONString(LAST_LOGGER_BLACKLIST_MAP));
log.info("refreshLoggerSwitch_suc.");
} catch (Exception e) {
log.error("refreshLoggerSwitch_error. [conf={}]", conf);
}
}
}
2. Logback Log switch
Add based on logback The implementation of the ;
package com.AA.service.config;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.AA.ConfigManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author AA
* @description logback Log switch tool
* @date 2022/1/19
*/
@Slf4j
@Component
public class LoggerSwitchHelper implements InitializingBean {
/**
* Mask logs only for specified classes
*/
private static final String LOGGER_BLACKLIST = "logger.switch.blacklist";
/**
* Temporarily save the last closed log object switch , It is used to restore the logs under the original class after modifying the configuration
*/
private static Map<String, Level> LAST_LOGGER_BLACKLIST_MAP = new ConcurrentHashMap<>();
@Override
public void afterPropertiesSet() throws Exception {
String conf = ConfigManager.getString(LOGGER_BLACKLIST, "");
refreshLoggerSwitch(conf);
ConfigManager.addListener((item, type) -> {
if (StringUtils.equalsIgnoreCase(item.getName(), LOGGER_BLACKLIST)) {
refreshLoggerSwitch(item.getValue());
}
});
}
/**
* Refresh configuration
*/
private synchronized void refreshLoggerSwitch(String conf) {
try {
log.info("refreshLoggerSwitch. [conf={}]", conf);
// Temporarily record the current blacklist
Map<String, ch.qos.logback.classic.Level> current_logger_blacklist = Maps.newConcurrentMap();
// Log configuration context
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
if (StringUtils.isNotEmpty(conf)) {
List<String> loggerBlackList = Arrays.asList(StringUtils.split(conf, ","));
if (CollectionUtils.isNotEmpty(loggerBlackList)) {
for (String loggerName : loggerBlackList) {
ch.qos.logback.classic.Logger logger = lc.getLogger(loggerName);
if (null != logger) {
if (null != logger.getLevel()) {
current_logger_blacklist.put(loggerName, logger.getLevel());
}
// If the level is empty, take the parent level Take when abnormal INFO Grade
else if (logger.isAdditive()) {
try {
Field field = logger.getClass().getDeclaredField("parent");
field.setAccessible(true);
final Logger parentLogger = (Logger) field.get(logger);
current_logger_blacklist.put(loggerName, parentLogger.getLevel());
} catch (Exception e) {
current_logger_blacklist.put(loggerName, ch.qos.logback.classic.Level.INFO);
}
}
logger.setLevel(Level.OFF);
log.info("logger_turn_off. [loggerName={}]", loggerName);
} else {
log.info("logger_not_found. [loggerName={}]", loggerName);
}
}
}
}
// For the last closed class , The log opened this time needs to be restored
if (MapUtils.isNotEmpty(LAST_LOGGER_BLACKLIST_MAP)) {
for (Map.Entry<String, ch.qos.logback.classic.Level> entry : LAST_LOGGER_BLACKLIST_MAP.entrySet()) {
String loggerName = entry.getKey();
if (!current_logger_blacklist.containsKey(loggerName)) {
// Remove from snapshot
LAST_LOGGER_BLACKLIST_MAP.remove(entry.getKey());
ch.qos.logback.classic.Logger logger = lc.getLogger(loggerName);
if (null != logger) {
// According to the snapshot Set to the log level before closing
logger.setLevel(entry.getValue());
log.info("logger_remove_fr_blackList_reopen. [loggerName={} level={}]", loggerName, entry.getValue());
}
}
}
}
// Save snapshots
current_logger_blacklist.forEach((loggerName, level) -> {
LAST_LOGGER_BLACKLIST_MAP.putIfAbsent(loggerName, level);
});
log.info("save_last_loggerCfg. [last_loggerCfg={}]", JSON.toJSONString(LAST_LOGGER_BLACKLIST_MAP));
log.info("refreshLoggerSwitch_suc.");
} catch (Exception e) {
log.error("refreshLoggerSwitch_error. [conf={}]", conf);
}
}
}
Mainly different from Logger Object acquisition ; You can also use the policy mode to be compatible with both logging frameworks ;
边栏推荐
- [sword finger offer] interview question 39: numbers that appear more than half of the time in the array
- openwrt 增加RTC(MCP7940 I2C总线)驱动详解
- DeFi安全之DEX与AMMs
- Join hands with sifive, Galanz will enter the semiconductor field! Exposure of two self-developed chips
- [sword finger offer] interview question 42: the maximum sum of continuous subarrays -- with 0x80000000 and int_ MIN
- web测试学习笔记01
- 4位数的随机数据
- Stock account opening commission discount, stock trading account opening which securities company is good, is online account opening safe
- 接连取消安富利/文晔/世平代理权,TI到底打的什么算盘?
- 逗号操作符你有用过吗?
猜你喜欢
![[sword finger offer] interview question 42: the maximum sum of continuous subarrays -- with 0x80000000 and int_ MIN](/img/01/bbf81cccb47b6351d7265ee4a77c55.png)
[sword finger offer] interview question 42: the maximum sum of continuous subarrays -- with 0x80000000 and int_ MIN

判断数据的精确类型

企业运维安全就用行云管家堡垒机!

无线网络分析有关的安全软件(aircrack-ng)

剑指 Offer 51. 数组中的逆序对

centos上mysql5.7主从热备设置

Division of entity classes (VO, do, dto)

MySQL索引

Axure 安装图标字体元件库
![[sword finger offer] interview question 41: median in data flow - large and small heap implementation](/img/c3/7caf008b3bd4d32a00b74f2c508c65.png)
[sword finger offer] interview question 41: median in data flow - large and small heap implementation
随机推荐
少见的按位操作符
Mapreduce实例(二):求平均值
Redis简介与使用
Hematemesis finishing c some commonly used help classes
百度图片复制图片地址
时间序列——使用tsfresh进行分类任务
Ncnn reasoning framework installation; Onnx to ncnn
数据表的约束以及设计、联合查询——8千字攻略+题目练习解答
Flask连接mysql数据库已有表
台积电的反击:指控格芯侵犯25项专利,并要求禁售!
[tensorboard] oserror: [errno 22] invalid argument processing
一款功能强大的Web漏洞扫描和验证工具(Vulmap)
Under the ban, the Countermeasures of security giants Haikang and Dahua!
JSP基础
webRTC中的coturn服务安装
Common tool classes under JUC package
First acquaintance with MySQL database
DRF学习笔记(二):数据反序列化
可载100人!马斯克发布史上最强“星际飞船” !最早明年上火星!
Oracle 常用语句