daemon: Automatically log into $site_path/logs

We now save daemon log files into $site_path/logs, rolling over
once per day using the DailyRollingFileAppender class from log4j.

Current messages are written to $site_path/logs/error_log until the
end of a day is reached in GMT, and then its renamed to an archive
file suffixed with the 'yyyy-mm-dd' format.

Because the daemon is meant to run in the background and service
user requests, the log messages are automatically redirect to the
log file by default.  An administrator can force us to send log
messages to stderr again by using the new option --console-log.

Bug: issue 328
Change-Id: I6add1d2ac8faf57c6ca17964a7e88a2afacfb094
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce 2009-11-12 17:56:19 -08:00
parent 22a17f4c64
commit a9d39fcc9f
7 changed files with 155 additions and 33 deletions

View File

@ -11,6 +11,7 @@ SYNOPSIS
'java' -DGerritServer=PATH -jar gerrit.war 'daemon'
[\--enable-httpd | \--disable-httpd]
[\--enable-sshd | \--disable-sshd]
[\--console-log]
[\--slave]
DESCRIPTION
@ -47,12 +48,23 @@ OPTIONS
+
This option automatically implies '\--disable-httpd \--enable-sshd'.
\--console-log::
Send log messages to the console, instead of to the standard
log file '$site_path/logs/error_log'.
CONTEXT
-------
This command can only be run on a server which has direct
connectivity to the metadata database, and local access to the
managed Git repositories.
LOGGING
-------
Error and warning messages from the server are automatically written
to the log file under '$site_path/logs/error_log'. This log file
is automatically rotated at 12:00 AM GMT each day, allowing an
external log cleaning service to clean up the prior logs.
KNOWN ISSUES
------------
Slave daemon caches can quickly become out of date when modifications

View File

@ -33,6 +33,11 @@ limitations under the License.
</description>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>

View File

