2016-01-22 61 views
3

我有一個春天的應用程序提供Web請求。每個請求方法看起來像這樣懶惰的人登錄

@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類產生緊密方法JSON對象,並且經由其被上傳到日誌分析工具(Splunk的)標準SLF4J日誌框架記錄它,它記錄請求,響應,響應時間等上。

在執行大量工作的getSomeResultMethod執行期間使用帶有生產中的logback綁定的標準slf4j生成大量有用的日誌。 我希望這些日誌被包含在JsonLogger中,而不用觸及所有的基礎類(其中有很多),比如使用方法JsonLogger.appendToResultLog(String txt)。我的第一個想法是創建自己的包裝slf4j綁定,每次調用logger.info時釋放堆棧跟蹤,logger.error等方法使用反射到rest控制器方法,並在其中追加日誌字符串JsonLogger。

我有強烈的感覺,我正在發明一輛自行車。是否有任何或多或少的標準方法來達到要求的結果?

+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)); 
    } 
} 
0

確實有一些簡單的選擇。我認爲Spring最簡潔的方法是創建一個自定義攔截器(它非常像一個servlet過濾器,但功能更加精緻)。

您可以在Spring documentation中閱讀更多關於它們的內容。

我忘了補充說,使用Logback Access可能會有一個更簡單的解決方案,它是從logback記錄有關請求和響應信息的小型文件。

+0

記錄HTTP請求和響應沒有問題。我甚至不需要使用應用程序日誌記錄來實現這一點 - 這可以通過服務器來實現。攔截器可能有助於擺脫我提供的代碼的和平,但它們有一些限制,例如沒有直接的方式來獲取請求和響應的機構。我在應用程序中沒有太多的API方法,所以更容易像代碼片段一樣包裝它們。真正的問題是在執行過程中收集與請求相關的所有日誌而不觸及代碼。 不知道方面如何可以幫助 - 需要考慮這一點) – alexey