Log4j, налаштовуючи веб-програму на використання відносного шляху


80

У мене є веб-додаток Java, який повинен бути розгорнутий на машинах Win або Linux. Тепер я хочу додати log4j для ведення журналу, і я хотів би використовувати відносний шлях до файлу журналу, оскільки я не хочу змінювати шлях до файлу при кожному розгортанні. Контейнером, швидше за все, буде Tomcat, але не обов'язково.

Який найкращий спосіб це зробити?


3
Це насправді лише половина питання. Наявність динамічного шляху до файлу журналу - це чудово, але як щодо самого файлу конфігурації. Якщо це просто розташування файлу журналу, яке є динамічним, тоді ви мали б однакові рівні журналів у всіх місцях, де це розгорнуто, і я не думаю, що це бажано. Я хотів би знати найкращий підхід для динамічного вказівки конфігурації, щоб моє середовище розробника могло входити в DEBUG та продавати в INFO / WARN. Як ти гадаєш?
Лукас

Відповіді:


100

Tomcat встановлює системну властивість catalina.home. Ви можете використовувати це у файлі властивостей log4j. Щось на зразок цього:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${catalina.home}/logs/LogFilename.log

На Debian (включаючи Ubuntu) ${catalina.home}не буде працювати, оскільки це вказує на / usr / share / tomcat6, який не має посилання на / var / log / tomcat6. Тут просто використовуйте ${catalina.base}.

Якщо ви використовуєте інший контейнер, спробуйте знайти подібну системну властивість або визначте власну. Встановлення властивості системи залежить від платформи та контейнера. Але для Tomcat на Linux / Unix я б створив setenv.sh в каталозі CATALINA_HOME / bin. Він містив би:

export JAVA_OPTS="-Dcustom.logging.root=/var/log/webapps"

Тоді вашим log4j.properties буде:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${custom.logging.root}/LogFilename.log

2
Чесно кажучи, я не бачу, яку перевагу має цей підхід над використанням прослуховувача, про який я пояснив у своїй відповіді. Мені байдуже, який це контейнер, він просто працюватиме незалежно від того, де я його розгортаю, тоді як у вашому підході я змінити значення, якщо я зміню середовище.
Ікер Хіменес

4
Обидва рішення використовують системні властивості, ми просто встановлюємо їх по-різному. Це насправді залежить лише від гнучкості та простоти. Ми керуємо усіма серверами Tomcat, на яких працює наш додаток, тому мені подобається гнучкість. Якщо ви розповсюджуєте війну для використання сторонніми особами, простота має сенс
Стів К

54

Я нарешті зробив це таким чином.

Додано ServletContextListener, який робить наступне:

public void contextInitialized(ServletContextEvent event) {
    ServletContext context = event.getServletContext();
    System.setProperty("rootPath", context.getRealPath("/"));
}

Потім у файлі log4j.properties:

log4j.appender.file.File=${rootPath}WEB-INF/logs/MyLog.log

Роблячи це таким чином, Log4j запише у потрібну папку, якщо ви не використовуєте її до встановлення системної властивості "rootPath". Це означає, що ви не можете використовувати його з самого ServletContextListener, але ви повинні мати можливість використовувати його з будь-якої іншої точки програми.

Він повинен працювати на кожному веб-контейнері та ОС, оскільки це не залежить від властивостей конкретного контейнера та на нього не впливають проблеми із конкретними шляхами ОС. Протестовано на веб-контейнерах Tomcat та Orion, а також на Windows та Linux, і дотепер він працює чудово.

Як ти гадаєш?


4
Це гарна ідея, але я думаю, що catalina.home може бути безпечнішим у використанні, оскільки він завжди буде встановлений / доступний перед ініціалізацією коду log4j.
matt b

6
Це було б правдою, якби я використовував лише Tomcat, але моєю вимогою є те, що він повинен працювати на будь-якому контейнері з конфігурацією 0. Мій підхід це виконує, і досі ніхто не пропонував кращого підходу для цього.
Ікер Хіменес

2
Це рішення може працювати для веб-додатків, які використовують сервлети, але рішення Стіва К. ( stackoverflow.com/questions/216781/… ) працює для будь-якого додатка, який використовує Log4j.
Дерек Махар

2
Рішення Спенсера К., яке також базується на відносних шляхах, працює для будь-якої програми, яка використовує Log4j, припускаючи, що ви встановили базовий каталог на передбачуваний шлях.
Дерек Махар

