it-swarm.com.de

So rollen Sie die Protokolldatei beim Start in Logback ein

Ich möchte Logback so konfigurieren, dass Folgendes ausgeführt wird.

  • Melden Sie sich in einer Datei an
  • Rollen Sie die Datei, wenn sie 50 MB erreicht
  • Bewahren Sie nur 7 Tage Protokolle auf
  • Beim Start immer eine neue Datei erzeugen (Rolle machen)

Ich habe alles bis auf den letzten Punkt, die Startrolle. Weiß jemand, wie man das schafft? Hier ist die Konfig ...

  <appender name="File" class="ch.qos.logback.core.rolling.RollingFileAppender">

    <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg \(%file:%line\)%n</Pattern>
    </layout>

    <File>server.log</File>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <FileNamePattern>server.%d{yyyy-MM-dd}.log</FileNamePattern>
      <!-- keep 7 days' worth of history -->
      <MaxHistory>7</MaxHistory>

      <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <MaxFileSize>50MB</MaxFileSize>
      </TimeBasedFileNamingAndTriggeringPolicy>

    </rollingPolicy>
  </appender>
42
Mike Q

Keiner der anderen Vorschläge war für meine Situation angemessen. Ich wollte keine auf Größe und Zeit basierende Lösung verwenden, da hierfür eine MaxFileSize-Konfiguration erforderlich ist und wir eine streng zeitbasierte Richtlinie verwenden. So habe ich die Datei beim Start mit einer TimeBasedRollingPolicy gerollt:

@NoAutoStart
public class StartupTimeBasedTriggeringPolicy<E> 
        extends DefaultTimeBasedFileNamingAndTriggeringPolicy<E> {

    @Override
    public void start() {
        super.start();
        nextCheck = 0L;
        isTriggeringEvent(null, null);
        try {
            tbrp.rollover();
        } catch (RolloverFailure e) {
            //Do nothing
        }
    }

}

Der Trick besteht darin, die nextCheck-Zeit auf 0L zu setzen, sodass isTriggeringEvent () der Meinung ist, dass es Zeit ist, die Protokolldatei zu rollen. Dadurch wird der Code ausgeführt, der zum Berechnen des Dateinamens erforderlich ist, und der nextCheck-Zeitwert kann bequem zurückgesetzt werden. Beim nachfolgenden Aufruf von rollover () wird die Protokolldatei gerollt. Da dies nur beim Start der Fall ist, ist dies eine bessere Lösung als die, die einen Vergleich in isTriggerEvent () durchführt. So klein dieser Vergleich auch sein mag, die Leistung wird bei jeder Protokollnachricht leicht beeinträchtigt. Dadurch wird der Rollover auch beim Start sofort ausgeführt, anstatt auf das erste Protokollereignis zu warten.

Die Annotation @NoAutoStart ist wichtig, um zu verhindern, dass Joran die start () - Methode ausführt, bevor alle anderen Initialisierungen abgeschlossen sind. Andernfalls erhalten Sie eine NullPointerException.

Hier ist die Konfig:

  <!-- Daily rollover appender that also appends timestamp and rolls over on startup -->
  <appender name="startupDailyRolloverAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_FILE}</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${LOG_FILE}.%d{yyyyMMdd}_%d{HHmmss,aux}</fileNamePattern>
      <TimeBasedFileNamingAndTriggeringPolicy class="my.package.StartupTimeBasedTriggeringPolicy" />
    </rollingPolicy>
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender> 

Hoffe das hilft!

25
Dave

Es funktioniert für mich mit der folgenden Klasse als timeBasedFileNamingAndTriggeringPolicy:

import Java.io.File;
import Java.util.concurrent.atomic.AtomicBoolean;

import ch.qos.logback.core.joran.spi.NoAutoStart;
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;

@NoAutoStart
public class Trigger<E> extends SizeAndTimeBasedFNATP<E>
{
    private final AtomicBoolean trigger = new AtomicBoolean();

    public boolean isTriggeringEvent(final File activeFile, final E event) {
        if (trigger.compareAndSet(false, true) && activeFile.length() > 0) {
            String maxFileSize = getMaxFileSize();
            setMaxFileSize("1");
            super.isTriggeringEvent(activeFile, event);
            setMaxFileSize(maxFileSize);
            return true;
        }
        return super.isTriggeringEvent(activeFile, event);
    }
}
7
proactif

Für eine Lösung mit bereits vorhandenen Komponenten schlägt der Logback die eindeutig benannten Dateien vor: http://logback.qos.ch/manual/appenders.html#uniquelyNamed

