aboutsummaryrefslogtreecommitdiffstats
path: root/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java
blob: 40f71f58077208c48971c0dcd9d1127b7ada9787 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package de.duenndns.mtmexample;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringBufferInputStream;
import java.io.StringWriter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import android.util.Log;

/**
 * A <code>java.util.logging</code> (JUL) Handler for Android.
 * <p>
 * If you want fine-grained control over MTM's logging, you can copy this
 * class to your code base and call the static {@link #initialize()} method.
 * </p>
 * <p>
 * This JUL Handler passes log messages sent to JUL to the Android log, while
 * keeping the format and stack traces of optionally supplied Exceptions. It
 * further allows to install a {@link DebugLogSettings} class via
 * {@link #setDebugLogSettings(DebugLogSettings)} that determines whether JUL log messages of
 * level {@link java.util.logging.Level#FINE} or lower are logged. This gives
 * the application developer more control over the logged messages, while
 * allowing a library developer to place debug log messages without risking to
 * spam the Android log.
 * </p>
 * <p>
 * If there are no {@code DebugLogSettings} configured, then all messages sent
 * to JUL will be logged.
 * </p>
 * 
 * @author Florian Schmaus
 * 
 */
@SuppressWarnings("deprecation")
public class JULHandler extends Handler {

	/** Implement this interface to toggle debug logging.
	 */
	public interface DebugLogSettings {
		public boolean isDebugLogEnabled();
	}

	private static final String CLASS_NAME = JULHandler.class.getName();

	/**
	 * The global LogManager configuration.
	 * <p>
	 * This configures:
	 * <ul>
	 * <li> JULHandler as the default handler for all log messages
	 * <li> A default log level FINEST (300). Meaning that log messages of a level 300 or higher a
	 * logged
	 * </ul>
	 * </p>
	 */
	private static final InputStream LOG_MANAGER_CONFIG = new StringBufferInputStream(
// @formatter:off
"handlers = " + CLASS_NAME + '\n' +
".level = FINEST"
);
// @formatter:on

	// Constants for Android vs. JUL debug level comparisons
	private static final int FINE_INT = Level.FINE.intValue();
	private static final int INFO_INT = Level.INFO.intValue();
	private static final int WARN_INT = Level.WARNING.intValue();
	private static final int SEVE_INT = Level.SEVERE.intValue();

	private static final Logger LOGGER = Logger.getLogger(CLASS_NAME);

	/** A formatter that creates output similar to Android's Log.x. */
	private static final Formatter FORMATTER = new Formatter() {
		@Override
		public String format(LogRecord logRecord) {
			Throwable thrown = logRecord.getThrown();
			if (thrown != null) {
				StringWriter sw = new StringWriter();
				PrintWriter pw = new PrintWriter(sw, false);
				pw.write(logRecord.getMessage() + ' ');
				thrown.printStackTrace(pw);
				pw.flush();
				return sw.toString();
			} else {
				return logRecord.getMessage();
			}
		}
	};

	private static DebugLogSettings sDebugLogSettings;
	private static boolean initialized = false;

	public static void initialize() {
		try {
			LogManager.getLogManager().readConfiguration(LOG_MANAGER_CONFIG);
			initialized = true;
		} catch (IOException e) {
			Log.e("JULHandler", "Can not initialize configuration", e);
		}
		if (initialized) LOGGER.info("Initialzied java.util.logging logger");
	}

	public static void setDebugLogSettings(DebugLogSettings debugLogSettings) {
		if (!isInitialized()) initialize();
		sDebugLogSettings = debugLogSettings;
	}

	public static boolean isInitialized() {
		return initialized;
	}

	public JULHandler() {
		setFormatter(FORMATTER);
	}

	@Override
	public void close() {}

	@Override
	public void flush() {}

	@Override
	public boolean isLoggable(LogRecord record) {
		final boolean debugLog = sDebugLogSettings == null ? true : sDebugLogSettings
				.isDebugLogEnabled();

		if (record.getLevel().intValue() <= FINE_INT) {
			return debugLog;
		}
		return true;
	}

	/** JUL method that forwards log records to Android's LogCat. */
	@Override
	public void publish(LogRecord record) {
		if (!isLoggable(record)) return;

		final int priority = getAndroidPriority(record.getLevel());
		final String tag = substringAfterLastDot(record.getSourceClassName());
		final String msg = getFormatter().format(record);

		Log.println(priority, tag, msg);
	}

	/** Helper to convert JUL verbosity levels to Android's Log. */
	private static int getAndroidPriority(Level level) {
		int value = level.intValue();
		if (value >= SEVE_INT) {
			return Log.ERROR;
		} else if (value >= WARN_INT) {
			return Log.WARN;
		} else if (value >= INFO_INT) {
			return Log.INFO;
		} else {
			return Log.DEBUG;
		}
	}

	/** Helper to extract short class names. */
	private static String substringAfterLastDot(String s) {
		return s.substring(s.lastIndexOf('.') + 1).trim();
	}
}