Log4j, Konfigurieren einer Webanwendung zur Verwendung eines relativen Pfads

80

Ich habe eine Java-Webanwendung, die entweder auf Win- oder Linux-Computern bereitgestellt werden muss. Ich möchte jetzt log4j für die Protokollierung hinzufügen und möchte einen relativen Pfad für die Protokolldatei verwenden, da ich den Dateipfad nicht bei jeder Bereitstellung ändern möchte. Der Container wird höchstwahrscheinlich Tomcat sein, aber nicht unbedingt.

Was ist der beste Weg, dies zu tun?

Iker Jimenez
quelle
3
Das ist wirklich nur die halbe Frage. Ein dynamischer Pfad für die Protokolldatei ist großartig, aber was ist mit der Konfigurationsdatei selbst? Wenn nur der Speicherort der Protokolldatei dynamisch ist, haben Sie an allen Stellen, an denen dies bereitgestellt wird, dieselben Protokollstufen, und ich halte das nicht für wünschenswert. Ich möchte den besten Ansatz für die dynamische Angabe der Konfiguration kennen, damit sich meine Entwicklungsumgebung bei DEBUG anmelden und bei INFO / WARN prod kann. Was denken Sie?
Lucas

Antworten:

100

Tomcat legt eine Systemeigenschaft Catalina.home fest. Sie können dies in Ihrer log4j-Eigenschaftendatei verwenden. Etwas wie das:

log4j.rootCategory=DEBUG,errorfile

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

Unter Debian (einschließlich Ubuntu) ${catalina.home}funktioniert dies nicht, da dies auf / usr / share / tomcat6 verweist, das keinen Link zu / var / log / tomcat6 hat. Hier einfach benutzen ${catalina.base}.

Wenn Sie einen anderen Container verwenden, versuchen Sie, eine ähnliche Systemeigenschaft zu finden, oder definieren Sie Ihre eigene. Das Festlegen der Systemeigenschaft variiert je nach Plattform und Container. Aber für Tomcat unter Linux / Unix würde ich eine setenv.sh im Verzeichnis CATALINA_HOME / bin erstellen. Es würde enthalten:

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

Dann wären Ihre log4j.properties:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${custom.logging.root}/LogFilename.log
Steve K.
quelle
2
Ich sehe ehrlich gesagt nicht, welchen Vorteil dieser Ansatz gegenüber der Verwendung des Listeners hat, den ich in meiner Antwort erklärt habe. Es ist mir egal, um welchen Container es sich handelt, es funktioniert einfach, egal wo ich ihn bereitstelle, während ich es in Ihrem Ansatz tun muss Ändern Sie einen Wert, wenn ich die Umgebung ändere.
Iker Jimenez
4
Beide Lösungen verwenden Systemeigenschaften, wir setzen sie nur unterschiedlich. Es kommt wirklich nur auf Flexibilität und Einfachheit an. Wir verwalten alle Tomcat-Server, auf denen unsere Anwendung ausgeführt wird, daher gefällt mir die Flexibilität. Wenn Sie einen Krieg für Drittanbieter verteilen, ist Einfachheit sinnvoll
Steve K
54

Ich habe es endlich so gemacht.

Es wurde ein ServletContextListener hinzugefügt, der Folgendes ausführt:

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

Dann in der Datei log4j.properties:

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

Auf diese Weise schreibt Log4j in den richtigen Ordner, solange Sie ihn nicht verwenden, bevor die Systemeigenschaft "rootPath" festgelegt wurde. Dies bedeutet, dass Sie es nicht über den ServletContextListener selbst verwenden können, sondern dass Sie es von einer anderen Stelle in der App aus verwenden können sollten.

Es sollte auf jedem Webcontainer und Betriebssystem funktionieren, da es nicht von einer container-spezifischen Systemeigenschaft abhängig ist und nicht von betriebssystemspezifischen Pfadproblemen betroffen ist. Getestet mit Tomcat- und Orion-Webcontainern sowie unter Windows und Linux und funktioniert bisher einwandfrei.

Was denken Sie?