Während der Anwendungsentwicklungsphase oder im Fall von kurzlebigen Anwendungen, z. Batch-Anwendungen ist es wünschenswert, bei jedem neuen Anwendungsstart eine neue Protokolldatei zu erstellen. Dies ist ziemlich einfach mit Hilfe des <timestamp> Element.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <timestamp key="startTimestamp" datePattern="yyyyMMddHHmmssSSS"/>
    <appender name="File"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg \(%file:%line\)%n</Pattern>
        </layout>

        <file>server-${startTimestamp}.log</file>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>server-${startTimestamp}-%d{yyyy-MM-dd}-%i.log</FileNamePattern>
            <!-- keep 7 days' worth of history -->
            <MaxHistory>7</MaxHistory>

            <TimeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>1KB</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="File" />
    </root>
</configuration>

AKTUALISIERT für Logback-1.2.1

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <timestamp key="startTimestamp" datePattern="yyyyMMddHHmmssSSS"/>
    <appender name="File"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg \(%file:%line\)%n</Pattern>
        </layout>

        <file>server-${startTimestamp}.log</file>

        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>server-${startTimestamp}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <!-- keep 7 days' worth of history -->
            <maxHistory>7</maxHistory>
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="File" />
    </root>
</configuration>
5
raisercostin

Ich habe eine andere Lösung gefunden, um die Protokolldatei einmal zu rollen, wenn die Anwendung gestartet wird.

Ich verwende das RollingFileAppender von logback mit dem FixedWindowRollingPolicy von logback und meine eigene Implementierung eines TriggeringPolicy<E>.

Das FixedWindowRollingPolicy ruft das fileNamePattern für die neue logFile ab, wobei %1 ist die neue Nummer der Datei. Der maxIndex steht für die maximale Anzahl meiner "Historie". Weitere Informationen: FixedWindowRollingPolicy

Meine Implementierungen TriggeringPolicy geben zum ersten Mal true zurück, wenn isTriggeringEvent (...) aufgerufen wird. Die WindowRollingPolicy rollt also über die Protokolldateien, wenn die Richtlinie zum ersten Mal aufgerufen wird, und anschließend wird sie nicht erneut gerollt.

Die xml-Konfiguration für das RollingFileAppender:

<configuration>
    ...
    <appender name="FILE_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logFile.log</file>

        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <fileNamePattern>logFile.%i.log</fileNamePattern>
            <minIndex>1</minIndex>
            <maxIndex>4</maxIndex>
        </rollingPolicy>

        <triggeringPolicy class="my.classpath.RollOncePerSessionTriggeringPolicy"/>
    </appender>
...
</configuration>

Das TriggeringPolicy:

package my.classpath;

import ch.qos.logback.core.rolling.TriggeringPolicyBase;

import Java.io.File;

public class RollOncePerSessionTriggeringPolicy<E> extends TriggeringPolicyBase<E> {
    private static boolean doRolling = true;

    @Override
    public boolean isTriggeringEvent(File activeFile, E event) {
        // roll the first time when the event gets called
        if (doRolling) {
            doRolling = false;
            return true;
        }
        return false;
    }
}
5
duffy356

Das Überschreiben der isTriggeringEvent () - Methode in ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP sollte einwandfrei funktionieren. Geben Sie einfach 'true' zurück, wenn die isTriggeringEvent () - Methode zum ersten Mal aufgerufen wird.

3
Ceki

Die Lösung von Ceki scheint für mich nicht zu funktionieren, scheint aber zumindest ein Teil davon zu sein.

Es wird explodiert, weil beim Starten von TimeBasedFileNamingAndTriggeringPolicyBase keine Rolling Policy angezeigt wird. Mit etwas Hacker bekam ich etwas Protokollieren, und mit ein paar mehr habe ich den Auslöser beobachtet, aber dann brach es erneut, weil eine der Eigenschaften des Dateinamens nicht aufgelöst werden konnte. Das Paket ist also ein Logback könnte zu einigen der Interna gelangen, einige der Logik in SizeAndTimeBasedFNATP#isTriggeringEvent replizieren und computeCurrentPeriodsHighestCounterValue aufrufen. Ich denke, etwas in dieser Richtung könnte funktionieren, habe die magische Kombination noch nicht gefunden. Ich hoffe wirklich, dass ich etwas Dummes mache, denn sonst denke ich, dass es entweder bedeutet, einige Details für die Unterklasse zu öffnen, oder dies direkt in Logback als eine andere rollierende/auslösende Richtlinie zu setzen.

