【问题标题】:Lazy man logging懒惰的人记录
【发布时间】:2016-04-29 14:52:23
【问题描述】:

我有一个服务 web 请求的 spring 应用程序。每个请求方法都是这样的

@RequestMapping(value = someUri, method = RequestMethod.POST)
Response someUriProcessor (SomeRequestModelMethod request) throws Exception {

    try (JsonLogger logger = new JsonLogger(request, loggingTest1Uri)){
        // get actual result
        Response result = getSomeResultMethod(request);
        // feed result to special logging object
        logger.setResult (result);
        // return result to client
        return result;
    }
}

JsonLogger 类通过close方法生成JSON对象,并通过标准的slf4j日志框架记录下来,上传到日志分析工具(Splunk),记录请求、响应、响应时间等。

在执行大量工作的 getSomeResultMethod 期间,使用标准 slf4j 和生产环境中的 logback 绑定生成大量有用的日志。 我希望这些日志包含在 JsonLogger 中,而不涉及所有底层类(它们有很多),比如使用方法 JsonLogger.appendToResultLog(String txt)。我的第一个想法是创建自己的包装 slf4j 绑定,在每次调用 logger.info、logger.error 等方法时展开堆栈跟踪,使用反射到 rest 控制器方法并在其中附加日志字符串 JsonLogger。

我有强烈的感觉,我正在发明一辆自行车。是否有或多或少的标准方法来实现所需的结果?

【问题讨论】:

  • 使用某种形式的方面?

标签: java spring spring-mvc slf4j splunk


【解决方案1】:

最终没有任何方面和反射 - 只是自定义 logback appender。

public class JsonInnerLogsAppender extends AppenderBase<ILoggingEvent> {
    public JsonInnerLogsAppender () {
        this.setName("JSON_LOGGING_INTERNAL");
    }
    @Override
    protected void append(ILoggingEvent eventObject) {
        JsonLogger.putLogInfo(eventObject.getFormattedMessage());
    }
}

还有这样的JsonLogger

public class JsonLogger implements AutoCloseable {

    /**
     * Contains mapping between current thread id and corresponding JsonLogger object
     */
    private static transient ConcurrentHashMap<Long, JsonLogger> loggersMap = new ConcurrentHashMap<>();

    /**
     * Should be configured with special appender pattern, like
     * <encoder>
     *     <pattern>%date{ISO8601} [%-18thread] %-5level %msg %logger{70}%n</pattern>
     * </encoder>
     */
    private transient Logger logger = LoggerFactory.getLogger(getClass());
    private transient static Gson gson = new Gson();


    /**
     * Container that would be serialized to JSON after close and added to JSON log
     */
    private final JsonLogContainer logContainer;

    public JsonLogger (Object request, HttpServletRequest servletRequest) {
        this.logContainer = new JsonLogContainer(gson.toJson(request), servletRequest.getRequestURI());
        // request is started
        loggersMap.put(Thread.currentThread().getId(), this);
    }


    /**
     * @param logString formatted log string, called from custom appender
     */
    public static void putLogInfo (String logString) {
        // find appropriate JsonLogger instance (if any) and append log to it
        JsonLogger jsonLogger = loggersMap.get(Thread.currentThread().getId());
        if ( null != jsonLogger ) {
            jsonLogger.putLogToContainer(logString);
        }
    }

    private void putLogToContainer (String logString) {
        logContainer.putLog(logString);
    }

    @Override
    public void close() throws Exception {
        String log = this.logContainer.getLog(new Date());
        // request is completed
        loggersMap.remove(Thread.currentThread().getId());
        // put record to JSON log
        logger.info(log);
    }

    public void setResult(Object result) {
        logContainer.setResponse(gson.toJson(result));
    }
}

【讨论】:

    【解决方案2】:

    确实有一些更简单的选择。我认为使用 spring 最简洁的方法是创建一个自定义拦截器(这很像一个 servlet 过滤器,但功能更完善)。

    您可以在Spring documentation 中阅读更多关于它们的信息。

    我忘了补充一点,使用Logback Access 可能有一个更简单的解决方案,这是一个从 logback 到有关请求和响应的日志信息的小型库。

    【讨论】:

    • 记录 HTTP 请求和响应没有问题。我什至不必为此使用应用程序日志记录——这可以通过服务器来实现。拦截器可能有助于摆脱我提供的代码的平静,但它们有一些限制,例如无法直接获取请求和响应主体。我在应用程序中没有太多 API 方法,因此更容易像在代码 sn-p 中那样包装它们。真正的问题是在请求执行期间收集与请求相关的所有日志,而无需接触代码。不确定方面如何提供帮助 - 需要考虑这一点)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 2011-08-01
    • 2015-09-06
    • 2011-07-16
    • 1970-01-01
    相关资源
    最近更新 更多