Iker Jimenez
quelle
4
Dies ist eine gute Idee, aber ich denke, Catalina.home ist möglicherweise sicherer zu verwenden, da es immer festgelegt / verfügbar ist, bevor ein log4j-Code initialisiert wird.
Matt B
6
Das wäre wahr, wenn ich nur Tomcat verwenden würde, aber meine Anforderung ist, dass es auf jedem Container mit 0-Konfiguration funktionieren muss. Mein Ansatz erfüllt dies und bisher hat niemand einen besseren Ansatz dafür vorgeschlagen.
Iker Jimenez
2
Diese Lösung funktioniert möglicherweise für Webanwendungen, die Servlets verwenden, aber die Lösung von Steve K ( stackoverflow.com/questions/216781/… ) funktioniert für alle Anwendungen, die Log4j verwenden.
Derek Mahar
2
Die Lösung von Spencer K, die ebenfalls auf relativen Pfaden basiert, funktioniert für jede Anwendung, die Log4j verwendet, vorausgesetzt, Sie setzen das Basisverzeichnis auf einen vorhersehbaren Pfad.
Derek Mahar
12
Diese Lösung funktioniert nur, wenn Sie eine einzelne WebApp für die Verwendung konfiguriert haben, da die Systemeigenschaften für Tomcat global sind. Ein zweiter Start der App würde den Wert überschreiben, den die erste Webanwendung festgelegt hat. Sie können jeder Webanwendung einen eindeutigen Eigenschaftsnamen geben. Wenn Sie dies jedoch tun möchten, können Sie auch einfach $ {catalogina.home} verwenden und den eindeutigen Teil des Pfads in die Datei log4j.properties einfügen, da dieser weniger fehleranfällig ist .
3urdoch
14

Wenn Sie Spring verwenden, können Sie:

1) Erstellen Sie eine log4j-Konfigurationsdatei, z. B. "/WEB-INF/classes/log4j-myapp.properties". Nennen Sie sie NICHT "log4j.properties".

Beispiel:

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

Wir werden "myWebapp-instance-root" später in Punkt (3) definieren.

2) Geben Sie den Konfigurationsspeicherort in web.xml an:

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

3) Geben Sie einen eindeutigen Variablennamen für das Stammverzeichnis Ihrer Webanwendung an, z. B. "myWebapp-instance-root".

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

4) Fügen Sie einen Log4jConfigListener hinzu:

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

Wenn Sie einen anderen Namen wählen, denken Sie daran, ihn auch in log4j-myapp.properties zu ändern.

Siehe meinen Artikel (nur Italienisch ... aber es sollte verständlich sein): http://www.megadix.it/content/configurare-path-relativi-log4j-utilizzando-spring

UPDATE (01.08.2009) Ich habe meinen Artikel ins Englische übersetzt: http://www.megadix.it/node/136

Dimitri De Franciscis
quelle
6

Nur ein Kommentar zu Ikers Lösung.

ServletContextist eine gute Lösung für Ihr Problem. Aber ich denke nicht, dass es gut für Unterhalt ist. Die meisten Zeitprotokolldateien müssen für lange Zeit gespeichert werden.

Da sich ServletContextdie Datei unter der bereitgestellten Datei befindet, wird sie entfernt, wenn der Server erneut bereitgestellt wird. Ich empfehle, den übergeordneten Ordner von rootPath anstelle des untergeordneten Ordners zu verwenden.

Lizi
quelle
5

Verwendet log4j nicht nur das Anwendungsstammverzeichnis, wenn Sie in der Pfadeigenschaft Ihres FileAppender kein Stammverzeichnis angeben? Sie sollten also nur Folgendes verwenden können:

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

Es ist schon eine Weile her, dass ich Java-Webentwicklung durchgeführt habe, aber dies scheint die intuitivste zu sein und kollidiert auch nicht mit anderen leider benannten Protokollen, die in das Verzeichnis $ {catalogina.home} / logs schreiben.

Spencer Kormos
quelle
4
Soweit ich gesehen habe, kann es das Home-Verzeichnis des Benutzers oder das Home-Verzeichnis des Containers verwenden, wenn Sie keinen absoluten Pfad angeben. Unzuverlässig.
Iker Jimenez
1
@Iker: Warum können Sie das Anwendungsstammverzeichnis in Ihrer Container- oder Anwendungskonfiguration nicht einfach explizit festlegen? Sobald Sie dies einmal in Entwicklung und Produktion getan haben, können Sie relative Pfade zuverlässig verwenden. Unter der Annahme, dass das Stammverzeichnis korrekt eingestellt ist, sind relative Pfade die portabelste (umsetzbare) Lösung.
Derek Mahar
1
Ich weiß, dass dies ein alter Beitrag ist, aber ich wollte dies für andere notieren. Wenn für Tomcat das Anwendungsstammverzeichnis nicht explizit festgelegt ist, wird standardmäßig das Verzeichnis verwendet, aus dem Sie Tomcat gestartet haben.
Geren White
2