logback.xml: Verschiedene Anordnungen von triggeringPolicy, TimeBasedFileNamingAndTriggeringPolicy innerhalb und außerhalb der rollingPolicy ausprobiert.

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_DIR}/${LOG_FILE_BASE}.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_DIR}/${LOG_FILE_BASE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <MaxHistory>7</MaxHistory>

        <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.RollOnStartupPolicy" />
    </rollingPolicy>

    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>

    <encoder>
        <pattern>%msg%n</pattern>
    </encoder>
</appender>

Die Auslöserrichtlinie:

package ch.qos.logback.core.rolling;
public class RollOnStartupPolicy<E> extends SizeAndTimeBasedFNATP<E> {
private final AtomicBoolean firstTime = new AtomicBoolean(true);

    @Override
    public boolean isTriggeringEvent(File activeFile, E event) {
        if (!firstTime.get()) { // fast path
            return false;
        }

        if (firstTime.getAndSet(false)) {
            return true;
        }
        return false;
    }
}

Die Ausnahme:

Java.lang.NullPointerException
at  at ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicyBase.start(TimeBasedFileNamingAndTriggeringPolicyBase.Java:46)
at  at ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP.start(SizeAndTimeBasedFNATP.Java:36)
at  at ch.qos.logback.core.joran... [snip joran config]
2
Joe Kearney

Diese Lösung funktioniert sehr gut, vielen Dank. Allerdings gibt es eine ärgerliche Störung: Wenn Sie das Programm zum ersten Mal ausführen, wird das Protokoll direkt nach der Erstellung gerollt, wenn es leer ist oder fast leer ist Ich empfehle einen Fix: Überprüfen Sie, ob die Protokolldatei vorhanden ist und zum Zeitpunkt, zu dem die Methode aufgerufen wird, nicht leer ist. Außerdem gibt es noch einen weiteren kosmetischen Fix: Benennen Sie die Variable "started" um, weil sie das geerbte Element mit derselben verdeckt Name.

@NoAutoStart
public class StartupSizeTimeBasedTriggeringPolicy<E> extends     SizeAndTimeBasedFNATP<E> {

    private boolean policyStarted;

    @Override
    public boolean isTriggeringEvent(File activeFile, E event) {
        if (!policyStarted) {
            policyStarted = true;
            if (activeFile.exists() && activeFile.length() > 0) {
                nextCheck = 0L;
                return true;
            }
        }
        return super.isTriggeringEvent(activeFile, event);
    }
}

Ich glaube auch, dass es ordnungsgemäß mit der Logback-Version 1.1.4-SNAPSHOT funktioniert (ich habe die Quelle selbst erstellt und selbst kompiliert), aber es funktioniert nicht vollständig mit der Version 1.1.3. Mit 1.1.3 werden die Dateien ordnungsgemäß mit der angegebenen Zeitzone benannt. Rollover wird jedoch immer noch in der Standardzeitzone Mitternacht ausgeführt.

1
Leonid Ilyevsky

Ich habe folgendes zur Arbeit bekommen (Ideen aus vorherigen Antworten kombinieren). Hinweis: Ich habe mit größenbasierten Dateien gearbeitet, nicht zeitbasiert, aber ich denke, dass die gleiche Lösung funktioniert.

public class StartupSizeBasedTriggeringPolicy<E> extends ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy<E> {

private final AtomicReference<Boolean> isFirstTime = new AtomicReference<Boolean>(true);

@Override
public boolean isTriggeringEvent(final File activeFile, final E event) {

    //this method appears to have side-effects so always call
    boolean result = super.isTriggeringEvent(activeFile, event);

    return isFirstTime.compareAndSet(true, false) || result;
}

}

1
djechlin

Erstellen Sie Ihre eigene Unterklasse von ch.qos.logback.core.rolling.TimeBasedRollingPolicy und überschreiben Sie deren start.

public class MyPolicy
    extends ch.qos.logback.core.rolling.TimeBasedRollingPolicy
{

    public void start ( )
    {
        super.start( );
        rollover( );
    }
}

Ich finde es endlich heraus. Ich kann nach Größe, Zeit und Start rollen. Hier ist die Lösung:

1. erstelle deine eigene Klasse

@NoAutoStart
public class StartupSizeTimeBasedTriggeringPolicy<E> extends SizeAndTimeBasedFNATP<E> {

    private boolean started = false;

    @Override
    public boolean isTriggeringEvent( File activeFile, E event ) {
        if ( !started ) {
            nextCheck = 0L;
            return started = true;
        }

        return super.isTriggeringEvent( activeFile, event );
    };
}

2. Logback konfigurieren

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOGS_DIR}/${FILE_NAME}.log</file>
    <encoder>
        <pattern>%d [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOGS_DIR}/${FILE_NAME}.%d{yyyy-MM-dd}_%d{HHmmss,aux}.%i.log.Zip</fileNamePattern>
        <maxHistory>30</maxHistory>
        <TimeBasedFileNamingAndTriggeringPolicy class="my.StartupSizeTimeBasedTriggeringPolicy">
            <MaxFileSize>250MB</MaxFileSize> 
        </TimeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
