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

如何为Log4j2 HTTP Appender生成可自动刷新的授权头?

技术问询

是否有合适的方法为Log4j2 HTTP Appender创建每次POST请求时可重新生成的Authorization头?

背景说明

每次向API提交POST数据时都必须计算Authorization头,这是该API唯一接受的认证方式,由签名、ID和时间戳组成。由于API不接受超过X分钟的旧认证头,因此无法将其固定设置为环境变量。

当前实现及问题

我尝试在Log4j2配置中使用脚本生成该认证头并用于HTTP Appender,参考了Log4j2的脚本配置文档。

Log4j2配置文件(log4j2http.xml)

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" name="MyApp">
  <Scripts>
    <Script name="selector" language="groovy">
      <![CDATA[

      import org.apache.commons.codec.binary.Hex
      import javax.crypto.Mac
      import javax.crypto.spec.SecretKeySpec
      import java.security.MessageDigest
      def id="XXXXXXXXXXXXXx"
      Long epoch_time = System.currentTimeMillis()
      Mac hmac = Mac.getInstance("HmacSHA256")
      hmac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"))
      def signature = Hex.encodeHexString(hmac.doFinal("GET${epoch_time}${path}".getBytes())).bytes.encodeBase64()
  
      return "${id}:${signature}:${epoch_time}"
            ]]>
    </Script>
  </Scripts>
  <Appenders>
    <Http name="Http" url="https://XXX/rest/log/ingest" method="POST">
      
      <KeyValuePair key="_Id:" value="{Id:&quot;588&quot;}" />
      <Property name="Authorization" value="${result}" />
      <Property name="Accept" value="application/json" />
      <JsonLayout properties="true" />
    </Http>

  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="Http" />
    </Root>
  </Loggers>
</Configuration>

应用代码

package App;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import java.io.*;

public class App {

    public static void main(String[] args) {

        System.setProperty("log4j2.Script.enableLanguages","groovy");
        System.setProperty("log4j2.debug","false");
        
        Logger logger = LogManager.getLogger("App");
        LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
        
        File file = new File("log4j2http.xml"); 
        context.setConfigLocation(file.toURI());     
        
        logger.debug("Debug log");

    }
}

目前没有报错,但${result}为空,怀疑是Groovy库导入失败或者脚本本身有错误。开启debug模式后也无法确认脚本是否正确执行,不确定当前这种方法是否可行。

相关日志片段

2022-10-09 00:58:39,198 main DEBUG Installed 2 script engines
2022-10-09 00:58:39,251 main DEBUG Groovy Scripting Engine version: 2.0, language: Groovy, threading: MULTITHREADED, compile: true, names: [groovy, Groovy], factory class: org.codehaus.groovy.jsr223.GroovyScriptEngineFactory
2022-10-09 00:58:39,252 main DEBUG PluginManager 'Core' found 127 plugins
2022-10-09 00:58:39,252 main DEBUG PluginManager 'Level' found 0 plugins
2022-10-09 00:58:39,257 main DEBUG PluginManager 'Lookup' found 16 plugins
2022-10-09 00:58:39,259 main DEBUG Building Plugin[name=Script, class=org.apache.logging.log4j.core.script.Script].
2022-10-09 00:58:39,278 main DEBUG PluginManager 'TypeConverter' found 26 plugins
2022-10-09 00:58:39,292 main DEBUG createScript(name="selector", language="groovy", scriptText=".................")
2022-10-09 00:58:39,292 main DEBUG Building Plugin[name=scripts, class=org.apache.logging.log4j.core.config.ScriptsPlugin].
2022-10-09 00:58:39,295 main DEBUG createScripts(={selector})
2022-10-09 00:58:39,297 main DEBUG Script selector is compilable
2022-10-09 00:58:40,383 main DEBUG Building Plugin[name=KeyValuePair, class=org.apache.logging.log4j.core.util.KeyValuePair].
2022-10-09 00:58:40,388 main DEBUG KeyValuePair$Builder(key="_Id:", value="{Id:"588"}")
2022-10-09 00:58:40,389 main DEBUG Building Plugin[name=property, class=org.apache.logging.log4j.core.config.Property].
2022-10-09 00:58:40,389 main DEBUG createProperty(name="Authorization", value="${result}", value="null")

解决方案

问题根源分析

  1. 脚本变量引用错误:配置中用${result}获取脚本返回值,但Log4j2脚本不会自动将返回值绑定到result变量,需通过脚本Lookup调用,格式应为${script:selector}(selector是你定义的脚本名称)。
  2. 脚本未定义变量:Groovy脚本里使用了key和path但未初始化,执行时会抛出异常,只是Log4j2默认吞了异常,导致返回值为空。
  3. 脚本执行时机错误:默认<Script>标签在配置加载时仅执行一次,认证头只会生成一次,后续请求会使用过期的时间戳。

修正步骤

1. 改用动态脚本Lookup,确保每次请求执行脚本

修改HTTP Appender的Authorization属性值:

<Property name="Authorization" value="${script:selector}" />

2. 修复Groovy脚本中的问题

补充变量定义,修正请求方法不一致的问题,调整Base64编码写法:

import org.apache.commons.codec.binary.Base64
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

// 替换为你的实际密钥和API路径
def id = "XXXXXXXXXXXXXx"
def key = "your-real-secret-key"
def path = "/rest/log/ingest"

Long epoch_time = System.currentTimeMillis()
Mac hmac = Mac.getInstance("HmacSHA256")
hmac.init(new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"))
// 请求方法与实际POST保持一致
def signData = "POST${epoch_time}${path}"
byte[] hmacBytes = hmac.doFinal(signData.getBytes("UTF-8"))
def signature = Base64.encodeBase64String(hmacBytes)

return "${id}:${signature}:${epoch_time}"

3. 确保依赖包齐全

项目依赖需包含:

  • Log4j2核心包
  • Log4j2 HTTP Appender包
  • Groovy脚本引擎包
  • Apache Commons Codec包(用于编码)

4. 启用脚本异常日志

在配置中添加脚本日志记录器,方便排查问题:

<Loggers>
  <Logger name="org.apache.logging.log4j.core.script" level="trace"/>
  <Root level="trace">
    <AppenderRef ref="Http" />
  </Root>
</Loggers>

替代方案:自定义HTTP Appender插件

如果脚本方式不够灵活,可编写自定义Log4j2 HTTP Appender插件,继承HttpAppender并重写buildRequest方法,在每次请求前动态生成Authorization头,完全控制认证逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 02:10:52