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 24package test.loggerfinder; 25 26import java.lang.System.Logger; 27import java.lang.System.Logger.Level; 28import java.lang.System.LoggerFinder; 29import java.security.AccessController; 30import java.security.PrivilegedAction; 31import java.util.Optional; 32import java.util.ResourceBundle; 33import java.util.function.Predicate; 34import java.lang.StackWalker.StackFrame; 35import java.text.MessageFormat; 36import java.time.LocalDateTime; 37import java.time.format.DateTimeFormatter; 38 39/** 40 * A LoggerFinder that provides System.Logger which print directly 41 * on System.err, without involving java.logging. 42 * For the purpose of the test, loggers whose name start with java.management. 43 * will log all messages, and other loggers will only log level > INFO. 44 * @author danielfuchs 45 */ 46public class TestLoggerFinder extends LoggerFinder { 47 48 static class TestLogger implements Logger { 49 50 final String name; 51 52 public TestLogger(String name) { 53 this.name = name; 54 } 55 56 57 @Override 58 public String getName() { 59 return name; 60 } 61 62 @Override 63 public boolean isLoggable(Level level) { 64 return name.equals("javax.management") 65 || name.startsWith("javax.management.") 66 || level.getSeverity() >= Level.INFO.getSeverity(); 67 } 68 69 @Override 70 public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) { 71 if (!isLoggable(level)) return; 72 publish(level, bundle, msg, thrown); 73 } 74 75 @Override 76 public void log(Level level, ResourceBundle bundle, String format, Object... params) { 77 if (!isLoggable(level)) return; 78 publish(level, bundle, format, params); 79 } 80 81 static void publish(Level level, ResourceBundle bundle, String msg, Throwable thrown) { 82 StackFrame sf = new CallerFinder().get().get(); 83 84 if (bundle != null && msg != null) { 85 msg = bundle.getString(msg); 86 } 87 if (msg == null) msg = ""; 88 LocalDateTime ldt = LocalDateTime.now(); 89 String date = DateTimeFormatter.ISO_DATE_TIME.format(ldt); 90 System.err.println(date + " " 91 + sf.getClassName() + " " + sf.getMethodName() + "\n" 92 + String.valueOf(level) + ": " + msg); 93 thrown.printStackTrace(System.err); 94 } 95 96 static void publish(Level level, ResourceBundle bundle, String format, Object... params) { 97 StackFrame sf = new CallerFinder().get().get(); 98 if (bundle != null && format != null) { 99 format = bundle.getString(format); 100 } 101 String msg = format(format, params); 102 LocalDateTime ldt = LocalDateTime.now(); 103 String date = DateTimeFormatter.ISO_DATE_TIME.format(ldt); 104 System.err.println(date + " " 105 + sf.getClassName() + " " + sf.getMethodName() + "\n" 106 + String.valueOf(level) + ": " + msg); 107 } 108 109 static String format(String format, Object... args) { 110 if (format == null) return ""; 111 int index = 0, len = format.length(); 112 while ((index = format.indexOf(index, '{')) >= 0) { 113 if (index >= len - 2) break; 114 char c = format.charAt(index+1); 115 if (c >= '0' && c <= '9') { 116 return MessageFormat.format(format, args); 117 } 118 index++; 119 } 120 return format; 121 } 122 123 } 124 125 /* 126 * CallerFinder is a stateful predicate. 127 */ 128 static final class CallerFinder implements Predicate<StackWalker.StackFrame> { 129 private static final StackWalker WALKER; 130 static { 131 PrivilegedAction<StackWalker> pa = 132 () -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 133 WALKER = AccessController.doPrivileged(pa); 134 } 135 136 /** 137 * Returns StackFrame of the caller's frame. 138 * @return StackFrame of the caller's frame. 139 */ 140 Optional<StackWalker.StackFrame> get() { 141 return WALKER.walk((s) -> s.filter(this).findFirst()); 142 } 143 144 private boolean lookingForLogger = true; 145 /** 146 * Returns true if we have found the caller's frame, false if the frame 147 * must be skipped. 148 * 149 * @param t The frame info. 150 * @return true if we have found the caller's frame, false if the frame 151 * must be skipped. 152 */ 153 @Override 154 public boolean test(StackWalker.StackFrame s) { 155 // We should skip all frames until we have found the logger, 156 // because these frames could be frames introduced by e.g. custom 157 // sub classes of Handler. 158 Class<?> c = s.getDeclaringClass(); 159 boolean isLogger = System.Logger.class.isAssignableFrom(c); 160 if (lookingForLogger) { 161 // Skip all frames until we have found the first logger frame. 162 lookingForLogger = c != TestLogger.class; 163 return false; 164 } 165 // Continue walking until we've found the relevant calling frame. 166 // Skips logging/logger infrastructure. 167 return !isLogger; 168 } 169 } 170 171 @Override 172 public Logger getLogger(String name, Module module) { 173 return new TestLogger(name); 174 } 175 176} 177