blob: 39c555eee9e0b8fe5957af79880ad7779edc5965 [file] [log] [blame]
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package test.loggerfinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.System.LoggerFinder;
import java.lang.reflect.Module;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Predicate;
import java.lang.StackWalker.StackFrame;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* A LoggerFinder that provides System.Logger which print directly
* on System.err, without involving java.logging.
* For the purpose of the test, loggers whose name start with java.management.
* will log all messages, and other loggers will only log level > INFO.
* @author danielfuchs
*/
public class TestLoggerFinder extends LoggerFinder {
static class TestLogger implements Logger {
final String name;
public TestLogger(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Level level) {
return name.equals("javax.management")
|| name.startsWith("javax.management.")
|| level.getSeverity() >= Level.INFO.getSeverity();
}
@Override
public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
if (!isLoggable(level)) return;
publish(level, bundle, msg, thrown);
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
if (!isLoggable(level)) return;
publish(level, bundle, format, params);
}
static void publish(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
StackFrame sf = new CallerFinder().get().get();
if (bundle != null && msg != null) {
msg = bundle.getString(msg);
}
if (msg == null) msg = "";
LocalDateTime ldt = LocalDateTime.now();
String date = DateTimeFormatter.ISO_DATE_TIME.format(ldt);
System.err.println(date + " "
+ sf.getClassName() + " " + sf.getMethodName() + "\n"
+ String.valueOf(level) + ": " + msg);
thrown.printStackTrace(System.err);
}
static void publish(Level level, ResourceBundle bundle, String format, Object... params) {
StackFrame sf = new CallerFinder().get().get();
if (bundle != null && format != null) {
format = bundle.getString(format);
}
String msg = format(format, params);
LocalDateTime ldt = LocalDateTime.now();
String date = DateTimeFormatter.ISO_DATE_TIME.format(ldt);
System.err.println(date + " "
+ sf.getClassName() + " " + sf.getMethodName() + "\n"
+ String.valueOf(level) + ": " + msg);
}
static String format(String format, Object... args) {
if (format == null) return "";
int index = 0, len = format.length();
while ((index = format.indexOf(index, '{')) >= 0) {
if (index >= len - 2) break;
char c = format.charAt(index+1);
if (c >= '0' && c <= '9') {
return MessageFormat.format(format, args);
}
index++;
}
return format;
}
}
/*
* CallerFinder is a stateful predicate.
*/
static final class CallerFinder implements Predicate<StackWalker.StackFrame> {
private static final StackWalker WALKER;
static {
PrivilegedAction<StackWalker> pa =
() -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
WALKER = AccessController.doPrivileged(pa);
}
/**
* Returns StackFrame of the caller's frame.
* @return StackFrame of the caller's frame.
*/
Optional<StackWalker.StackFrame> get() {
return WALKER.walk((s) -> s.filter(this).findFirst());
}
private boolean lookingForLogger = true;
/**
* Returns true if we have found the caller's frame, false if the frame
* must be skipped.
*
* @param t The frame info.
* @return true if we have found the caller's frame, false if the frame
* must be skipped.
*/
@Override
public boolean test(StackWalker.StackFrame s) {
// We should skip all frames until we have found the logger,
// because these frames could be frames introduced by e.g. custom
// sub classes of Handler.
Class<?> c = s.getDeclaringClass();
boolean isLogger = System.Logger.class.isAssignableFrom(c);
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
lookingForLogger = c != TestLogger.class;
return false;
}
// Continue walking until we've found the relevant calling frame.
// Skips logging/logger infrastructure.
return !isLogger;
}
}
@Override
public Logger getLogger(String name, Module module) {
return new TestLogger(name);
}
}