-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathSplitLogManager.java
More file actions
150 lines (134 loc) · 5.18 KB
/
Copy pathSplitLogManager.java
File metadata and controls
150 lines (134 loc) · 5.18 KB
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
package org.jlab.logging;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogManager;
/**
* {@code LogManager} that sends errors to {@code stderr} and everything else to {@code stdout}
* @author dilks
*/
public class SplitLogManager extends LogManager {
/** whether or not this manager is managing */
private static final boolean isManaging = SplitLogManager.class.getName().equals(System.getProperty("java.util.logging.manager"));
/**
* create a new {@link Logger} instance
* @param name the name of the logger
* @return a new {@link Logger} instance
*/
@Override
public Logger getLogger(String name) {
warnIfNotManaging("getLogger");
Logger logger = super.getLogger(name);
if(logger != null)
configureHandlers(logger, true);
return logger;
}
/**
* add a new {@link Logger} instance
* @param name the name of the logger
* @return {@code true} if the argument logger was registered successfully, {@code false} if a logger of that name already exists
*/
@Override
public synchronized boolean addLogger(Logger logger) {
warnIfNotManaging("addLogger");
boolean added = super.addLogger(logger);
if(added)
configureHandlers(logger, true);
return added;
}
/**
* Configure a logger handlers such that errors go to {@code stderr} and everything else to {@code stdout}
* @param logger the {@code Logger} instance to configure
* @param includePrefix whether or not to include a prefix in the formatting
*/
public static void configureHandlers(Logger logger, boolean includePrefix) {
// do nothing, if `SplitLogManager` is not the log manager
if(!isManaging)
return;
// clear handlers
logger.setUseParentHandlers(false);
for(var handler : logger.getHandlers())
logger.removeHandler(handler);
// log message formatting
if(includePrefix)
// "[source] level: message throwable_backtrace\n"
System.setProperty(
"java.util.logging.SimpleFormatter.format",
"%4$s: [" + logger.getName().replaceAll(".*\\.","") + "] %5$s%6$s%n");
else
// "level: message throwable_backtrace\n"
System.setProperty(
"java.util.logging.SimpleFormatter.format",
"%4$s: %5$s%6$s%n");
java.util.logging.Formatter formatter = new java.util.logging.SimpleFormatter();
// stdout handler
java.util.logging.Handler infoHandler = new java.util.logging.StreamHandler(System.out, formatter) {
@Override
public synchronized void publish(java.util.logging.LogRecord record) {
if(isLoggable(record) && record.getLevel().intValue() <= Level.INFO.intValue()) {
super.publish(record);
flush();
}
}
};
// stderr handler
java.util.logging.Handler errorHandler = new java.util.logging.StreamHandler(System.err, formatter) {
@Override
public synchronized void publish(java.util.logging.LogRecord record) {
if(isLoggable(record) && record.getLevel().intValue() >= Level.WARNING.intValue()) {
super.publish(record);
flush();
}
}
};
// add the handlers
logger.addHandler(infoHandler);
logger.addHandler(errorHandler);
// set the log level, since the handlers need to know it too
Level thisLevel = null;
// if a system property named '<ClassName>.level' is set, use that
String userLevelProperty = System.getProperty(logger.getName() + ".level");
if(userLevelProperty != null)
thisLevel = Level.parse(userLevelProperty);
// else if the `SplitLogManagerConfig` default level was set, use that level
else if(SplitLogManagerConfig.INSTANCE.defaultLevelWasSet())
thisLevel = SplitLogManagerConfig.INSTANCE.getDefaultLevel();
// else fallback to the level of `logger` itself
else
thisLevel = logger.getLevel();
// if all else fails, try to use the parent's level
if(thisLevel==null) {
try {
thisLevel = logger.getParent().getLevel();
}
catch(NullPointerException e) {
System.err.println("WARNING: 'getParent()' of logger '" + logger.getName() + "' failed");
}
if(thisLevel==null) { // should never happen, but just in case, fall back to default and complain directly to `stderr`
thisLevel = Level.INFO;
System.err.println("WARNING: trouble setting level of logger '" + logger.getName() + "'; defaulting to level '" + thisLevel + "'");
}
}
configureLevel(logger, thisLevel);
}
/**
* set the log level of a logger and its handlers
* @param logger the {@code Logger} instance to configure
* @param level the {@code Level} to apply
*/
public static void configureLevel(Logger logger, Level level) {
if(!isManaging)
return;
warnIfNotManaging("configureLevel");
logger.setLevel(level);
for(var handler : logger.getHandlers())
handler.setLevel(level);
}
/**
* warn, if this log manager is not the manager
* @param src the source of the warning, such as a function name
*/
private static void warnIfNotManaging(String src) {
if(!isManaging)
System.err.println("WARNING: SplitLogManager is not the LogManager, but its '" + src + "' was called");
}
}