Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Commit

Permalink
A stab at a logging API (see #4605)
Browse files Browse the repository at this point in the history
Issues with this:

* No tests yet, since it's for discussion.
* Being able to change the threshold level of the DefaultLog is all very well
  but that only works if the default log is the one being used. OTOH, we need
  to support things like `ceylon compile --verbose`.
* The same points as are made in #4606 also apply to logging., so having a single
  place (LogFactory.setFactory()) where 'the' LogFactory defined for a whole
  application isn't ideal.
  • Loading branch information
tombentley committed Aug 24, 2012
1 parent d194b64 commit 0448ae3
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 0 deletions.
86 changes: 86 additions & 0 deletions common/src/com/redhat/ceylon/common/log/DefaultLog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.redhat.ceylon.common.log;

import java.io.PrintStream;
import java.util.concurrent.atomic.AtomicReference;

/**
* A default implementation of {@link Log} which prints to standard error.
* This is probably suitable for most command line applications.
* Other applications should set a {@link LogFactory} so that they can do
* something suitable.
* @author tom
*/
public class DefaultLog implements Log {

private static enum Level {
OFF,
ERROR,
WARN,
INFO,
DEBUG
}

private static AtomicReference<Level> LEVEL = new AtomicReference<DefaultLog.Level>(Level.WARN);

public static void setLevel(Level level) {
DefaultLog.LEVEL.set(level);
}

private final PrintStream out = System.err;

private DefaultLog() {
}

protected boolean isEnabled(Level level) {
return level.ordinal() <= this.LEVEL.get().ordinal();
}

@Override
public void error(String message, Throwable cause) {
if (isEnabled(Level.ERROR)) {
out.println(message);
if (cause != null) {
cause.printStackTrace(out);
}
}
}

@Override
public void warning(String message, Throwable cause) {
if (isEnabled(Level.WARN)) {
out.println(message);
if (cause != null) {
cause.printStackTrace(out);
}
}
}

@Override
public void info(String message, Throwable cause) {
if (isEnabled(Level.INFO)) {
out.println(message);
if (cause != null) {
cause.printStackTrace(out);
}
}
}

@Override
public void debug(String message, Throwable cause) {
if (isEnabled(Level.DEBUG)) {
out.println(message);
if (cause != null) {
cause.printStackTrace(out);
}
}
}

private static DefaultLog instance = null;
static synchronized DefaultLog getInstance() {
if (instance == null) {
instance = new DefaultLog();
}
return instance;
}

}
50 changes: 50 additions & 0 deletions common/src/com/redhat/ceylon/common/log/Log.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.redhat.ceylon.common.log;

/**
* <p>A very simple log facade.</p>
*
* <p>At the point of use a {@code Log} instance is created like this:</p>
* <pre>
* Log log = LogFactory.getLog("foo");
* // or
* log = LogFactory.getLog(Foo.class);
* </pre>
*
* @see LogFactory
* @author tom
*/
public interface Log {

/**
* Log an error.
* Do this if something is definitely wrong, but the program can continue.
* If the program cannot continue, you should throw an exception.
*/
public void error(String str, Throwable cause);

/**
* Log a warning.
* Do this if something is probably wrong, but the program can continue.
* If something is definitely wrong, call
* {@link #error(String, Throwable)} instead.
*/
public void warning(String str, Throwable cause);

/**
* Log some information.
* Do this if nothing is wrong, but the user might be interested.
* If something is probably wrong call
* {@link #warning(String, Throwable)} instead.
*/
public void info(String str, Throwable cause);

/**
* Log some debugging information.
* Do this if nothing is wrong, but the message could be useful to
* someone debugging a problem.
* If the user might be interested call
* {@link #info(String, Throwable)} instead.
*/
public void debug(String str, Throwable cause);

}
86 changes: 86 additions & 0 deletions common/src/com/redhat/ceylon/common/log/LogFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.redhat.ceylon.common.log;

import com.redhat.ceylon.common.config.CeylonConfig;

/**
* <p>The factory used to create {@link Log} instances.</p>
*
* <p>The factory methods used to create {@link Log} instances
* are {@link #getLog(String)} and {@link #getLog(Class)}. It is implementation
* dependent whether these return new instances each time they are called with
* the same argument.</p>
*
* <h3>Configuring a LogFactory</h3>
*
* <ol>
* <li>If {@link #setFactory(LogFactory)} has been called then that instance
* is used.</li>
* <li>Otherwise, if the {@code logging.factory} property of the default
* {@linkplain CeylonConfig configuration} is not null it is assumed to be
* the name of a subclass of {@code LogFactory}, and an instance is
* created using the nullary constructor. If an instance is successfully
* created it is used as the {@code LogFactory}.</li>
* <li>Otherwise (if the {@code logging.factory} property was null, or an
* instance could not be created), an instance of the LogFactory itself is
* used as the {@code LogFactory}.</li>
* <li>If instantiation of the given {@code logging.factory} was attempted
* and failed, a {@linkplain Log#error(String, Throwable) error} is
* logged</li>
* </ol>
*
* <p>Note: An application seeking to replace the default {@code LogFactory} with its
* own implementation by calling {@link #setFactory(LogFactory)}, but it must
* do so before the {@code Log} factory methods are called, (i.e. before any log
* messages are generated).</p>
*
* @author tom
*/
public class LogFactory {

protected LogFactory() {
}

private static LogFactory logFactory = null;

private static synchronized LogFactory getFactory() {
if (logFactory == null) {
String factoryClassName = CeylonConfig.get("logging.factory");
if (factoryClassName != null) {
try {
logFactory = (LogFactory) Class.forName(factoryClassName).newInstance();
} catch (Exception e) {
DefaultLog.getInstance().error("Error initializing logging factory", e);
}
}
if (logFactory == null) {
logFactory = new LogFactory();
}
}
return logFactory;
}

/**
* Sets the factory to use to create {@link Log} instances. This must be
* before any Log instances are created.
* @param factory
*/
public static synchronized void setFactory(LogFactory factory) {
if (logFactory != null) {
throw new RuntimeException("Too late!");
}
logFactory = factory;
}

protected Log createLog(String category) {
return DefaultLog.getInstance();
}

public static Log getLog(String category) {
return getFactory().createLog(category);
}

public static Log getLog(Class<?> cls) {
return getLog(cls.getName());
}

}

0 comments on commit 0448ae3

Please sign in to comment.