Log4j, konfigurowanie aplikacji internetowej do używania ścieżki względnej

80

Mam aplikację internetową java, którą należy wdrożyć na komputerach z systemem Win lub Linux. Chcę teraz dodać log4j do rejestrowania i chciałbym użyć względnej ścieżki do pliku dziennika, ponieważ nie chcę zmieniać ścieżki pliku przy każdym wdrożeniu. Kontener najprawdopodobniej będzie Tomcat, ale niekoniecznie.

Jaki jest najlepszy sposób na zrobienie tego?

Iker Jimenez
źródło
3
To naprawdę tylko połowa pytania. Posiadanie dynamicznej ścieżki do pliku dziennika jest świetne, ale co z samym plikiem konfiguracyjnym. Jeśli jest to tylko lokalizacja pliku dziennika, która jest dynamiczna, miałbyś te same poziomy dzienników we wszystkich miejscach, w których jest wdrażany, i nie sądzę, że jest to pożądane. Chciałbym poznać najlepsze podejście do dynamicznego określania konfiguracji, aby moje środowisko deweloperskie mogło logować się w DEBUG i produkować w INFO / WARN. Co myślisz?
Lucas,

Odpowiedzi:

100

Tomcat ustawia właściwość systemową catalina.home. Możesz tego użyć w swoim pliku właściwości log4j. Coś takiego:

log4j.rootCategory=DEBUG,errorfile

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

Na Debianie (w tym Ubuntu), ${catalina.home}nie będzie działać, ponieważ wskazuje na / usr / share / tomcat6, który nie ma linku do / var / log / tomcat6. Tutaj po prostu użyj ${catalina.base}.

Jeśli używasz innego kontenera, spróbuj znaleźć podobną właściwość systemową lub zdefiniuj własną. Ustawienie właściwości systemu będzie się różnić w zależności od platformy i kontenera. Ale dla Tomcata na Linux / Unix utworzyłbym plik setenv.sh w katalogu CATALINA_HOME / bin. Zawierałby:

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

Wtedy twój log4j.properties byłby taki:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${custom.logging.root}/LogFilename.log
Steve K.
źródło
2
Szczerze mówiąc, nie rozumiem, jaką przewagę ma to podejście przy użyciu Listenera, który wyjaśniłem w mojej odpowiedzi. zmień wartość, jeśli zmienię środowisko.
Iker Jimenez
4
Oba rozwiązania wykorzystują właściwości systemu, po prostu ustawiamy je inaczej. To naprawdę zależy od elastyczności w porównaniu z prostotą. Zarządzamy wszystkimi serwerami Tomcat, na których działa nasza aplikacja, więc lubię elastyczność. Jeśli rozpowszechniasz wojnę na użytek strony trzeciej, prostota ma sens
Steve K,
54

W końcu zrobiłem to w ten sposób.

Dodano ServletContextListener, który wykonuje następujące czynności:

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

Następnie w pliku log4j.properties:

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

Robiąc to w ten sposób, Log4j zapisze się we właściwym folderze, o ile nie użyjesz go przed ustawieniem właściwości systemowej "rootPath". Oznacza to, że nie możesz go używać z samego ServletContextListener, ale powinieneś móc go używać z dowolnego innego miejsca w aplikacji.

Powinien działać na każdym kontenerze internetowym i systemie operacyjnym, ponieważ nie jest zależny od właściwości systemu specyficznej dla kontenera i nie ma na niego wpływu problemy ze ścieżką specyficzne dla systemu operacyjnego. Testowane z kontenerami internetowymi Tomcat i Orion oraz w systemach Windows i Linux. Jak dotąd działa dobrze.

Co myślisz?