12
Це рішення працює лише в тому випадку, якщо для його використання налаштовано один веб-додаток, оскільки властивості системи є загальнодоступними для створення другої програми, яка запускається, перезапише значення, встановлене першим веб-додатком. Ви можете дати кожному веб-додатку унікальну назву властивості, але якщо ви збираєтеся це зробити, ви можете просто використовувати $ {catalina.home} і додати унікальну частину шляху у файл log4j.properties, оскільки він менш схильний до помилок .
3urdoch,

14

Якщо ви використовуєте Spring, ви можете:

1) створити файл конфігурації log4j, наприклад "/WEB-INF/classes/log4j-myapp.properties" НЕ називати його "log4j.properties"

Приклад:

log4j.rootLogger=ERROR, stdout, rollingFile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n

log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=${myWebapp-instance-root}/WEB-INF/logs/application.log
log4j.appender.rollingFile.MaxFileSize=512KB
log4j.appender.rollingFile.MaxBackupIndex=10
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.rollingFile.Encoding=UTF-8

Ми визначимо "myWebapp-instance-root" пізніше в пункті (3)

2) Вкажіть розташування конфігурації в web.xml:

<context-param>
  <param-name>log4jConfigLocation</param-name>
  <param-value>/WEB-INF/classes/log4j-myapp.properties</param-value>
</context-param>

3) Вкажіть унікальне ім'я змінної для кореня вашого веб-додатка, наприклад "myWebapp-instance-root"

<context-param>
  <param-name>webAppRootKey</param-name>
  <param-value>myWebapp-instance-root</param-value>
</context-param>

4) Додайте Log4jConfigListener:

<listener>
  <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

Якщо ви вибрали інше ім'я, не забудьте змінити його також у log4j-myapp.properties.

Дивіться мою статтю (лише італійською ... але це повинно бути зрозуміло): http://www.megadix.it/content/configurare-path-relativi-log4j-utilizzando-spring

ОНОВЛЕННЯ (01.08.2009) Я переклав свою статтю англійською мовою: http://www.megadix.it/node/136


6

Просто коментар до рішення Ікера .

ServletContextє хорошим рішенням для вашої проблеми. Але я не думаю, що це добре для обслуговування. Більшу частину часових файлів потрібно зберігати протягом тривалого часу.

Оскільки ServletContextробить файл під розгорнутим файлом, він буде видалений при повторному розгортанні сервера. Я пропоную використовувати батьківську папку rootPath замість дочірньої.


5

Чи не використовує log4j просто кореневий каталог програми, якщо ви не вказали кореневий каталог у властивості шляху FileAppender? Отже, ви просто повинні мати можливість використовувати:

log4j.appender.file.File = журнали / MyLog.log

Минув деякий час з того часу, як я займався веб-розробкою Java, але це, здається, є найбільш інтуїтивно зрозумілим, а також не стикається з іншими журналами, на жаль, з іменами, що записуються в каталог $ {catalina.home} / logs.


4
З того, що я бачив, він може використовувати домашній каталог користувача або домашній каталог контейнера, якщо ви не вказали абсолютний шлях. Ненадійно.
Ікер Хіменес

1
@Iker: Чому ви не можете просто явно встановити кореневий каталог програми у вашому контейнері або конфігурації програми? Як тільки ви це зробите в розробці та виробництві, ви зможете надійно використовувати відносні шляхи. Якщо припустити, що кореневий каталог встановлений правильно, відносні шляхи є найбільш портативним (перерозміщуваним) рішенням.
Дерек Махар

1
Я знаю, що це стара публікація, але я хотів зробити це для інших. Якщо для кореневого каталогу програми явно не встановлено кореневий каталог програми, я вважаю, що за замовчуванням використовується той каталог, з якого ви запустили tomcat.
Geren White

2

Як подальший коментар на https://stackoverflow.com/a/218037/2279200 - це може зламатися, якщо веб-програма неявно запускає інші сервіси ServletContextListener, які можуть бути викликані раніше і які вже намагаються використовувати log4j - у цьому випадку, конфігурація log4j буде прочитана та проаналізована ще до того, як буде встановлено властивість, що визначає кореневий каталог журналу => файли журналів з'являться десь нижче поточного каталогу (поточний каталог під час запуску tomcat).