</appender>
1
Perlos

Die API hat sich geändert (zum Beispiel existiert setMaxFileSize nicht mehr) und viele der oben genannten Dinge scheinen nicht zu funktionieren, aber ich habe etwas, das für mich gegen Logback 1.1.8 arbeitet (spätestens zu diesem Zeitpunkt).

Ich wollte beim Start rollen und auf Größe rollen, aber nicht die Zeit. Das macht es:

public class RollOnStartupAndSizeTriggeringPolicy<E> extends SizeBasedTriggeringPolicy<E> {
    private final AtomicBoolean firstTime = new AtomicBoolean();

    public boolean isTriggeringEvent(final File activeFile, final E event) {
        if (firstTime.compareAndSet(false, true) && activeFile != null && activeFile.length() > 0) {
            return true;
        }
        return super.isTriggeringEvent(activeFile, event);
    }
}

Dazu benötigen Sie auch eine rollierende Politik. FixedWindowRollingPolicy würde das wahrscheinlich tun, aber ich mag es nicht, weil ich eine große Anzahl von Dateien behalten möchte, und das ist sehr ineffizient. Etwas, das Zahlen inkrementell hoch sind (anstatt wie FixedWindow zu gleiten), würde funktionieren, aber das existiert nicht. So lange ich meine eigene schreibe, entschied ich mich dafür, die Zeit anstelle des Zählens zu verwenden. Ich wollte den aktuellen Logback-Code erweitern, aber für das zeitbasierte Material werden die Rolling- und Auslöserichtlinien häufig in einer Klasse zusammengefasst, und es gibt Protokolle von Verschachtelungen und kreisförmigen Elementen und Feldern ohne Getter. Ich musste also von Grund auf viel tun. Ich halte es einfach und implementiere keine Funktionen wie Komprimierung - ich hätte sie gerne, aber ich versuche es einfach zu halten.

public class TimestampRollingPolicy<E> extends RollingPolicyBase {
    private final RenameUtil renameUtil = new RenameUtil();
    private String activeFileName;
    private String fileNamePatternStr;
    private FileNamePattern fileNamePattern;

    @Override
    public void start() {
        super.start();
        renameUtil.setContext(this.context);
        activeFileName = getParentsRawFileProperty();
        if (activeFileName == null || activeFileName.isEmpty()) {
            addError("No file set on appender");
        }
        if (fileNamePatternStr == null || fileNamePatternStr.isEmpty()) {
            addError("fileNamePattern not set");
            fileNamePattern = null;
        } else {
            fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context);
        }
        addInfo("Will use the pattern " + fileNamePattern + " to archive files");
    }

    @Override
    public void rollover() throws RolloverFailure {
        File f = new File(activeFileName);
        if (!f.exists()) {
            return;
        }
        if (f.length() <= 0) {
            return;
        }
        try {
            String archiveFileName = fileNamePattern.convert(new Date(f.lastModified()));
            renameUtil.rename(activeFileName, archiveFileName);
        } catch (RolloverFailure e) {
            throw e;
        } catch (Exception e) {
            throw new RolloverFailure(e.toString(), e);
        }
    }

    @Override
    public String getActiveFileName() {
        return activeFileName;
    }

    public void setFileNamePattern(String fnp) {
        fileNamePatternStr = fnp;
    }
}

Und dann sieht die config so aus

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <encoder>
    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  </encoder>
  <file>/tmp/monitor.log</file>
  <rollingPolicy class="my.log.TimestampRollingPolicy">
    <fileNamePattern>/tmp/monitor.%d{yyyyMMdd-HHmmss}.log</fileNamePattern>
  </rollingPolicy>
  <triggeringPolicy class="my.log.RollOnStartupAndSizeTriggeringPolicy">
    <maxFileSize>1gb</maxFileSize>
  </triggeringPolicy>
</appender>

wenn Sie frustriert sind, wird dies nicht nativ gelöst. Stimmen Sie dafür ab

http://jira.qos.ch/browse/LOGBACK-204

http://jira.qos.ch/browse/LOGBACK-215

(Es ist Jahre her und für mich ist dies eine absolut kritische Funktionalität, obwohl ich weiß, dass viele andere Frameworks auch daran scheitern).

0
dlipofsky