Встроенный 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) обрабатывается встроенным 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)?

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

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

  • Tomcat Logging с использованием log4j? Это не та проблема, с которой я столкнулся, она не основана на Embedded 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 файл, а не как работает регистрация встраиваемых Tomcat.

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 выбран (потому что он находится в той же банке и, следовательно, "ближе" (не нужно углубляться в порядок загрузки classpath)). Вообще, это плохая практика иметь один и тот же класс (в том же пакете) на пути к классам. Это приведет только к путанице, и вы в основном никогда не узнаете, какой класс на самом деле используется (да, я знаю, что есть способы и правила, но, если коротко, это не хорошо). Таким образом, я решил вместо того, чтобы использовать 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 и почему я решил довольно быстро, чтобы в моем проекте не было одного и того же класса в одном и том же пакете:

Другие вопросы по тегам