web-dev-qa-db-ja.com

RestTemplateの応答のロギング

SpringのRestTemplateを使用して応答をログに記録したい。最初はClientHttpRequestInterceptorを使用してそれを実行する必要があると思いましたが、すべてのリクエストのセットを別のファイルに記録する必要があるため、すべてのリクエストセットに対して新しいRestTemplateを使用して新しいClientHttpRequestInterceptorを作成する必要があることがわかりました(他のすべての構成は同じ)。

しかし、RestTemplateは作成に非常に負荷がかかるオブジェクトであり、作成時間の約70%がMessageConvertersを作成しているときにわかりました。

それで-この要件を実装するより良い方法はありますか?複数のMessageConvertersインスタンスでRestTemplateを共有して、パフォーマンスを大幅に向上させることは可能ですか?

[〜#〜] edit [〜#〜]これが私のコードのサンプルです:

protected MyResponse<MyInterface> getInterfaces(RestTemplate restClient, RestParams inputParams, String cookie, File outputFile) {

    Request devInterfacesRequest = getRequest(RequestType.INTERFACES, inputParams, cookie);

    return restClient.postForObject(inputParams.getUrl(), devInterfacesRequest , new ParameterizedTypeReference<MyResponse<MyInterface>>(){});
}

OutputFileはどこに追加できますか?

5
Nati

さて、私はこの質問で考えていました、そしてこれは私が得るものです:

制限事項

引用質問の所有者:

  1. 独自のRestTemplateを実装するのは重いです

しかし、RestTemplateは作成するのにかなり高価なオブジェクトであり、作成時間の約70%がMessageConvertersを作成しているのを見ました。

  1. RestTemplateは構築後、スレッドセーフではありません。しかし、それは構築後ではありません。続きを参照してください RestTemplateスレッドは安全ですか?

概念的には、JdbcTemplate、JmsTemplate、およびSpring Frameworkやその他のポートフォリオプロジェクトにあるその他のさまざまなテンプレートと非常に似ています。これは、たとえば、RestTemplateが構築されるとスレッドセーフであることを意味します

この最後の機能は、構築後にRestTemplateを変更しないことを推奨しています。だから私は新しいインターセプターを追加したり削除したりしません

  1. 私たちはシステムとその要件、境界などに関する多くの詳細を見逃しています...

  2. これらのファイルの目的がわかりません。そのため、提案するソリューションの妥当性が損なわれます。

何がありますか?

私が正しい場合、 Appache Log4J が構成されています。 (@ナティはチャットで教えてくれました)。

そこで、Log4jをロガーツールとして使用することにしました。ただし、Drawbacksセクションを確認してください。 (この答えを読んでいる誰かが私の疑問を払拭してくれることを願っています

なぜ自分のファイル管理の代わりにLog4jを使用するのですか?Log4jに次の機能を委任したかったので、ファイルへのアクセスパターンの使用法フィルターの使用法(オプション)レベルの使用法、...

解決

LoggerHelper

可能な限りカップリングの少ないレイヤーを介してソリューションを移植できるように、ヘルパーを実装しました。

_package org.myworkouts.helper;

import Java.io.File;
import Java.io.IOException;

import org.Apache.log4j.FileAppender;
import org.Apache.log4j.Level;
import org.Apache.log4j.LogManager;
import org.Apache.log4j.Logger;
import org.Apache.log4j.PatternLayout;


public class LoggerHelper {

    // Helper's own logger
    private static final Logger LOGGER = LogManager
            .getLogger(LoggerHelper.class);
    private static final String DEFAULT_LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n";
    private static final String LOGGER_PREFIX = "logger";
    private static final String APPENDER_PREFIX = "appender";

    private static LoggerHelper instance = null;

    // Singleton
    private LoggerHelper() {
        super();
    }

    public static LoggerHelper getInstance() {
        if (instance == null) {
            instance = new LoggerHelper();
        }
        return instance;
    }

    /**
     * Get the logger by its name or create if never was instanciated previously
     * 
     * @throws IOException
     */
    public Logger getCustomFileLogger(String filePath) throws IOException {
        String loggerName = resolveLoggerName(filePath);

        Logger logger = LogManager.exists(loggerName);
        if (logger == null) {
            LOGGER.info("Creating new Logger: ".concat(loggerName));
            logger = LogManager.getLogger(loggerName);
        }
        setLevel(logger, Level.INFO).addFileAppender(logger, filePath);
        return logger;
    }

    private LoggerHelper addFileAppender(Logger logger, String filePath)
            throws IOException {
        if (logger != null) {
            String appenderName = resolveAppenderName(filePath);
            if (logger.getAppender(appenderName) == null) {
                logger.addAppender(new FileAppender(new PatternLayout(
                        DEFAULT_LOG_PATTERN), filePath, Boolean.TRUE));
            }
        }
        return this;
    }

    private LoggerHelper setLevel(Logger logger, Level level) {
        if (logger != null) {
            logger.setLevel(level);
        }
        return this;
    }

    /* Optionl. Generate a custom name for a new Appender */
    private String resolveAppenderName(String filePath) {
        return APPENDER_PREFIX.concat(File.pathSeparator).concat(
                getNameWithoutExtension(filePath));
    }

    /* Optionl. Generate a custom name for a new Logger */
    private String resolveLoggerName(String filePath) {
        return LOGGER_PREFIX.concat(File.pathSeparator).concat(
                getNameWithoutExtension(filePath));
    }

    //Took from com.google.common.io.Files
    //I had it in my workspace but I didn't want to use it at this
    //class
    private String getNameWithoutExtension(String filePath) {
        String fileName = new File(filePath).getName();
        int dotIndex = fileName.lastIndexOf('.');
        return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
    }
}
_

実装

所有者のコードにソリューションを添付する

_protected MyResponse<MyInterface> getInterfaces(RestTemplate restClient, RestParams inputParams, String cookie, File outputFile) {

    Request devInterfacesRequest = getRequest(RequestType.INTERFACES, inputParams, cookie);

     MyResponse<MyInterface> response =  restClient.postForObject(inputParams.getUrl(), devInterfacesRequest , new ParameterizedTypeReference<MyResponse<MyInterface>>(){});
     try{
           Logger customLogger = LoggerHelper.getInstance().getCustomFileLogger(outputFile.getPath());
           //... we can also to parse it to Json, XML, whatever...
           customLogger.info(response.toString());
     }catch(IOException ex){
          //... what do we have to do if logger could not be instanciated?
     }
     return response;
}
_

実装は、getInterfacesbucleを持っている(私が思う)for呼び出し元に移動することもでき、 outputFile。次に、getCustomFileLogger(...)が1回だけ呼び出されます。

テスト

_public class LoggerHelperTest {

    public static final Logger LOGGER = LogManager.getLogger(LoggerHelperTest.class);

    @Test
    public void getCustomLoggerTest() throws IOException{
        Logger customLogger = LoggerHelper.getInstance().getCustomFileLogger("file.txt");
        Assert.assertNotNull("CustomLogger should not be null",customLogger);
        Assert.assertEquals("CustomLogger's level should be INFO by default",Level.INFO, customLogger.getLevel());
        Assert.assertTrue("CustomLogger's name should contain file's name on its name",customLogger.getName().contains("file"));
        customLogger.info("Trace 1");
        customLogger.info("Trace 2");
        customLogger.info("Trace 3");
    }
}
_

file.txt

私が得た出力

_2016-05-03 13:38:56 INFO  logger:file:47 - Trace 1
2016-05-03 13:38:56 INFO  logger:file:48 - Trace 2
2016-05-03 13:38:56 INFO  logger:file:49 - Trace 3
_

疑い

これは提案です。それは提案や改善に開かれています。私の解決策にも疑問があります。 (私は解決策を強調しませんでした):

  1. 他のプロセスによってFile outputFileがロック(オープン)されていますか?その後、IOExceptionがスローされます。
  2. ホーム何か違いoutputFileは? 10,100,1000? Loggerの多くをLog4jに保持できますか?
  3. これらoutputFileは実行時に移動されますか?移動/削除/置換された場合、log4jはログインを続けることができません。
  4. 手放す代わりにIOExceptionをキャッチしましょうか。 LoggerHelperユーザーに委任して、何をすべきかを決定します...

Log4jを使用したのは、ロガーを分離して全員を独自のファイルに印刷することを要件とすることを念頭に置いていたためです。しかし、ログトレースのみが含まれることを期待しています(詳細なレビューまたはデバッグの場合)。

ただし、ファイルのコンテンツが重要である場合、コンテンツが重要であると、ソリューションは適切ではありません。

コンテンツが重要で、ファイル管理を実装したくない場合は、これらのログをnoSQLdb's( mongoDB、または私たちのプラットフォームに適したその他のすべて)。これも大量のデータをサポートできます。データは後で適切な要求で取得できます

1
Laiv