Iker Jimenez
źródło
4
To dobry pomysł, ale myślę, że Catalina.home może być bezpieczniejszy w użyciu, ponieważ zawsze będzie ustawiony / dostępny przed zainicjowaniem kodu log4j.
mat b
6
Byłoby to prawdą, gdybym używał tylko Tomcata, ale moim wymaganiem jest, aby działał na dowolnym kontenerze z konfiguracją 0. Moje podejście to spełnia i nikt do tej pory nie zaproponował lepszego podejścia.
Iker Jimenez
2
To rozwiązanie może działać w przypadku aplikacji internetowych korzystających z serwletów, ale rozwiązanie Steve'a K ( stackoverflow.com/questions/216781/… ) działa w przypadku każdej aplikacji korzystającej z Log4j.
Derek Mahar
2
Rozwiązanie Spencer K, również oparte na ścieżkach względnych, działa dla każdej aplikacji używającej Log4j, zakładając, że ustawisz katalog podstawowy na przewidywalną ścieżkę.
Derek Mahar
12
To rozwiązanie działa tylko wtedy, gdy masz jedną aplikację internetową skonfigurowaną do korzystania z niej, ponieważ właściwości systemu są globalne, aby uruchomić drugą aplikację, która nadpisze wartość ustawioną przez pierwszą aplikację internetową. Możesz nadać każdej aplikacji internetowej unikalną nazwę właściwości, ale jeśli masz zamiar to zrobić, możesz po prostu użyć $ {catalina.home} i dodać unikalną część ścieżki w pliku log4j.properties, ponieważ jest mniej podatna na błędy .
3urdoch
14

Jeśli używasz Springa, możesz:

1) utwórz plik konfiguracyjny log4j, np. „/WEB-INF/classes/log4j-myapp.properties” NIE nazywaj go „log4j.properties”

Przykład:

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

W dalszej części (3) zdefiniujemy „myWebapp-instance-root”

2) Określ lokalizację konfiguracji w web.xml:

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

3) Określ unikalną nazwę zmiennej dla katalogu głównego swojej aplikacji internetowej, np. „MyWebapp-instance-root”

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

4) Dodaj Log4jConfigListener:

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

Jeśli wybierzesz inną nazwę, pamiętaj, aby zmienić ją również w log4j-myapp.properties.

Zobacz mój artykuł (tylko włoski ... ale powinien być zrozumiały): http://www.megadix.it/content/configurare-path-relativi-log4j-utilizzando-spring

UPDATE (2009/08/01) Przetłumaczyłem mój artykuł na język angielski: http://www.megadix.it/node/136

Dimitri De Franciscis
źródło
6

Tylko komentarz do rozwiązania Ikera .

ServletContextto dobre rozwiązanie Twojego problemu. Ale nie sądzę, żeby to było dobre na utrzymanie. W większości przypadków pliki dziennika muszą być przechowywane przez długi czas.

Ponieważ ServletContexttworzy plik pod wdrożonym plikiem, zostanie on usunięty po ponownym wdrożeniu serwera. Sugeruję, aby przejść do folderu nadrzędnego rootPath zamiast folderu podrzędnego.

lizi
źródło
5

Czy log4j nie używa tylko katalogu głównego aplikacji, jeśli nie określisz katalogu głównego we właściwości path FileAppender? Więc powinieneś być w stanie użyć:

log4j.appender.file.File = logs / MyLog.log

Minęło trochę czasu, odkąd zajmowałem się tworzeniem stron internetowych w Javie, ale wydaje się, że jest to najbardziej intuicyjne, a także nie koliduje z innymi niestety nazwanymi dziennikami zapisującymi się do katalogu $ {catalina.home} / logs.

Spencer Kormos
źródło
4
Z tego, co widziałem, może korzystać z katalogu domowego użytkownika lub katalogu domowego kontenera, jeśli nie podasz bezwzględnej ścieżki.
Iker Jimenez
1
@Iker: Dlaczego nie możesz po prostu jawnie ustawić katalogu głównego aplikacji w swoim kontenerze lub konfiguracji aplikacji? Gdy już to zrobisz w fazie rozwoju i produkcji, możesz niezawodnie używać ścieżek względnych. Zakładając, że katalog główny jest ustawiony poprawnie, ścieżki względne są rozwiązaniem najbardziej przenośnym (możliwym do ponownego zlokalizowania).
Derek Mahar
1
Wiem, że to stary post, ale chciałem zrobić o tym notatkę dla innych. W przypadku tomcat, jeśli katalog główny aplikacji nie jest jawnie ustawiony, uważam, że domyślnie jest to katalog, z którego uruchomiono tomcat.
Geren White,
2

