Skip to content

Commit a875519

Browse files
committed
8191033: Regression in logging.properties: specifying .handlers= for root logger (instead of handlers=) no longer works
The behavior observed for Java 8 is restored Reviewed-by: martin, mchung
1 parent 6534e2a commit a875519

5 files changed

Lines changed: 310 additions & 5 deletions

File tree

src/java.logging/share/classes/java/util/logging/LogManager.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,23 @@ public Object run() {
388388
// create root logger before reading primordial
389389
// configuration - to ensure that it will be added
390390
// before the global logger, and not after.
391-
owner.rootLogger = owner.new RootLogger();
391+
final Logger root = owner.rootLogger = owner.new RootLogger();
392392

393393
// Read configuration.
394394
owner.readPrimordialConfiguration();
395395

396396
// Create and retain Logger for the root of the namespace.
397-
owner.addLogger(owner.rootLogger);
398-
if (!owner.rootLogger.isLevelInitialized()) {
399-
owner.rootLogger.setLevel(defaultLevel);
397+
owner.addLogger(root);
398+
399+
// For backward compatibility: add any handlers configured using
400+
// ".handlers"
401+
owner.createLoggerHandlers("", ".handlers")
402+
.stream()
403+
.forEach(root::addHandler);
404+
405+
// Initialize level if not yet initialized
406+
if (!root.isLevelInitialized()) {
407+
root.setLevel(defaultLevel);
400408
}
401409

402410
// Adding the global Logger.
@@ -1708,7 +1716,7 @@ static boolean needsUpdating(String k, Properties previous, Properties next) {
17081716
* @param k a property key in the configuration
17091717
* @param previous the old configuration
17101718
* @param next the new configuration (modified by this function)
1711-
* @param remappingFunction the mapping function.
1719+
* @param mappingFunction the mapping function.
17121720
*/
17131721
static void merge(String k, Properties previous, Properties next,
17141722
BiFunction<String, String, String> mappingFunction) {
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
import java.io.IOException;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.nio.file.Paths;
27+
import java.util.Arrays;
28+
import java.util.Collections;
29+
import java.util.List;
30+
import java.util.logging.Handler;
31+
import java.util.logging.Level;
32+
import java.util.logging.LogManager;
33+
import java.util.logging.Logger;
34+
import java.util.stream.Collectors;
35+
import java.util.stream.Stream;
36+
37+
/**
38+
* @test
39+
* @bug 8191033
40+
* @build custom.DotHandler custom.Handler
41+
* @run main/othervm RootLoggerHandlers
42+
* @author danielfuchs
43+
*/
44+
public class RootLoggerHandlers {
45+
46+
public static final Path SRC_DIR =
47+
Paths.get(System.getProperty("test.src", "src"));
48+
public static final Path USER_DIR =
49+
Paths.get(System.getProperty("user.dir", "."));
50+
public static final Path CONFIG_FILE = Paths.get("logging.properties");
51+
52+
// Uncomment this to run the test on Java 8. Java 8 does not have
53+
// List.of(...)
54+
// static final class List {
55+
// static <T> java.util.List<T> of(T... items) {
56+
// return Collections.unmodifiableList(Arrays.asList(items));
57+
// }
58+
// }
59+
60+
public static void main(String[] args) throws IOException {
61+
Path initialProps = SRC_DIR.resolve(CONFIG_FILE);
62+
Path loggingProps = USER_DIR.resolve(CONFIG_FILE);
63+
System.setProperty("java.util.logging.config.file", loggingProps.toString());
64+
Files.copy(initialProps, loggingProps);
65+
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
66+
if (Logger.getLogger("").getLevel() != Level.INFO) {
67+
throw new RuntimeException("Expected root level INFO, got: "
68+
+ Logger.getLogger("").getLevel());
69+
}
70+
// Verify that we have two handlers. One was configured with
71+
// handlers=custom.Handler, the other with
72+
// .handlers=custom.DotHandler
73+
// Verify that exactly one of the two handlers is a custom.Handler
74+
// Verify that exactly one of the two handlers is a custom.DotHandler
75+
// Verify that the two handlers has an id of '1'
76+
checkHandlers(Logger.getLogger("").getHandlers(),
77+
1L,
78+
custom.Handler.class,
79+
custom.DotHandler.class);
80+
81+
// The log message "hi" should appear twice on the console.
82+
// We don't check that. This is just for log analysis in case
83+
// of test failure.
84+
Logger.getAnonymousLogger().info("hi");
85+
86+
// Change the root logger level to FINE in the properties file
87+
// and reload the configuration.
88+
Files.write(loggingProps,
89+
Files.lines(initialProps)
90+
.map((s) -> s.replace("INFO", "FINE"))
91+
.collect(Collectors.toList()));
92+
LogManager.getLogManager().readConfiguration();
93+
94+
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
95+
if (Logger.getLogger("").getLevel() != Level.FINE) {
96+
throw new RuntimeException("Expected root level FINE, got: "
97+
+ Logger.getLogger("").getLevel());
98+
}
99+
100+
// Verify that we have now only one handler, configured with
101+
// handlers=custom.Handler, and that the other configured with
102+
// .handlers=custom.DotHandler was ignored.
103+
// Verify that the handler is a custom.Handler
104+
// Verify that the handler has an id of '2'
105+
checkHandlers(Logger.getLogger("").getHandlers(),
106+
2L,
107+
custom.Handler.class);
108+
109+
// The log message "there" should appear only once on the console.
110+
// We don't check that. This is just for log analysis in case
111+
// of test failure.
112+
Logger.getAnonymousLogger().info("there!");
113+
114+
// Change the root logger level to FINER in the properties file
115+
// and reload the configuration.
116+
Files.write(loggingProps,
117+
Files.lines(initialProps)
118+
.map((s) -> s.replace("INFO", "FINER"))
119+
.collect(Collectors.toList()));
120+
LogManager.getLogManager().readConfiguration();
121+
122+
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
123+
if (Logger.getLogger("").getLevel() != Level.FINER) {
124+
throw new RuntimeException("Expected root level FINER, got: "
125+
+ Logger.getLogger("").getLevel());
126+
}
127+
128+
// Verify that we have only one handler, configured with
129+
// handlers=custom.Handler, and that the other configured with
130+
// .handlers=custom.DotHandler was ignored.
131+
// Verify that the handler is a custom.Handler
132+
// Verify that the handler has an id of '3'
133+
checkHandlers(Logger.getLogger("").getHandlers(),
134+
3L,
135+
custom.Handler.class);
136+
137+
// The log message "done" should appear only once on the console.
138+
// We don't check that. This is just for log analysis in case
139+
// of test failure.
140+
Logger.getAnonymousLogger().info("done!");
141+
}
142+
143+
static void checkHandlers(Handler[] handlers, Long expectedID, Class<?>... clz) {
144+
// Verify that we have the expected number of handlers.
145+
if (Stream.of(handlers).count() != clz.length) {
146+
throw new RuntimeException("Expected " + clz.length + " handlers, got: "
147+
+ List.of(Logger.getLogger("").getHandlers()));
148+
}
149+
for (Class<?> cl : clz) {
150+
// Verify that the handlers are of the expected class.
151+
// For each class, we should have exactly one handler
152+
// of that class.
153+
if (Stream.of(handlers)
154+
.map(Object::getClass)
155+
.filter(cl::equals)
156+
.count() != 1) {
157+
throw new RuntimeException("Expected one " + cl +", got: "
158+
+ List.of(Logger.getLogger("").getHandlers()));
159+
}
160+
}
161+
// Verify that all handlers have the expected ID
162+
if (Stream.of(Logger.getLogger("").getHandlers())
163+
.map(RootLoggerHandlers::getId)
164+
.filter(expectedID::equals)
165+
.count() != clz.length) {
166+
throw new RuntimeException("Expected ids to be " + expectedID + ", got: "
167+
+ List.of(Logger.getLogger("").getHandlers()));
168+
}
169+
}
170+
171+
static long getId(Handler h) {
172+
if (h instanceof custom.Handler) {
173+
return ((custom.Handler)h).id;
174+
}
175+
if (h instanceof custom.DotHandler) {
176+
return ((custom.DotHandler)h).id;
177+
}
178+
return -1;
179+
}
180+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package custom;
24+
25+
import java.util.concurrent.atomic.AtomicLong;
26+
27+
/**
28+
*
29+
* @author danielfuchs
30+
*/
31+
public class DotHandler extends java.util.logging.ConsoleHandler {
32+
33+
public static final AtomicLong IDS = new AtomicLong();
34+
public final long id = IDS.incrementAndGet();
35+
public DotHandler() {
36+
System.out.println("DotHandler(" + id + ") created");
37+
//new Exception("DotHandler").printStackTrace();
38+
}
39+
40+
@Override
41+
public void close() {
42+
System.out.println("DotHandler(" + id + ") closed");
43+
super.close();
44+
}
45+
46+
@Override
47+
public String toString() {
48+
return this.getClass().getName() + '(' + id + ')';
49+
}
50+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package custom;
24+
25+
import java.util.concurrent.atomic.AtomicLong;
26+
27+
/**
28+
*
29+
* @author danielfuchs
30+
*/
31+
public class Handler extends java.util.logging.ConsoleHandler {
32+
33+
static final AtomicLong IDS = new AtomicLong();
34+
public final long id = IDS.incrementAndGet();
35+
public Handler() {
36+
System.out.println("Handler(" + id + ") created");
37+
}
38+
39+
@Override
40+
public void close() {
41+
System.out.println("Handler(" + id + ") closed");
42+
super.close();
43+
}
44+
45+
@Override
46+
public String toString() {
47+
return this.getClass().getName() + '(' + id + ')';
48+
}
49+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
############################################################
2+
# Global properties
3+
############################################################
4+
5+
# "handlers" specifies a comma separated list of log Handler
6+
# classes. These handlers will be installed during VM startup.
7+
#handlers= java.util.logging.ConsoleHandler
8+
handlers= custom.Handler
9+
.handlers= custom.DotHandler
10+
11+
# Default global logging level.
12+
.level= INFO
13+
14+
# Other configuration
15+
custom.Handler.level=ALL
16+
custom.DotHandler.level=ALL
17+
java.util.logging.SimpleFormatter.format=%4$s [%1$tc]: %2$s: %5$s%n
18+

0 commit comments

Comments
 (0)