惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
B
Blog RSS Feed
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News
B
Blog
博客园 - 司徒正美
Vercel News
Vercel News
A
About on SuperTechFans
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
Recent Announcements
Recent Announcements
S
SegmentFault 最新的问题
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 热门话题
Y
Y Combinator Blog
F
Full Disclosure
月光博客
月光博客
C
Cyber Attacks, Cyber Crime and Cyber Security
MongoDB | Blog
MongoDB | Blog
The Cloudflare Blog
The Hacker News
The Hacker News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
A
Arctic Wolf
Scott Helme
Scott Helme
V
Visual Studio Blog
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tor Project blog
P
Privacy International News Feed
Spread Privacy
Spread Privacy
G
GRAHAM CLULEY
Microsoft Security Blog
Microsoft Security Blog
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
PCI Perspectives
PCI Perspectives
小众软件
小众软件
博客园 - 【当耐特】
Cloudbric
Cloudbric
S
Secure Thoughts
L
LINUX DO - 最新话题
Google Online Security Blog
Google Online Security Blog
T
Troy Hunt's Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
W
WeLiveSecurity
V
Vulnerabilities – Threatpost
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位

博客园 - Doyourself!

rocketmq 启动后 在mq console界面的consumer的Quantity数量显示为0 问题记录 python 切换版本后 提示 无法在python 3.11(.venv)(D:/my_rag_bot/.venv/Scripts/python.exe)设置 python sdk,该sdk似乎无效 多个WebMvcConfigurer配置Jackson2ObjectMapperBuilder不生效问题记录 自定义拦截器不生效问题记录 记录一次nginx能通但是请求一直不了的问题 idea远程连接并本地打包到远程服务器 记一次生产环境内存溢出记录 凤凰架构总结 sentinel接入记录 JVM虚拟机总结 记录一次首页优化的经历 使用sharding-jdbc做分库分表记录 使用druid自定义拦截器 记录一次 maven 子模块相互依赖导致的父模块无法动态升级的问题 'parent.relativePath' points at wrong local POM 雪花算法snowflakeIdWorker使用记录 全局调用链路traceId网关到业务层、feign调用统一问题记录 Spring Cloud 的ribbon的饥饿加载机制 打印mq异常消息记录 根据druid将慢sql通过钉钉的方式进行告警功能记录
记录一次日志告警随着nacos文件动态刷新而失效的问题
Doyourself! · 2025-12-29 · via 博客园 - Doyourself!

           事情是这样的:我把告警的配置修改成了随着nacos动态更新的,但是更新后日志异常告警却不打印了。刚开始怀疑是不是网络或者nacos配置的问题,经过复现发现是因为我每次修改完nacos动态配置后,我的日志组件TurboFilter 是基于日志logback,他不是spring的bean的方式加载的,而nacos的动态更新需要bean是spring的,当我在nacos更新后,日志组件没有动态加载导致失效了。

   我的原来的代码:

/**
 * 异常日志堆栈-用于非运行时异常的告警提示
 * @author fanht
 * @date 2022-03-21  17:28
 * @versio 1.0
 */
@Component
public class MarketingLogFilter extends TurboFilter {

    /**rocketmq异常信息打印*/
    private static final String ROCKETMQ_ERROR = "rocketmq";

    @Resource
    private SimpleBufferTriggerUtils simpleBufferTriggerUtils;

    @Resource
    private OptionalAlarmUriUtils optionalAlarmUriUtils;

    @Resource
    private DingdingAlarmUtil dingdingAlarmUtil;
    @Override
    public FilterReply decide(Marker marker, Logger logger, Level level, String s, Object[] objects, Throwable throwable) {
        if(logger.getName().contains("rocketmq")){
            System.out.println("mq error:" + (Optional.of(logger).map(l->l.getName()).orElse(null))+ ",throwable:" + Optional.ofNullable(throwable).map(t->t.getMessage()).orElse(null));
        }
        if(dingdingAlarmUtil==null){
            dingdingAlarmUtil = ApplicationContext.getBean(DingdingAlarmUtil.class);
        }
        //todo 如果是非运行时异常 发送钉钉告警。排除运行时异常
        if(throwable != null && StringUtils.isNotEmpty(throwable.getMessage()) && level.equals(Level.ERROR)){
            if(optionalAlarmUriUtils == null){
                optionalAlarmUriUtils = ApplicationContext.getBean(OptionalAlarmUriUtils.class);
            }
            if(!optionalAlarmUriUtils.isExistUri(logger.getName())){
                if(simpleBufferTriggerUtils == null){
                    simpleBufferTriggerUtils =  ApplicationContext.getBean(SimpleBufferTriggerUtils.class);
                }
                simpleBufferTriggerUtils.proceErrorAlarm(Tag.builder().applicationName(dingdingAlarmUtil.getApplicationName())
                        .env(dingdingAlarmUtil.getEnv())
                        .ip(IpUtil.initIp())
                        .traceId(MDC.get(TraceUtils.TRACE_ID))
                        .requestUri(logger.getName())
                        .exMessage(JSONObject.toJSON(throwable.getMessage().length() >500 ?
                                throwable.getMessage().substring(0,500).toString():throwable.getMessage()).toString())
                        .build());
            }
        }else if(Level.ERROR.equals(level) && logger.getName().contains(ROCKETMQ_ERROR)){
            //如果是mq的异常 也要进行告警
            if(!optionalAlarmUriUtils.isExistUri(logger.getName())){
                String exMessage = Optional.ofNullable(s).orElse("") + Optional.ofNullable(objects).map(t->t[0]).orElse(null);
                simpleBufferTriggerUtils.proceErrorAlarm(Tag.builder().applicationName(dingdingAlarmUtil.getApplicationName())
                        .env(dingdingAlarmUtil.getEnv()).ip(IpUtil.initIp()).requestUri(logger.getName()).traceId(MDC.get(TraceUtils.TRACE_ID))
                        .exMessage(Optional.ofNullable(exMessage).map(t->t.length()>500?t.substring(0,500):t).orElse(""))
                        .build());
            }
        }
        //todo 如果接入kafka,则此处需要需要改为NEUTRAL,以防止kakfa的多次打印
        return FilterReply.NEUTRAL;
    }
}

   借助ai工具,问题记录如下:

Spring Cloud 的 Logging Refresh 会重建 Logback 上下文

当你调用 /actuator/refresh(或 Nacos 自动触发 refresh)时,Spring Cloud 会刷新 logging 相关配置(如 logging.level.*logging.file.name 等)。

而 Logback 的实现方式是:重建整个 LoggerContext

这意味着:

  • 原有的 LoggerContext 被关闭
  • 所有已注册的 AppenderTurboFilterLogger 都被清除
  • 创建一个新的 LoggerContext
  • 重新加载 logback-spring.xml

这就是为什么刷新后 decide() 完全不执行 —— filter 根本不在新上下文中。


✅ 解决方案:监听 LoggerContext 重建事件,自动重新注册

Logback 提供了 LoggerContextListener 机制,但更简单的方式是:监听 Spring 的 ApplicationEnvironmentPreparedEventContextRefreshedEvent 不行,因为 logging refresh 是独立的。

正确做法:监听 Logback 自身的 ContextResetEvent

但 Spring Boot 封装了 Logback,我们可以通过 监听 Spring 的 LoggingApplicationListener 行为 来间接处理。

不过最可靠、最通用的方法是:

每次 Logback 初始化时自动注册你的 TurboFilter

✅ 推荐方案:自定义 LoggerContextListener

创建一个 Logback 的 listener,在每次上下文初始化时注册 filter:

 最佳方案(不改 XML):

监听 RefreshScopeRefreshedEvent,并在新 LoggerContext 初始化后重新注册 TurboFilter

关键点:

  • Spring Cloud 在 /actuator/refresh 后会发布 RefreshScopeRefreshedEvent
  • 此时 Logback 的 LoggerContext 可能已经被重建
  • 我们可以在事件监听器中 重新获取当前 LoggerContext 并注册 filter
Component
public class TurboFilterReRegistrar {

    private final MarketingLogFilter marketingLogFilter;

    public TurboFilterReRegistrar(MarketingLogFilter marketingLogFilter) {
        this.marketingLogFilter = marketingLogFilter;
    }

    /**
     * 监听 Refresh 完成事件(包括 Nacos 配置刷新)
     */
    @EventListener
    public void handleRefresh(RefreshScopeRefreshedEvent event) {
        reRegisterTurboFilter();
    }

    /**
     * 也监听 Context Refreshed(兜底,确保启动时也注册)
     */
    @EventListener
    public void handleContextRefreshed(ContextRefreshedEvent event) {
        reRegisterTurboFilter();
    }

    private void reRegisterTurboFilter() {
        try {
            ILoggerFactory factory = LoggerFactory.getILoggerFactory();
            if (factory instanceof LoggerContext) {
                LoggerContext context = (LoggerContext) factory;

                // 检查是否已存在,避免重复注册(TurboFilter 没有 equals/hashCode,简单用类型判断)
                boolean alreadyRegistered = context.getTurboFilterList().stream()
                    .anyMatch(f -> f.getClass() == MarketingLogFilter.class);

                if (!alreadyRegistered) {
                    context.addTurboFilter(marketingLogFilter);
                    System.out.println("✅ Re-registered MarketingLogFilter after refresh");
                } else {
                    // 如果已存在,可能是同一个实例,无需操作
                    System.out.println("ℹ️ MarketingLogFilter already registered");
                }
            }
        } catch (Exception e) {
            System.err.println("❌ Failed to re-register TurboFilter: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

   验证通过,没问题了