@ -63,6 +63,9 @@ public class Daemon extends SiteProgram {
@Option(name = "--slave", usage = "Support fetch only; implies --disable-httpd")
private boolean slave;
@Option(name = "--console-log", usage = "Log to console (not $site_path/logs)")
private boolean consoleLog;
private final LifecycleManager manager = new LifecycleManager();
private Injector dbInjector;
private Injector cfgInjector;
@ -88,6 +91,11 @@ public class Daemon extends SiteProgram {
throw die("--enable-httpd currently requires --enable-sshd");
}
if (consoleLog) {
} else {
manager.add(ErrorLogFile.start(getSitePath()));
}
dbInjector = createDbInjector();
cfgInjector = createCfgInjector();
sysInjector = createSysInjector();

View File

@ -0,0 +1,105 @@
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.pgm;
import com.google.gerrit.lifecycle.LifecycleListener;
import org.apache.log4j.Appender;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.helpers.OnlyOnceErrorHandler;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.LoggingEvent;
import java.io.File;
public class ErrorLogFile {
public static LifecycleListener start(final File sitePath) {
final File logdir = new File(sitePath, "logs");
if (!logdir.exists() && !logdir.mkdirs()) {
throw new Die("Cannot create log directory: " + logdir);
}
final PatternLayout layout = new PatternLayout();
layout.setConversionPattern("[%d] %-5p %c %x: %m%n");
final DailyRollingFileAppender dst = new DailyRollingFileAppender();
dst.setName("error_log");
dst.setLayout(layout);
dst.setEncoding("UTF-8");
dst.setFile(new File(logdir, "error_log").getAbsolutePath());
dst.setImmediateFlush(true);
dst.setAppend(true);
dst.setThreshold(Level.INFO);
dst.setErrorHandler(new DieErrorHandler());
dst.activateOptions();
dst.setErrorHandler(new OnlyOnceErrorHandler());
final Logger root = LogManager.getRootLogger();
root.removeAllAppenders();
root.addAppender(dst);
return new LifecycleListener() {
@Override
public void start() {
}
@Override
public void stop() {
LogManager.shutdown();
}
};
}
private ErrorLogFile() {
}
private static final class DieErrorHandler implements ErrorHandler {
@Override
public void error(String message, Exception e, int errorCode,
LoggingEvent event) {
error(e != null ? e.getMessage() : message);
}
@Override
public void error(String message, Exception e, int errorCode) {
error(e != null ? e.getMessage() : message);
}
@Override
public void error(String message) {
throw new Die("Cannot open log file: " + message);
}
@Override
public void activateOptions() {
}
@Override
public void setAppender(Appender appender) {
}
@Override
public void setBackupAppender(Appender appender) {
}
@Override
public void setLogger(Logger logger) {
}
}
}

View File

@ -23,19 +23,27 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** Tracks and executes registered {@link LifecycleListener}s. */
public class LifecycleManager {
private final List<Injector> injectors = new ArrayList<Injector>();
private LifecycleListener[] listeners;
private final LinkedHashMap<LifecycleListener, Boolean> listeners =
new LinkedHashMap<LifecycleListener, Boolean>();
private boolean started;
/** Add a single listener. */
public void add(final LifecycleListener listener) {
listeners.put(listener, true);
}
/** Add all {@link LifecycleListener}s registered in the Injector. */
public void add(final Injector injector) {
if (listeners != null) {
if (started) {
throw new IllegalStateException("Already started");
}
injectors.add(injector);
for (final Binding<LifecycleListener> binding : get(injector)) {
add(binding.getProvider().get());
}
}
/** Add all {@link LifecycleListener}s registered in the Injectors. */
@ -47,10 +55,9 @@ public class LifecycleManager {
/** Start all listeners, in the order they were registered. */
public void start() {
if (listeners == null) {
listeners = all();
for (LifecycleListener obj : listeners) {
if (!started) {
started = true;
for (LifecycleListener obj : listeners.keySet()) {
obj.start();
}
}
@ -58,9 +65,12 @@ public class LifecycleManager {
/** Stop all listeners, in the reverse order they were registered. */
public void stop() {
if (listeners != null) {
for (int i = listeners.length - 1; 0 <= i; i--) {
final LifecycleListener obj = listeners[i];
if (started) {
final List<LifecycleListener> t =
new ArrayList<LifecycleListener>(listeners.keySet());
for (int i = t.size() - 1; 0 <= i; i--) {
final LifecycleListener obj = t.get(i);
try {
obj.stop();
} catch (Throwable err) {
@ -68,28 +78,10 @@ public class LifecycleManager {
}
}
listeners = null;
started = false;
}
}
private LifecycleListener[] all() {
final Map<LifecycleListener, Boolean> found =
new LinkedHashMap<LifecycleListener, Boolean>();
for (final Injector injector : injectors) {
if (injector == null) {
continue;
}
for (final Binding<LifecycleListener> binding : get(injector)) {
found.put(binding.getProvider().get(), true);
}
}
final LifecycleListener[] a = new LifecycleListener[found.size()];
found.keySet().toArray(a);
return a;
}
private static List<Binding<LifecycleListener>> get(Injector i) {
return i.findBindingsByType(new TypeLiteral<LifecycleListener>() {});
}

View File

@ -16,7 +16,7 @@ log4j.rootCategory=DEBUG, stderr
log4j.appender.stderr=org.apache.log4j.ConsoleAppender
log4j.appender.stderr.target=System.err
log4j.appender.stderr.layout=org.apache.log4j.PatternLayout
log4j.appender.stderr.layout.ConversionPattern=%d::%-5p: %c %x - %m%n
log4j.appender.stderr.layout.ConversionPattern=[%d] %-5p %c %x: %m%n
log4j.logger.com.google.gerrit=INFO

View File

@ -13,7 +13,7 @@
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.classpathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="Main"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="daemon"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="daemon&#10;--console-log"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit-war"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M&#10;-DGerritServer=${resource_loc:/gerrit-parent/GerritServer.properties}"/>