Я міг лише подумати про таке рішення цієї проблеми: - перейменуйте файл log4j.properties (або logj4.xml) у те, що log4j не буде читати автоматично. - У вашому контекстному фільтрі після встановлення властивості викличте допоміжний клас DOM / PropertyConfigurator, щоб переконатись, що ваш log4j -. {Xml, properties} прочитаний - Скиньте конфігурацію log4j (IIRC існує метод для цього)

Це трохи груба сила, але, на думку, це єдиний спосіб зробити її водонепроникною.


1

Якщо ви використовуєте Maven, я маю для вас чудове рішення:

  1. Відредагуйте файл pom.xml, щоб включити такі рядки:

    <profiles>
        <profile>
            <id>linux</id>
            <activation>
                <os>
                    <family>unix</family>
                </os>
            </activation>
            <properties>
                <logDirectory>/var/log/tomcat6</logDirectory>
            </properties>
        </profile>
        <profile>
            <id>windows</id>
            <activation>
                <os>
                    <family>windows</family>
                </os>
            </activation>
            <properties>
                <logDirectory>${catalina.home}/logs</logDirectory>
            </properties>
        </profile>
    </profiles>
    

    Тут ви визначаєте logDirectoryвластивість спеціально для сімейства ОС.

  2. Використовуйте вже визначену logDirectoryвластивість у log4j.propertiesфайлі:

    log4j.appender.FILE=org.apache.log4j.RollingFileAppender
    log4j.appender.FILE.File=${logDirectory}/mylog.log
    log4j.appender.FILE.MaxFileSize=30MB
    log4j.appender.FILE.MaxBackupIndex=10
    log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.FILE.layout.ConversionPattern=%d{ISO8601} [%x] %-5p [%t] [%c{1}] %m%n
    
  3. Це воно!

PS: Я впевнений, що цього можна досягти за допомогою Ant, але, на жаль, у мене недостатньо досвіду з цим.


1

Я пропоную, щоб файл журналу завжди реєструвався над кореневим контекстом webApp, тому, якщо ми передислокуємо webApp, ми не хочемо замінювати наявні файли журналів.


1

Моє рішення аналогічно Iker Хіменес рішення , але замість використання System.setProperty(...)використання I org.apache.log4j.PropertyConfigurator.configure(Properties). Для цього мені потрібно log4j , щоб бути не в змозі знайти свою конфігурацію самостійно і я завантажити його вручну (обидві точки , описані в Вольфганга Лібіх в відповідь ).

Це працює для Jetty та Tomcat, автономно або працює з IDE, вимагає нульової конфігурації, дозволяє зберігати журнали кожного додатка у власній папці, незалежно від кількості програм всередині контейнера (що є проблемою з Systemрішенням на основі). Таким чином, можна також розмістити конфігураційний файл log4j де завгодно у веб-програмі (наприклад, в одному проекті у нас були всі конфігураційні файли всередині WEB-INF/).

Подробиці:

  1. У мене є свої властивості у log4j-no-autoload.propertiesфайлі в шляху до класу (наприклад, у моєму проекті Maven він спочатку знаходиться src/main/resources, упаковується в WEB-INF/classes),
  2. У ньому є додаток файлів, налаштований як, наприклад:

    log4j.appender.MyAppFileAppender = org.apache.log4j.FileAppender
    log4j.appender.MyAppFileAppender.file = ${webAppRoot}/WEB-INF/logs/my-app.log
    ...
    
  3. І у мене є такий прослуховувач контексту (стає набагато коротшим із синтаксисом "try-with-resource" на Java 7):

    @WebListener
    public class ContextListener implements ServletContextListener {
        @Override
        public void contextInitialized(final ServletContextEvent event) {
            Properties props = new Properties();
            InputStream strm =
                ContextListener.class.getClassLoader()
                    .getResourceAsStream("log4j-no-autoload.properties");
            try {
                props.load(strm);
            } catch (IOException propsLoadIOE) {
                throw new Error("can't load logging config file", propsLoadIOE);
            } finally {
                try {
                    strm.close();
                } catch (IOException configCloseIOE) {
                    throw new Error("error closing logging config file", configCloseIOE);
                }
            }
            props.put("webAppRoot", event.getServletContext().getRealPath("/"));
            PropertyConfigurator.configure(props);
            // from now on, I can use LoggerFactory.getLogger(...)
        }
        ...
    }
    

1

Ви можете вказати відносний шлях до файлу журналу, використовуючи робочий каталог :

appender.file.fileName = ${sys:user.dir}/log/application.log

Це не залежить від контейнера сервлетів і не вимагає передачі користувацької змінної в системне середовище.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.