Chciałbym skonfigurować logback, aby wykonać następujące czynności.
Mam wszystko działa, z wyjątkiem ostatniego elementu, roll startu. Czy ktoś wie, jak to osiągnąć? Oto konfiguracja ...
<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>
Żadna z pozostałych sugestii nie była odpowiednia dla mojej sytuacji. Nie chciałem korzystać z rozwiązania opartego na rozmiarze i czasie, ponieważ wymaga ono skonfigurowania MaxFileSize i stosujemy zasady ściśle oparte na czasie. Oto, jak udało mi się przetoczyć plik przy starcie za pomocą TimeBasedRollingPolicy:
@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
}
}
}
Sztuczka polega na ustawieniu czasu nextCheck na 0L, aby isTriggeringEvent () pomyślał, że nadszedł czas, aby przerzucić plik dziennika. W ten sposób wykona kod niezbędny do obliczenia nazwy pliku, a także wygodnie zresetuje wartość czasu nextCheck. Kolejne wywołanie funkcji rollover () powoduje wyrzucenie pliku dziennika. Ponieważ dzieje się to tylko przy starcie, jest to rozwiązanie bardziej optymalne niż te, które dokonują porównania wewnątrz isTriggerEvent (). Niezależnie od tego, jak małe jest to porównanie, nadal nieznacznie obniża wydajność, gdy jest wykonywany przy każdym komunikacie dziennika. Zmusza to również do natychmiastowego wystąpienia rollover, zamiast czekać na pierwsze zdarzenie dziennika.
Adnotacja @NoAutoStart jest ważna, aby uniemożliwić Joranowi wykonanie metody start () przed zakończeniem całej innej inicjalizacji. W przeciwnym razie otrzymasz wyjątek NullPointerException.
Oto konfiguracja:
<!-- 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>
Mam nadzieję że to pomoże!
Działa dla mnie, używając następującej klasy jako 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);
}
}
W przypadku rozwiązania wykorzystującego już istniejące komponenty logback sugeruje pliki o unikalnych nazwach: http://logback.qos.ch/manual/appenders.html#uniquelyNamed
Podczas fazy tworzenia aplikacji lub w przypadku krótkotrwałych aplikacji , Np. aplikacje wsadowe, pożądane jest utworzenie nowego pliku dziennika przy każdym uruchomieniu nowej aplikacji. Jest to dość łatwe do zrobienia Za pomocą elementu
<timestamp>
.
<?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>
AKTUALIZACJA dla 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>
Znalazłem inne rozwiązanie do toczenia pliku dziennika raz, gdy aplikacja się uruchomi.
Używam RollingFileAppender
name logback's z FixedWindowRollingPolicy
i moja własna implementacja TriggeringPolicy<E>
.
FixedWindowRollingPolicy
pobiera nazwę pliku fileNazwa dla nowego pliku dziennika, gdzie %1
to nowy numer pliku. MaxIndex oznacza maksymalną liczbę mojej „historii”. Więcej informacji: FixedWindowRollingPolicy
Moje implementacje TriggeringPolicy
zwraca true dla first time, gdy wywoływany jest isTriggeringEvent (...). Więc WindowRollingPolicy przewija pliki dziennika, gdy polityka zostanie wywołana po raz pierwszy, a potem nie będzie się ona ponownie przewijać.
Konfiguracja xml dla RollingFileAppender
name__:
<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>
TriggeringPolicy
name__:
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;
}
}
Zastąpienie metody isTriggeringEvent () w ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP powinno działać dobrze. Po prostu zwróć 'true' po pierwszym wywołaniu metody isTriggeringEvent ().
Rozwiązanie Ceki wydaje się nie działać dla mnie, ale wydaje się być przynajmniej częściowo.
Uderza, ponieważ nie widzi widocznej zasady podczas uruchamiania TimeBasedFileNamingAndTriggeringPolicyBase
. Z jakimś hackerem mam go do zrobienia, a przy kilku innych dostałem go do obserwacji wyzwalacza, ale potem znowu się załamał, ponieważ nie mógł rozwiązać jednej z właściwości nazwy pliku ... Pakiet jest logbackiem, więc ja może dostać się do niektórych elementów wewnętrznych, aby odtworzyć część logiki w SizeAndTimeBasedFNATP#isTriggeringEvent
i wywołać computeCurrentPeriodsHighestCounterValue
. Myślę, że coś takiego może zadziałać, ale jeszcze nie znalazłem magicznej kombinacji. Naprawdę mam nadzieję, że robię coś głupiego, ponieważ w przeciwnym razie myślę, że będzie to oznaczało albo otwarcie niektórych szczegółów dla podklasy, albo wprowadzenie tego prostego do logbacku jako kolejnej polityki toczenia/wyzwalania.
logback.xml: wypróbował różne porządki triggeringPolicy
, TimeBasedFileNamingAndTriggeringPolicy
wewnątrz i na zewnątrz rollingPolicy
.
<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>
Polityka wyzwalacza:
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;
}
}
Wyjątek:
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]
To rozwiązanie naprawdę działa, dziękuję bardzo. Istnieje jednak jedna irytująca usterka: po uruchomieniu programu po raz pierwszy dziennik jest przewijany zaraz po utworzeniu, gdy jest pusty lub prawie pusty. Proponuję więc poprawkę: sprawdź, czy plik dziennika istnieje i nie jest pusty w momencie wywołania metody. Również jedna poprawka kosmetyczna: zmień nazwę zmiennej „rozpoczęty”, ponieważ ukrywa się odziedziczony członek o tej samej nazwie.
@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);
}
}
Ponadto uważam, że działa poprawnie z wersją 1.1.4-SNAPSHOT z logbackiem (sam dostałem źródło i skompilowałem je), ale nie działa w pełni z wersją 1.1.3. W wersji 1.1.3 nazwy plików są poprawne w określonej strefie czasowej, ale najazd nadal odbywa się w domyślnej strefie czasowej o północy.
Do pracy doszedłem (łącząc pomysły z poprzednich odpowiedzi). Uwaga: Miałem do czynienia z plikami opartymi na rozmiarze, a nie na czasie, ale zgaduję, że to samo rozwiązanie działa.
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;
}
}
Utwórz własną podklasę ch.qos.logback.core.rolling.TimeBasedRollingPolicy
i zastąp jej start
public class MyPolicy
extends ch.qos.logback.core.rolling.TimeBasedRollingPolicy
{
public void start ( )
{
super.start( );
rollover( );
}
}
W końcu to zrozumiałem. Mogę toczyć według rozmiaru, czasu i startu. Oto rozwiązanie:
1. stwórz własną klasę
@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. konfiguracja logback
<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>
API się zmieniło (na przykład setMaxFileSize już nie istnieje) i wiele z powyższych rzeczy nie działa, ale mam coś, co działa dla mnie w porównaniu z logback 1.1.8 (najnowszy w tym czasie).
Chciałem toczyć się po starcie i zmieniać rozmiar, ale nie czas. To robi:
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);
}
}
W tym celu potrzebujesz także polityki kroczącej. FixedWindowRollingPolicy prawdopodobnie to zrobi, ale nie podoba mi się, ponieważ chcę zachować dużą liczbę plików i jest to bardzo nieefektywne. Coś, co wzrasta stopniowo (zamiast przesuwać jak FixedWindow), zadziałałoby, ale to nie istnieje. Dopóki piszę własną, postanowiłem wykorzystać czas zamiast liczyć. Chciałem rozszerzyć bieżący kod logowania, ale dla rzeczy opartych na czasie reguły toczenia i wyzwalania są często łączone w jedną klasę i istnieją logi zagnieżdżania i okrągłych rzeczy i pól bez getterów, więc stwierdziłem, że jest to raczej niemożliwe. Więc musiałem zrobić dużo od zera. Utrzymuję to w prosty sposób i nie zaimplementowałem funkcji takich jak kompresja - chciałbym je mieć, ale staram się po prostu zachować prostotę.
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;
}
}
A potem wygląda jak config
<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>
jeśli jesteś sfrustrowany, nie rozwiązuje się tego natywnie, zagłosuj na niego w
http://jira.qos.ch/browse/LOGBACK-204
http://jira.qos.ch/browse/LOGBACK-215
(minęły lata, a dla mnie jest to absolutnie krytyczna funkcjonalność, chociaż wiem, że wiele innych ram zawodzi również)