JULHandler.java

  1package de.duenndns.mtmexample;
  2
  3import java.io.IOException;
  4import java.io.InputStream;
  5import java.io.PrintWriter;
  6import java.io.StringBufferInputStream;
  7import java.io.StringWriter;
  8import java.util.logging.Formatter;
  9import java.util.logging.Handler;
 10import java.util.logging.Level;
 11import java.util.logging.LogManager;
 12import java.util.logging.LogRecord;
 13import java.util.logging.Logger;
 14
 15import android.util.Log;
 16
 17/**
 18 * A <code>java.util.logging</code> (JUL) Handler for Android.
 19 * <p>
 20 * If you want fine-grained control over MTM's logging, you can copy this
 21 * class to your code base and call the static {@link #initialize()} method.
 22 * </p>
 23 * <p>
 24 * This JUL Handler passes log messages sent to JUL to the Android log, while
 25 * keeping the format and stack traces of optionally supplied Exceptions. It
 26 * further allows to install a {@link DebugLogSettings} class via
 27 * {@link #setDebugLogSettings(DebugLogSettings)} that determines whether JUL log messages of
 28 * level {@link java.util.logging.Level#FINE} or lower are logged. This gives
 29 * the application developer more control over the logged messages, while
 30 * allowing a library developer to place debug log messages without risking to
 31 * spam the Android log.
 32 * </p>
 33 * <p>
 34 * If there are no {@code DebugLogSettings} configured, then all messages sent
 35 * to JUL will be logged.
 36 * </p>
 37 * 
 38 * @author Florian Schmaus
 39 * 
 40 */
 41@SuppressWarnings("deprecation")
 42public class JULHandler extends Handler {
 43
 44	/** Implement this interface to toggle debug logging.
 45	 */
 46	public interface DebugLogSettings {
 47		public boolean isDebugLogEnabled();
 48	}
 49
 50	private static final String CLASS_NAME = JULHandler.class.getName();
 51
 52	/**
 53	 * The global LogManager configuration.
 54	 * <p>
 55	 * This configures:
 56	 * <ul>
 57	 * <li> JULHandler as the default handler for all log messages
 58	 * <li> A default log level FINEST (300). Meaning that log messages of a level 300 or higher a
 59	 * logged
 60	 * </ul>
 61	 * </p>
 62	 */
 63	private static final InputStream LOG_MANAGER_CONFIG = new StringBufferInputStream(
 64// @formatter:off
 65"handlers = " + CLASS_NAME + '\n' +
 66".level = FINEST"
 67);
 68// @formatter:on
 69
 70	// Constants for Android vs. JUL debug level comparisons
 71	private static final int FINE_INT = Level.FINE.intValue();
 72	private static final int INFO_INT = Level.INFO.intValue();
 73	private static final int WARN_INT = Level.WARNING.intValue();
 74	private static final int SEVE_INT = Level.SEVERE.intValue();
 75
 76	private static final Logger LOGGER = Logger.getLogger(CLASS_NAME);
 77
 78	/** A formatter that creates output similar to Android's Log.x. */
 79	private static final Formatter FORMATTER = new Formatter() {
 80		@Override
 81		public String format(LogRecord logRecord) {
 82			Throwable thrown = logRecord.getThrown();
 83			if (thrown != null) {
 84				StringWriter sw = new StringWriter();
 85				PrintWriter pw = new PrintWriter(sw, false);
 86				pw.write(logRecord.getMessage() + ' ');
 87				thrown.printStackTrace(pw);
 88				pw.flush();
 89				return sw.toString();
 90			} else {
 91				return logRecord.getMessage();
 92			}
 93		}
 94	};
 95
 96	private static DebugLogSettings sDebugLogSettings;
 97	private static boolean initialized = false;
 98
 99	public static void initialize() {
100		try {
101			LogManager.getLogManager().readConfiguration(LOG_MANAGER_CONFIG);
102			initialized = true;
103		} catch (IOException e) {
104			Log.e("JULHandler", "Can not initialize configuration", e);
105		}
106		if (initialized) LOGGER.info("Initialzied java.util.logging logger");
107	}
108
109	public static void setDebugLogSettings(DebugLogSettings debugLogSettings) {
110		if (!isInitialized()) initialize();
111		sDebugLogSettings = debugLogSettings;
112	}
113
114	public static boolean isInitialized() {
115		return initialized;
116	}
117
118	public JULHandler() {
119		setFormatter(FORMATTER);
120	}
121
122	@Override
123	public void close() {}
124
125	@Override
126	public void flush() {}
127
128	@Override
129	public boolean isLoggable(LogRecord record) {
130		final boolean debugLog = sDebugLogSettings == null ? true : sDebugLogSettings
131				.isDebugLogEnabled();
132
133		if (record.getLevel().intValue() <= FINE_INT) {
134			return debugLog;
135		}
136		return true;
137	}
138
139	/** JUL method that forwards log records to Android's LogCat. */
140	@Override
141	public void publish(LogRecord record) {
142		if (!isLoggable(record)) return;
143
144		final int priority = getAndroidPriority(record.getLevel());
145		final String tag = substringAfterLastDot(record.getSourceClassName());
146		final String msg = getFormatter().format(record);
147
148		Log.println(priority, tag, msg);
149	}
150
151	/** Helper to convert JUL verbosity levels to Android's Log. */
152	private static int getAndroidPriority(Level level) {
153		int value = level.intValue();
154		if (value >= SEVE_INT) {
155			return Log.ERROR;
156		} else if (value >= WARN_INT) {
157			return Log.WARN;
158		} else if (value >= INFO_INT) {
159			return Log.INFO;
160		} else {
161			return Log.DEBUG;
162		}
163	}
164
165	/** Helper to extract short class names. */
166	private static String substringAfterLastDot(String s) {
167		return s.substring(s.lastIndexOf('.') + 1).trim();
168	}
169}