Als weiterer Kommentar zu https://stackoverflow.com/a/218037/2279200 - dies kann zu Problemen führen, wenn die Web-App implizit andere ServletContextListener startet, die möglicherweise früher aufgerufen werden und bereits versuchen, log4j zu verwenden - in diesem Fall die Die log4j-Konfiguration wird bereits gelesen und analysiert, bevor die Eigenschaft festgelegt wird, die das Protokollstammverzeichnis bestimmt => Die Protokolldateien werden irgendwo unterhalb des aktuellen Verzeichnisses angezeigt (das aktuelle Verzeichnis beim Starten von Tomcat).

Ich konnte mir nur eine folgende Lösung für dieses Problem vorstellen: - Benennen Sie Ihre Datei log4j.properties (oder logj4.xml) in etwas um, das log4j nicht automatisch liest. - Rufen Sie in Ihrem Kontextfilter nach dem Festlegen der Eigenschaft die Hilfsklasse DOM / PropertyConfigurator auf, um sicherzustellen, dass Ihr log4j -. {Xml, properties} gelesen wird. - Setzen Sie die log4j-Konfiguration zurück (IIRC gibt es eine Methode, um dies zu tun).

Dies ist ein bisschen rohe Gewalt, aber ich denke, es ist der einzige Weg, es wasserdicht zu machen.

Wolfgang Liebich
quelle
1

Für den Fall, dass Sie Maven verwenden, habe ich eine großartige Lösung für Sie:

  1. Bearbeiten Sie Ihre Datei pom.xml so, dass sie folgende Zeilen enthält:

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

    Hier definieren Sie die logDirectoryEigenschaft speziell für die Betriebssystemfamilie.

  2. Verwenden Sie die bereits definierte logDirectoryEigenschaft in der log4j.propertiesDatei:

    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. Das ist es!

PS: Ich bin sicher, dass dies mit Ant erreicht werden kann, aber leider habe ich nicht genug Erfahrung damit.

alex.antaniuk
quelle
1

Mein Vorschlag ist, dass die Protokolldatei immer über dem Stammkontext der Webanwendung protokolliert werden sollte. Wenn wir die Webanwendung erneut bereitstellen, möchten wir die vorhandenen Protokolldateien nicht überschreiben.

user553633
quelle
1

Meine Lösung ist ähnlich wie Iker Jimenez Lösung , aber anstelle von System.setProperty(...)I Verwendung org.apache.log4j.PropertyConfigurator.configure(Properties). Dazu brauche ich auch log4j, um seine Konfiguration nicht selbst finden zu können, und lade es manuell (beide Punkte in Wolfgang Liebichs Antwort beschrieben ).

Dies funktioniert für Jetty und Tomcat, eigenständig oder von IDE ausgeführt, erfordert keine Konfiguration und ermöglicht es, die Protokolle jeder App in einem eigenen Ordner abzulegen, unabhängig davon, wie viele Apps sich im Container befinden (was das Problem bei der Systembasierten Lösung ist). Auf diese Weise kann man die log4j-Konfigurationsdatei auch an einer beliebigen Stelle in der Web-App ablegen (z. B. in einem Projekt hatten wir alle Konfigurationsdateien WEB-INF/).

Einzelheiten:

  1. Ich habe meine Eigenschaften in der log4j-no-autoload.propertiesDatei im Klassenpfad (z. B. in meinem Maven-Projekt, in dem es ursprünglich enthalten ist und in src/main/resourcesdas es gepackt wird WEB-INF/classes).
  2. Der Datei-Appender ist wie folgt konfiguriert:

    log4j.appender.MyAppFileAppender = org.apache.log4j.FileAppender
    log4j.appender.MyAppFileAppender.file = ${webAppRoot}/WEB-INF/logs/my-app.log
    ...
    
  3. Und ich habe einen Kontext-Listener wie diesen (wird mit der "Try-with-Resource" -Syntax von Java 7 viel kürzer):

    @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
quelle
1

Sie können den relativen Pfad zur Protokolldatei über das Arbeitsverzeichnis angeben :

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

Dies ist unabhängig vom Servlet-Container und erfordert keine Übergabe einer benutzerdefinierten Variablen an die Systemumgebung.

Dimitar II
quelle