Встроенный Tomcat с использованием log4j для ведения журнала

Я использую встроенный Tomcat 8.5.4, т.е.

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>8.5.4</version>
</dependency>

Реализация работает отлично (Tomcat работает как шарм), единственное, что меня беспокоит, это то, что встроенный Tomcat регистрируется на System.out. Внутри моего приложения я использую log4j для ведения журнала, поэтому это приводит к следующей смеси журналов (а не для регистрации Tomcat в любом файле):

...
2017-07-30 17:57:54 DEBUG EmbeddedTomcat:136 - Binding servlet 'sample' to path '/sample/*'.
Jul 30, 2017 5:57:54 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-15000"]
Jul 30, 2017 5:57:54 PM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFO: Using a shared selector for servlet write/read
Jul 30, 2017 5:57:54 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Jul 30, 2017 5:57:54 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/8.5.4
Jul 30, 2017 5:57:54 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler [http-nio-15000]
2017-07-30 17:57:54 INFO  EmbeddedTomcat:80 - Successfully started Tomcat on port 15000 (base: null, url: http://localhost:15000).
...

В этом фрагменте первая и последняя строки (после и до ...) регистрируются моим приложением с использованием log4j и конфигурации log4j (которая записывает в файл и System.out). Тем не менее, средняя часть (регистрация Tomcat) обрабатывается Embedded Tomcat, и я понятия не имею, как заставить Tomcat использовать доступный log4j (и его конфигурацию).

Я попытался добавить следующую зависимость (версия 8.5.4 недоступна в репозитории Maven или Maven Central), но безуспешно.

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-logging-log4j</artifactId>
    <version>8.5.2</version>
</dependency>

Кто-нибудь знает, как заставить Embedded Tomcat вести журнал с помощью log4j (версия 1, я не использую log4j2)?

Я посмотрел/попробовал следующие ответы StackOverflow:

  • https://tomcat.apache.org/tomcat-8.0-doc/logging.html Итак, я просмотрел документацию, в которой log4j упоминается как структура ведения журнала. В нем упоминается tomcat-juli-adapters.jar, которого я не смог найти для встроенной версии (это то же самое, что и «обычный» Tomcat?). Как бы я сделал это программно, то есть в моей реализации Embedded Tomcat.

  • Ведение журнала Tomcat с использованием log4j? На самом деле это не проблема, с которой я столкнулся, она не основана на Встроенный Tomcat, версия довольно старая, и на самом деле я использую log4j, а не System.out.

  • Встроенное ведение журнала Tomcat через logback/sl4j Этот вопрос фактически касается logback и автора упоминает, что I found some info about using a standalone tomcat with log4j, но автономный вариант отличается, я вижу, что автор использует похожие зависимости, но не уверен, что когда-либо было решение.

  • Как включить встроенное ведение журнала tomcat Сначала я подумал, что это может быть решением, но все это просто связано с дополнительным ведением журнала для Embedded Tomcat. Я хочу, чтобы Embedded Tomcat использовал приложения log4j, поэтому один файл log4j.properties определяет, как все регистрируется.

  • Вход в Embedded Tomcat Я не уверен, почему этот ответ даже помечен как правильный, но это это просто объяснение того, как Tomcat записывает файл catalina.out, а не то, как работает ведение журнала Embedded Tomcat.


person Philipp    schedule 31.07.2017    source источник
comment
Вы проверили документы Tomcat здесь   -  person JJF    schedule 31.07.2017
comment
@JJF спасибо, это одна из первых ссылок, которые я проверил. На самом деле речь идет не о Embedded Tomcat, а просто о обычном ведении журналов Tomcat. Я пытался следовать приведенным там инструкциям, но когда дело доходит до версии Embedded Tomcat, большинство из упомянутых вещей недоступны или нет способа загрузить журнал во встроенной версии.   -  person Philipp    schedule 31.07.2017


Ответы (1)


Это заняло у меня некоторое время, но после того, как я получил исходники реализации 8.5.4, я понял, что реализация juli ведения журнала добавлена ​​в банку core.

Версия ‹= 8.5.2

Поэтому я вернулся и начал работать с версией 8.5.2 и использовал tomcat-embed-logging-log4j-8.5.2.jar, а также tomcat-embed-core-8.5.2.jar. Первое, на что следует обратить внимание, это то, что в отличие от большей части онлайн-документации важно, чтобы вы не добавляли tomcat-embed-logging-juli-8.5.2.jar. При этом 8.5.2 версия работает с log4j из коробки и больше ничего не надо делать.

Версия > 8.5.2

При использовании более новой версии встроенного Tomcat, то есть 8.5.4. или даже самой новой версии 8.5.19, LogFactory уже включен в банку. Таким образом, при добавлении более старого tomcat-embed-logging-log4j-8.5.2.jar в путь к классам теперь доступны две реализации LogFactory. Первый предоставляется с core и загружает DirectJDKLog (я называю его Core-LogFactory), а второй предоставляется через log4j (обозначается как Log4j-LogFactory). Таким образом, когда LogFactory загружается из пути к классам, выбирается Core-LogFactory (потому что он находится в той же банке и, следовательно, "ближе" (нет необходимости слишком углубляться в порядок загрузки пути к классам)). Как правило, использование одного и того же класса (в одном пакете) в пути к классам является плохой практикой. Это приведет только к путанице, и вы в основном никогда не узнаете, какой класс на самом деле используется (да, я знаю, что есть способы и правила, но, короче говоря, это нехорошо). Таким образом, я решил вместо использования tomcat-embed-logging-log4j-8.5.2.jar более того, я следовал подходу ServiceLoader, который фактически реализован в Core-LogFactory более новых версий (https://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/juli/logging/LogFactory.java).

private LogFactory() {
    // Look via a ServiceLoader for a Log implementation that has a
    // constructor taking the String name.
    ServiceLoader<Log> logLoader = ServiceLoader.load(Log.class);
    Constructor<? extends Log> m=null;
    for (Log log: logLoader) {
        Class<? extends Log> c=log.getClass();
        try {
            m=c.getConstructor(String.class);
            break;
        }
        catch (NoSuchMethodException | SecurityException e) {
            throw new Error(e);
        }
    }
    discoveredLogConstructor=m;
}

Для этого я добавил файл org.apache.juli.logging.Log в папку META-INF/services (внутри моего jar/sources) и добавил полное имя моей "собственной" реализации Log, то есть net.meisen.tomcat.logging.Log4jLog, которое выглядит следующим образом:

package net.meisen.tomcat.logging;

import org.apache.juli.logging.Log;
import org.apache.log4j.Logger;

public class Log4jLog implements Log {
    private final Logger logger;

    // this constructor is important, otherwise the ServiceLoader cannot start
    public Log4jLog() {
        logger = Logger.getLogger(Log4jLog.class);
    }

    // this constructor is needed by the LogFactory implementation
    public Log4jLog(final String name) {
        logger = Logger.getLogger(name);
    }

    // now we have to implement the `Log` interface
    @Override
    public boolean isFatalEnabled() {
        return true;
    }

    // ... more isLevelEnabled()

    @Override
    public boolean isTraceEnabled() {
        return logger.isTraceEnabled();
    }

    // ... and also all the fatal(...) - trace(...) methods

    @Override
    public void fatal(final Object msg) {
        logger.fatal(msg);
    }

    @Override
    public void fatal(final Object msg, final Throwable throwable) {
        logger.fatal(msg, throwable);
    }
}

И вуаля, вот конечный результат:

2017-07-31 19:27:04 TRACE EmbeddedTomcat:48 - Initializing Tomcat on port 15000 (base: null)...
2017-07-31 19:27:33 INFO  Http11NioProtocol:69 - Initializing ProtocolHandler ["http-nio-15000"]
2017-07-31 19:27:33 INFO  NioSelectorPool:69 - Using a shared selector for servlet write/read
2017-07-31 19:27:33 INFO  StandardService:69 - Starting service [Tomcat]
2017-07-31 19:27:33 INFO  StandardEngine:69 - Starting Servlet Engine: Apache Tomcat/8.5.19
2017-07-31 19:27:34 WARN  SessionIdGeneratorBase:79 - Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [170] milliseconds.
2017-07-31 19:27:34 INFO  Http11NioProtocol:69 - Starting ProtocolHandler ["http-nio-15000"]
2017-07-31 19:27:34 INFO  EmbeddedTomcat:80 - Successfully started Tomcat on port 15000 (base: null, url: http://localhost:15000).

Приложение:

Вот несколько ссылок, которые помогли мне понять, что такое ServiceLoader, и почему я довольно быстро решил не использовать один и тот же класс в одном и том же пакете в разных jar-файлах в моем проекте:

person Philipp    schedule 01.08.2017