You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中Undertow访问日志%u字段的系统化自定义实现方案

系统化实现Undertow访问日志自定义%u字段的方案

现有通过字符串替换%u为固定值的方式虽然简单,但存在明显局限:只能设置静态值,无法动态获取实际请求中的用户信息;且依赖字符串替换逻辑,耦合度高,后续扩展其他字段时维护成本高。以下是两种更系统化的实现方案:

方案一:自定义Undertow AccessLogElement(推荐)

Undertow的访问日志模式由多个AccessLogElement实现类支撑,比如%u默认对应UserElement。我们可以通过自定义AccessLogElement替换默认实现,实现动态自定义用户值的需求。

步骤1:自定义AccessLogElement实现类

import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.accesslog.AccessLogElement;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

public class CustomUserAccessLogElement implements AccessLogElement {

    @Override
    public void log(HttpServerExchange exchange, StringBuilder builder) {
        // 可根据实际场景获取用户信息:比如从Spring Security上下文、请求头、参数等
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String user = "anonymous";
        if (authentication != null && authentication.isAuthenticated() 
            && !"anonymousUser".equals(authentication.getName())) {
            user = authentication.getName();
            // 此处可对用户信息做自定义处理,比如脱敏、添加标识前缀等
            user = "custom_" + user;
        }
        builder.append(user);
    }
}

步骤2:自定义WebServerFactoryCustomizer替换默认元素

import io.undertow.server.handlers.accesslog.AccessLogHandler;
import io.undertow.server.handlers.accesslog.AccessLogParser;
import io.undertow.server.handlers.accesslog.DefaultAccessLogParser;
import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class CustomUndertowAccessLogCustomizer implements WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory> {

    @Override
    public void customize(ConfigurableUndertowWebServerFactory factory) {
        factory.addDeploymentInfoCustomizers(deploymentInfo -> {
            String pattern = deploymentInfo.getDeploymentSettings().getAccessLogPattern();
            // 构建自定义元素映射,替换%u对应的默认实现
            Map<String, AccessLogElement> customElements = new HashMap<>();
            customElements.put("u", new CustomUserAccessLogElement());
            
            // 创建自定义日志解析器
            AccessLogParser parser = DefaultAccessLogParser.builder()
                    .setPattern(pattern)
                    .addCustomElements(customElements)
                    .build();
            
            // 替换默认的AccessLogHandler
            deploymentInfo.setAccessLogHandlerFactory(exchange -> 
                    new AccessLogHandler(exchange.getNextHandler(), parser, 
                            deploymentInfo.getAccessLogReceiver(), deploymentInfo.getAccessLogPrefix(), 
                            deploymentInfo.getAccessLogSuffix(), deploymentInfo.isAccessLogResolveHostname()));
        });
    }
}

优势

  • 遵循Undertow访问日志的原生设计规范,扩展性强
  • 支持动态获取请求上下文内的用户信息,而非静态固定值
  • 可轻松扩展其他自定义日志字段,只需新增对应AccessLogElement实现

方案二:自定义AccessLogReceiver处理日志输出

若不想修改日志元素的解析逻辑,可通过自定义AccessLogReceiver,在日志输出前修改%u对应的内容。

实现代码

import io.undertow.server.handlers.accesslog.AccessLogReceiver;
import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class AccessLogReceiverCustomizer implements WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory> {

    @Override
    public void customize(ConfigurableUndertowWebServerFactory factory) {
        factory.addDeploymentInfoCustomizers(deploymentInfo -> {
            AccessLogReceiver originalReceiver = deploymentInfo.getAccessLogReceiver();
            // 包装原有接收器,修改日志内容后再输出
            deploymentInfo.setAccessLogReceiver((message) -> {
                // 示例:将日志中%u对应的字段替换为自定义内容,需匹配日志格式调整正则
                String modifiedMessage = message.replaceAll("(\\S+) (\\S+) (\\S+)", "$1 $2 custom_user");
                originalReceiver.logMessage(modifiedMessage);
            });
        });
    }
}

注意事项

  • 依赖日志输出的字符串格式,若日志模式修改,替换逻辑需同步调整,耦合度较高
  • 适合快速实现简单的日志内容修改,不适合复杂动态场景

内容的提问来源于stack exchange,提问作者Akash Shinde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 13:32:49