Jako kolejny komentarz na https://stackoverflow.com/a/218037/2279200 - może się to zepsuć, jeśli aplikacja internetowa niejawnie uruchomi inne ServletContextListener, które mogą zostać wywołane wcześniej i które już próbują używać log4j - w tym przypadku Konfiguracja log4j zostanie odczytana i przeanalizowana już przed ustawieniem właściwości określającej katalog główny logów => pliki dziennika pojawią się gdzieś pod bieżącym katalogiem (bieżącym katalogiem podczas uruchamiania tomcat).

Mogłem tylko wymyślić następujące rozwiązanie tego problemu: - zmień nazwę pliku log4j.properties (lub logj4.xml) na coś, czego log4j nie odczyta automatycznie. - W filtrze kontekstu, po ustawieniu właściwości, wywołaj klasę pomocniczą DOM / PropertyConfigurator, aby upewnić się, że Twój log4j -. {Xml, properties} zostanie odczytany - Zresetuj konfigurację log4j (IIRC jest do tego metoda)

To trochę brutalna siła, ale wydaje mi się, że to jedyny sposób, aby uczynić go wodoszczelnym.

Wolfgang Liebich
źródło
1

Jeśli korzystasz z Mavena, mam dla Ciebie świetne rozwiązanie:

  1. Edytuj plik pom.xml, aby zawierał następujące wiersze:

    <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>
    

    Tutaj definiujesz logDirectorywłaściwość specjalnie dla rodziny systemów operacyjnych.

  2. Użyj już zdefiniowanej logDirectorywłaściwości w log4j.propertiespliku:

    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. Otóż ​​to!

PS: Jestem pewien, że można to osiągnąć za pomocą Ant, ale niestety nie mam z tym wystarczającego doświadczenia.

alex.antaniuk
źródło
1

Moja sugestia jest taka, że ​​plik dziennika powinien być zawsze rejestrowany powyżej kontekstu głównego aplikacji internetowej, więc w przypadku ponownego wdrożenia aplikacji internetowej nie chcemy nadpisywać istniejących plików dziennika.

user553633
źródło
1

Moje rozwiązanie jest podobne do Iker Jiménez rozwiązania , ale zamiast korzystania z System.setProperty(...)pomocy I org.apache.log4j.PropertyConfigurator.configure(Properties). Do tego trzeba również log4j być w stanie znaleźć swoją konfigurację na własną rękę i załadować go ręcznie (oba punkty opisane w Wolfganga Liebich za odpowiedź ).

Działa to w przypadku Jetty i Tomcat, samodzielne lub uruchamiane z IDE, wymaga zerowej konfiguracji, pozwala na umieszczenie dzienników każdej aplikacji w ich własnym folderze, bez względu na to, ile aplikacji znajduje się w kontenerze (co jest problemem z Systemrozwiązaniem opartym na bazie ). W ten sposób można również umieścić plik konfiguracyjny log4j w dowolnym miejscu aplikacji internetowej (np. W jednym projekcie mieliśmy w środku wszystkie pliki konfiguracyjne WEB-INF/).

Detale:

  1. Mam swoje właściwości w log4j-no-autoload.propertiespliku w ścieżce klas (np. W moim projekcie Mavena, w którym jest oryginalnie src/main/resources, jest pakowany WEB-INF/classes),
  2. Posiada program appender skonfigurowany jako np .:

    log4j.appender.MyAppFileAppender = org.apache.log4j.FileAppender
    log4j.appender.MyAppFileAppender.file = ${webAppRoot}/WEB-INF/logs/my-app.log
    ...
    
  3. Mam program nasłuchujący kontekstów, taki jak ten (znacznie krótszy dzięki składni „try-with-resource” w Javie 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(...)
        }
        ...
    }
    
starikoff
źródło
1

Możesz określić względną ścieżkę do pliku dziennika, używając katalogu roboczego :

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

Jest to niezależne od kontenera serwletów i nie wymaga przekazywania niestandardowych zmiennych do środowiska systemowego.

Dimitar II
źródło