懒人伐木

时间:2016-01-22 21:32:24

标签: java spring spring-mvc slf4j splunk

我有一个提供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等方法时展开stacktrace,使用反射到rest控制器方法并在其中附加日志字符串JsonLogger。

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

2 个答案:

答案 0 :(得分: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));
    }
}

答案 1 :(得分:0)

确实有一些更简单的选择。我认为spring最干净的方法是创建一个自定义Interceptor(它非常像servlet过滤器,但具有更精细的功能)。

您可以在Spring documentation

中详细了解它们

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