1 /* 2 * Copyright (c) 2015, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.logger; 27 28 import java.io.PrintStream; 29 import java.io.PrintWriter; 30 import java.io.StringWriter; 31 import java.lang.StackWalker.StackFrame; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 import java.time.ZonedDateTime; 35 import java.util.Optional; 36 import java.util.ResourceBundle; 37 import java.util.function.Function; 38 import java.lang.System.Logger; 39 import java.util.function.Predicate; 40 import java.util.function.Supplier; 41 import sun.util.logging.PlatformLogger; 42 import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration; 43 44 /** 45 * A simple console logger to emulate the behavior of JUL loggers when 46 * in the default configuration. SimpleConsoleLoggers are also used when 47 * JUL is not present and no DefaultLoggerFinder is installed. 48 */ 49 public class SimpleConsoleLogger extends LoggerConfiguration 50 implements Logger, PlatformLogger.Bridge, PlatformLogger.ConfigurableBridge { 51 52 static final PlatformLogger.Level DEFAULT_LEVEL = PlatformLogger.Level.INFO; 53 54 final String name; 55 volatile PlatformLogger.Level level; 56 final boolean usePlatformLevel; 57 SimpleConsoleLogger(String name, boolean usePlatformLevel) { 58 this.name = name; 59 this.usePlatformLevel = usePlatformLevel; 60 } 61 62 @Override 63 public String getName() { 64 return name; 65 } 66 67 private Enum<?> logLevel(PlatformLogger.Level level) { 68 return usePlatformLevel ? level : level.systemLevel(); 69 } 70 71 private Enum<?> logLevel(Level level) { 72 return usePlatformLevel ? PlatformLogger.toPlatformLevel(level) : level; 73 } 74 75 // --------------------------------------------------- 76 // From Logger 77 // --------------------------------------------------- 78 79 @Override 80 public boolean isLoggable(Level level) { 81 return isLoggable(PlatformLogger.toPlatformLevel(level)); 82 } 83 84 @Override 85 public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) { 86 if (isLoggable(level)) { 87 if (bundle != null) { 88 key = bundle.getString(key); 89 } 90 publish(getCallerInfo(), logLevel(level), key, thrown); 91 } 92 } 93 94 @Override 95 public void log(Level level, ResourceBundle bundle, String format, Object... params) { 96 if (isLoggable(level)) { 97 if (bundle != null) { 98 format = bundle.getString(format); 99 } 100 publish(getCallerInfo(), logLevel(level), format, params); 101 } 102 } 103 104 // --------------------------------------------------- 105 // From PlatformLogger.Bridge 106 // --------------------------------------------------- 107 108 @Override 109 public boolean isLoggable(PlatformLogger.Level level) { 110 final PlatformLogger.Level effectiveLevel = effectiveLevel(); 111 return level != PlatformLogger.Level.OFF 112 && level.ordinal() >= effectiveLevel.ordinal(); 113 } 114 115 @Override 116 public boolean isEnabled() { 117 return level != PlatformLogger.Level.OFF; 118 } 119 120 @Override 121 public void log(PlatformLogger.Level level, String msg) { 122 if (isLoggable(level)) { 123 publish(getCallerInfo(), logLevel(level), msg); 124 } 125 } 126 127 @Override 128 public void log(PlatformLogger.Level level, String msg, Throwable thrown) { 129 if (isLoggable(level)) { 130 publish(getCallerInfo(), logLevel(level), msg, thrown); 131 } 132 } 133 134 @Override 135 public void log(PlatformLogger.Level level, String msg, Object... params) { 136 if (isLoggable(level)) { 137 publish(getCallerInfo(), logLevel(level), msg, params); 138 } 139 } 140 141 private PlatformLogger.Level effectiveLevel() { 142 if (level == null) return DEFAULT_LEVEL; 143 return level; 144 } 145 146 @Override 147 public PlatformLogger.Level getPlatformLevel() { 148 return level; 149 } 150 151 @Override 152 public void setPlatformLevel(PlatformLogger.Level newLevel) { 153 level = newLevel; 154 } 155 156 @Override 157 public LoggerConfiguration getLoggerConfiguration() { 158 return this; 159 } 160 161 /** 162 * Default platform logging support - output messages to System.err - 163 * equivalent to ConsoleHandler with SimpleFormatter. 164 */ 165 static PrintStream outputStream() { 166 return System.err; 167 } 168 169 // Returns the caller's class and method's name; best effort 170 // if cannot infer, return the logger's name. 171 private String getCallerInfo() { 172 Optional<StackWalker.StackFrame> frame = new CallerFinder().get(); 173 if (frame.isPresent()) { 174 return frame.get().getClassName() + " " + frame.get().getMethodName(); 175 } else { 176 return name; 177 } 205 * Returns true if we have found the caller's frame, false if the frame 206 * must be skipped. 207 * 208 * @param t The frame info. 209 * @return true if we have found the caller's frame, false if the frame 210 * must be skipped. 211 */ 212 @Override 213 public boolean test(StackWalker.StackFrame t) { 214 final String cname = t.getClassName(); 215 // We should skip all frames until we have found the logger, 216 // because these frames could be frames introduced by e.g. custom 217 // sub classes of Handler. 218 if (lookingForLogger) { 219 // Skip all frames until we have found the first logger frame. 220 lookingForLogger = !isLoggerImplFrame(cname); 221 return false; 222 } 223 // Continue walking until we've found the relevant calling frame. 224 // Skips logging/logger infrastructure. 225 return !isFilteredFrame(t); 226 } 227 228 private boolean isLoggerImplFrame(String cname) { 229 return (cname.equals("sun.util.logging.PlatformLogger") || 230 cname.equals("jdk.internal.logger.SimpleConsoleLogger")); 231 } 232 } 233 234 private String getCallerInfo(String sourceClassName, String sourceMethodName) { 235 if (sourceClassName == null) return name; 236 if (sourceMethodName == null) return sourceClassName; 237 return sourceClassName + " " + sourceMethodName; 238 } 239 240 private String toString(Throwable thrown) { 241 String throwable = ""; 242 if (thrown != null) { 243 StringWriter sw = new StringWriter(); 244 PrintWriter pw = new PrintWriter(sw); 245 pw.println(); 246 thrown.printStackTrace(pw); 247 pw.close(); 248 throwable = sw.toString(); 249 } 250 return throwable; 251 } 252 253 private synchronized String format(Enum<?> level, 254 String msg, Throwable thrown, String callerInfo) { 255 256 ZonedDateTime zdt = ZonedDateTime.now(); 257 String throwable = toString(thrown); 258 259 return String.format(Formatting.formatString, 260 zdt, 261 callerInfo, 262 name, 263 level.name(), 264 msg, 265 throwable); 266 } 267 268 // publish accepts both PlatformLogger Levels and LoggerFinder Levels. 269 private void publish(String callerInfo, Enum<?> level, String msg) { 270 outputStream().print(format(level, msg, null, callerInfo)); 271 } 272 // publish accepts both PlatformLogger Levels and LoggerFinder Levels. 273 private void publish(String callerInfo, Enum<?> level, String msg, Throwable thrown) { 274 outputStream().print(format(level, msg, thrown, callerInfo)); 275 } 276 // publish accepts both PlatformLogger Levels and LoggerFinder Levels. 277 private void publish(String callerInfo, Enum<?> level, String msg, Object... params) { 278 msg = params == null || params.length == 0 ? msg 279 : Formatting.formatMessage(msg, params); 280 outputStream().print(format(level, msg, null, callerInfo)); 281 } 282 283 public static SimpleConsoleLogger makeSimpleLogger(String name, boolean usePlatformLevel) { 284 return new SimpleConsoleLogger(name, usePlatformLevel); 285 } 286 287 public static SimpleConsoleLogger makeSimpleLogger(String name) { 288 return new SimpleConsoleLogger(name, false); 289 } 290 291 public static String getSimpleFormat(Function<String, String> defaultPropertyGetter) { 292 return Formatting.getSimpleFormat(defaultPropertyGetter); 293 } 294 295 public static boolean isFilteredFrame(StackFrame st) { 296 return Formatting.isFilteredFrame(st); 297 } 298 299 @Override 300 public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) { 301 if (isLoggable(level)) { 302 publish(getCallerInfo(), logLevel(level), msgSupplier.get()); 303 } 304 } 305 306 @Override 307 public void log(PlatformLogger.Level level, Throwable thrown, 308 Supplier<String> msgSupplier) { 309 if (isLoggable(level)) { 310 publish(getCallerInfo(), logLevel(level), msgSupplier.get(), thrown); 311 } 312 } 313 314 @Override 315 public void logp(PlatformLogger.Level level, String sourceClass, 316 String sourceMethod, String msg) { 317 if (isLoggable(level)) { 318 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg); 319 } 320 } 321 322 @Override 323 public void logp(PlatformLogger.Level level, String sourceClass, 324 String sourceMethod, Supplier<String> msgSupplier) { 325 if (isLoggable(level)) { 326 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get()); 327 } 328 } 329 330 @Override 331 public void logp(PlatformLogger.Level level, String sourceClass, String sourceMethod, 332 String msg, Object... params) { 333 if (isLoggable(level)) { 334 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params); 335 } 336 } 337 338 @Override 339 public void logp(PlatformLogger.Level level, String sourceClass, 340 String sourceMethod, String msg, Throwable thrown) { 341 if (isLoggable(level)) { 342 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown); 343 } 344 } 345 346 @Override 347 public void logp(PlatformLogger.Level level, String sourceClass, 348 String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) { 349 if (isLoggable(level)) { 350 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get(), thrown); 351 } 352 } 353 354 @Override 355 public void logrb(PlatformLogger.Level level, String sourceClass, 356 String sourceMethod, ResourceBundle bundle, String key, Object... params) { 357 if (isLoggable(level)) { 358 String msg = bundle == null ? key : bundle.getString(key); 359 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params); 360 } 361 } 362 363 @Override 364 public void logrb(PlatformLogger.Level level, String sourceClass, 365 String sourceMethod, ResourceBundle bundle, String key, Throwable thrown) { 366 if (isLoggable(level)) { 367 String msg = bundle == null ? key : bundle.getString(key); 368 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown); 369 } 370 } 371 372 @Override 373 public void logrb(PlatformLogger.Level level, ResourceBundle bundle, 374 String key, Object... params) { 375 if (isLoggable(level)) { 376 String msg = bundle == null ? key : bundle.getString(key); 377 publish(getCallerInfo(), logLevel(level), msg, params); 378 } 379 } 380 381 @Override 382 public void logrb(PlatformLogger.Level level, ResourceBundle bundle, 383 String key, Throwable thrown) { 384 if (isLoggable(level)) { 385 String msg = bundle == null ? key : bundle.getString(key); 386 publish(getCallerInfo(), logLevel(level), msg, thrown); 387 } 388 } 389 390 private static final class Formatting { 391 static final String DEFAULT_FORMAT = 392 "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n"; 393 static final String FORMAT_PROP_KEY = 394 "java.util.logging.SimpleFormatter.format"; 395 static final String formatString = getSimpleFormat(null); 396 397 // Make it easier to wrap Logger... 398 static private final String[] skips; 399 static { 400 String additionalPkgs = AccessController.doPrivileged( 401 (PrivilegedAction<String>) 402 () -> System.getProperty("jdk.logger.packages")); 403 skips = additionalPkgs == null ? new String[0] : additionalPkgs.split(","); 404 405 } 406 407 static boolean isFilteredFrame(StackFrame st) { 408 // skip logging/logger infrastructure 409 if (System.Logger.class.isAssignableFrom(st.getDeclaringClass())) { 410 return true; 411 } 412 413 // fast escape path: all the prefixes below start with 's' or 'j' and 414 // have more than 12 characters. 415 final String cname = st.getClassName(); 416 char c = cname.length() < 12 ? 0 : cname.charAt(0); 417 if (c == 's') { 418 // skip internal machinery classes 419 if (cname.startsWith("sun.util.logging.")) return true; 420 if (cname.startsWith("sun.rmi.runtime.Log")) return true; 421 } else if (c == 'j') { 422 // Message delayed at Bootstrap: no need to go further up. 423 if (cname.startsWith("jdk.internal.logger.BootstrapLogger$LogEvent")) return false; 424 // skip public machinery classes 425 if (cname.startsWith("jdk.internal.logger.")) return true; 426 if (cname.startsWith("java.util.logging.")) return true; 427 if (cname.startsWith("java.lang.invoke.MethodHandle")) return true; 428 if (cname.startsWith("java.security.AccessController")) return true; 429 } 430 431 // check additional prefixes if any are specified. 432 if (skips.length > 0) { 433 for (int i=0; i<skips.length; i++) { 434 if (!skips[i].isEmpty() && cname.startsWith(skips[i])) { 435 return true; 436 } 437 } 438 } 439 440 return false; 441 } 442 443 static String getSimpleFormat(Function<String, String> defaultPropertyGetter) { 444 // Using a lambda here causes 445 // jdk/test/java/lang/invoke/lambda/LogGeneratedClassesTest.java 446 // to fail - because that test has a testcase which somehow references 447 // PlatformLogger and counts the number of generated lambda classes 448 // So we explicitely use new PrivilegedAction<String> here. 449 String format = 450 AccessController.doPrivileged(new PrivilegedAction<String>() { 451 @Override 452 public String run() { 453 return System.getProperty(FORMAT_PROP_KEY); 454 } 455 }); 456 if (format == null && defaultPropertyGetter != null) { 457 format = defaultPropertyGetter.apply(FORMAT_PROP_KEY); 458 } 459 if (format != null) { 460 try { 461 // validate the user-defined format string 462 String.format(format, ZonedDateTime.now(), "", "", "", "", ""); 463 } catch (IllegalArgumentException e) { 464 // illegal syntax; fall back to the default format 465 format = DEFAULT_FORMAT; 466 } 467 } else { 468 format = DEFAULT_FORMAT; 469 } 470 return format; 471 } 472 473 474 // Copied from java.util.logging.Formatter.formatMessage 475 static String formatMessage(String format, Object... parameters) { 476 // Do the formatting. 477 try { | 1 /* 2 * Copyright (c) 2015, 2016, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.logger; 27 28 import java.io.PrintStream; 29 import java.io.PrintWriter; 30 import java.io.StringWriter; 31 import java.lang.StackWalker.StackFrame; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 import java.time.ZonedDateTime; 35 import java.util.Optional; 36 import java.util.ResourceBundle; 37 import java.util.function.Function; 38 import java.lang.System.Logger; 39 import java.util.PropertyPermission; 40 import java.util.function.Predicate; 41 import java.util.function.Supplier; 42 import sun.security.action.GetPropertyAction; 43 import sun.util.logging.PlatformLogger; 44 import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration; 45 46 /** 47 * A simple console logger to emulate the behavior of JUL loggers when 48 * in the default configuration. SimpleConsoleLoggers are also used when 49 * JUL is not present and no DefaultLoggerFinder is installed. 50 */ 51 public class SimpleConsoleLogger extends LoggerConfiguration 52 implements Logger, PlatformLogger.Bridge, PlatformLogger.ConfigurableBridge { 53 54 static final Level DEFAULT_LEVEL = getDefaultLevel(); 55 static final PlatformLogger.Level DEFAULT_PLATFORM_LEVEL = 56 PlatformLogger.toPlatformLevel(DEFAULT_LEVEL); 57 58 static Level getDefaultLevel() { 59 String levelName = AccessController.doPrivileged( 60 new GetPropertyAction("jdk.system.logger.level", "INFO")); 61 try { 62 return Level.valueOf(levelName); 63 } catch (IllegalArgumentException iae) { 64 return Level.INFO; 65 } 66 } 67 68 final String name; 69 volatile PlatformLogger.Level level; 70 final boolean usePlatformLevel; 71 SimpleConsoleLogger(String name, boolean usePlatformLevel) { 72 this.name = name; 73 this.usePlatformLevel = usePlatformLevel; 74 } 75 76 String defaultFormatString() { 77 return Formatting.defaultFormatString; 78 } 79 80 PlatformLogger.Level defaultLevel() { 81 return DEFAULT_PLATFORM_LEVEL; 82 } 83 84 @Override 85 public final String getName() { 86 return name; 87 } 88 89 private Enum<?> logLevel(PlatformLogger.Level level) { 90 return usePlatformLevel ? level : level.systemLevel(); 91 } 92 93 private Enum<?> logLevel(Level level) { 94 return usePlatformLevel ? PlatformLogger.toPlatformLevel(level) : level; 95 } 96 97 // --------------------------------------------------- 98 // From Logger 99 // --------------------------------------------------- 100 101 @Override 102 public final boolean isLoggable(Level level) { 103 return isLoggable(PlatformLogger.toPlatformLevel(level)); 104 } 105 106 @Override 107 public final void log(Level level, ResourceBundle bundle, String key, Throwable thrown) { 108 if (isLoggable(level)) { 109 if (bundle != null) { 110 key = bundle.getString(key); 111 } 112 publish(getCallerInfo(), logLevel(level), key, thrown); 113 } 114 } 115 116 @Override 117 public final void log(Level level, ResourceBundle bundle, String format, Object... params) { 118 if (isLoggable(level)) { 119 if (bundle != null) { 120 format = bundle.getString(format); 121 } 122 publish(getCallerInfo(), logLevel(level), format, params); 123 } 124 } 125 126 // --------------------------------------------------- 127 // From PlatformLogger.Bridge 128 // --------------------------------------------------- 129 130 @Override 131 public final boolean isLoggable(PlatformLogger.Level level) { 132 final PlatformLogger.Level effectiveLevel = effectiveLevel(); 133 return level != PlatformLogger.Level.OFF 134 && level.ordinal() >= effectiveLevel.ordinal(); 135 } 136 137 @Override 138 public final boolean isEnabled() { 139 return level != PlatformLogger.Level.OFF; 140 } 141 142 @Override 143 public final void log(PlatformLogger.Level level, String msg) { 144 if (isLoggable(level)) { 145 publish(getCallerInfo(), logLevel(level), msg); 146 } 147 } 148 149 @Override 150 public final void log(PlatformLogger.Level level, String msg, Throwable thrown) { 151 if (isLoggable(level)) { 152 publish(getCallerInfo(), logLevel(level), msg, thrown); 153 } 154 } 155 156 @Override 157 public final void log(PlatformLogger.Level level, String msg, Object... params) { 158 if (isLoggable(level)) { 159 publish(getCallerInfo(), logLevel(level), msg, params); 160 } 161 } 162 163 private PlatformLogger.Level effectiveLevel() { 164 if (level == null) return defaultLevel(); 165 return level; 166 } 167 168 @Override 169 public final PlatformLogger.Level getPlatformLevel() { 170 return level; 171 } 172 173 @Override 174 public final void setPlatformLevel(PlatformLogger.Level newLevel) { 175 level = newLevel; 176 } 177 178 @Override 179 public final LoggerConfiguration getLoggerConfiguration() { 180 return this; 181 } 182 183 /** 184 * Default platform logging support - output messages to System.err - 185 * equivalent to ConsoleHandler with SimpleFormatter. 186 */ 187 static PrintStream outputStream() { 188 return System.err; 189 } 190 191 // Returns the caller's class and method's name; best effort 192 // if cannot infer, return the logger's name. 193 private String getCallerInfo() { 194 Optional<StackWalker.StackFrame> frame = new CallerFinder().get(); 195 if (frame.isPresent()) { 196 return frame.get().getClassName() + " " + frame.get().getMethodName(); 197 } else { 198 return name; 199 } 227 * Returns true if we have found the caller's frame, false if the frame 228 * must be skipped. 229 * 230 * @param t The frame info. 231 * @return true if we have found the caller's frame, false if the frame 232 * must be skipped. 233 */ 234 @Override 235 public boolean test(StackWalker.StackFrame t) { 236 final String cname = t.getClassName(); 237 // We should skip all frames until we have found the logger, 238 // because these frames could be frames introduced by e.g. custom 239 // sub classes of Handler. 240 if (lookingForLogger) { 241 // Skip all frames until we have found the first logger frame. 242 lookingForLogger = !isLoggerImplFrame(cname); 243 return false; 244 } 245 // Continue walking until we've found the relevant calling frame. 246 // Skips logging/logger infrastructure. 247 return !Formatting.isFilteredFrame(t); 248 } 249 250 private boolean isLoggerImplFrame(String cname) { 251 return (cname.equals("sun.util.logging.PlatformLogger") || 252 cname.equals("jdk.internal.logger.SimpleConsoleLogger")); 253 } 254 } 255 256 private String getCallerInfo(String sourceClassName, String sourceMethodName) { 257 if (sourceClassName == null) return name; 258 if (sourceMethodName == null) return sourceClassName; 259 return sourceClassName + " " + sourceMethodName; 260 } 261 262 private String toString(Throwable thrown) { 263 String throwable = ""; 264 if (thrown != null) { 265 StringWriter sw = new StringWriter(); 266 PrintWriter pw = new PrintWriter(sw); 267 pw.println(); 268 thrown.printStackTrace(pw); 269 pw.close(); 270 throwable = sw.toString(); 271 } 272 return throwable; 273 } 274 275 private synchronized String format(Enum<?> level, 276 String msg, Throwable thrown, String callerInfo) { 277 278 ZonedDateTime zdt = ZonedDateTime.now(); 279 String throwable = toString(thrown); 280 281 return String.format(defaultFormatString(), 282 zdt, 283 callerInfo, 284 name, 285 level.name(), 286 msg, 287 throwable); 288 } 289 290 // publish accepts both PlatformLogger Levels and LoggerFinder Levels. 291 private void publish(String callerInfo, Enum<?> level, String msg) { 292 outputStream().print(format(level, msg, null, callerInfo)); 293 } 294 // publish accepts both PlatformLogger Levels and LoggerFinder Levels. 295 private void publish(String callerInfo, Enum<?> level, String msg, Throwable thrown) { 296 outputStream().print(format(level, msg, thrown, callerInfo)); 297 } 298 // publish accepts both PlatformLogger Levels and LoggerFinder Levels. 299 private void publish(String callerInfo, Enum<?> level, String msg, Object... params) { 300 msg = params == null || params.length == 0 ? msg 301 : Formatting.formatMessage(msg, params); 302 outputStream().print(format(level, msg, null, callerInfo)); 303 } 304 305 public static SimpleConsoleLogger makeSimpleLogger(String name) { 306 return new SimpleConsoleLogger(name, false); 307 } 308 309 @Override 310 public final void log(PlatformLogger.Level level, Supplier<String> msgSupplier) { 311 if (isLoggable(level)) { 312 publish(getCallerInfo(), logLevel(level), msgSupplier.get()); 313 } 314 } 315 316 @Override 317 public final void log(PlatformLogger.Level level, Throwable thrown, 318 Supplier<String> msgSupplier) { 319 if (isLoggable(level)) { 320 publish(getCallerInfo(), logLevel(level), msgSupplier.get(), thrown); 321 } 322 } 323 324 @Override 325 public final void logp(PlatformLogger.Level level, String sourceClass, 326 String sourceMethod, String msg) { 327 if (isLoggable(level)) { 328 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg); 329 } 330 } 331 332 @Override 333 public final void logp(PlatformLogger.Level level, String sourceClass, 334 String sourceMethod, Supplier<String> msgSupplier) { 335 if (isLoggable(level)) { 336 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get()); 337 } 338 } 339 340 @Override 341 public final void logp(PlatformLogger.Level level, String sourceClass, String sourceMethod, 342 String msg, Object... params) { 343 if (isLoggable(level)) { 344 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params); 345 } 346 } 347 348 @Override 349 public final void logp(PlatformLogger.Level level, String sourceClass, 350 String sourceMethod, String msg, Throwable thrown) { 351 if (isLoggable(level)) { 352 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown); 353 } 354 } 355 356 @Override 357 public final void logp(PlatformLogger.Level level, String sourceClass, 358 String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) { 359 if (isLoggable(level)) { 360 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get(), thrown); 361 } 362 } 363 364 @Override 365 public final void logrb(PlatformLogger.Level level, String sourceClass, 366 String sourceMethod, ResourceBundle bundle, String key, Object... params) { 367 if (isLoggable(level)) { 368 String msg = bundle == null ? key : bundle.getString(key); 369 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params); 370 } 371 } 372 373 @Override 374 public final void logrb(PlatformLogger.Level level, String sourceClass, 375 String sourceMethod, ResourceBundle bundle, String key, Throwable thrown) { 376 if (isLoggable(level)) { 377 String msg = bundle == null ? key : bundle.getString(key); 378 publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown); 379 } 380 } 381 382 @Override 383 public final void logrb(PlatformLogger.Level level, ResourceBundle bundle, 384 String key, Object... params) { 385 if (isLoggable(level)) { 386 String msg = bundle == null ? key : bundle.getString(key); 387 publish(getCallerInfo(), logLevel(level), msg, params); 388 } 389 } 390 391 @Override 392 public final void logrb(PlatformLogger.Level level, ResourceBundle bundle, 393 String key, Throwable thrown) { 394 if (isLoggable(level)) { 395 String msg = bundle == null ? key : bundle.getString(key); 396 publish(getCallerInfo(), logLevel(level), msg, thrown); 397 } 398 } 399 400 static final class Formatting { 401 static final String DEFAULT_FORMAT = 402 "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n"; 403 static final String DEFAULT_FORMAT_PROP_KEY = 404 "jdk.system.logger.format"; 405 static final String JUL_FORMAT_PROP_KEY = 406 "java.util.logging.SimpleFormatter.format"; 407 static final String defaultFormatString = getSimpleFormat(DEFAULT_FORMAT_PROP_KEY, null); 408 409 // Make it easier to wrap Logger... 410 static private final String[] skips; 411 static { 412 String additionalPkgs = AccessController.doPrivileged( 413 new GetPropertyAction("jdk.logger.packages")); 414 skips = additionalPkgs == null ? new String[0] : additionalPkgs.split(","); 415 416 } 417 418 static boolean isFilteredFrame(StackFrame st) { 419 // skip logging/logger infrastructure 420 if (System.Logger.class.isAssignableFrom(st.getDeclaringClass())) { 421 return true; 422 } 423 424 // fast escape path: all the prefixes below start with 's' or 'j' and 425 // have more than 12 characters. 426 final String cname = st.getClassName(); 427 char c = cname.length() < 12 ? 0 : cname.charAt(0); 428 if (c == 's') { 429 // skip internal machinery classes 430 if (cname.startsWith("sun.util.logging.")) return true; 431 if (cname.startsWith("sun.rmi.runtime.Log")) return true; 432 } else if (c == 'j') { 433 // Message delayed at Bootstrap: no need to go further up. 434 if (cname.startsWith("jdk.internal.logger.BootstrapLogger$LogEvent")) return false; 435 // skip public machinery classes 436 if (cname.startsWith("jdk.internal.logger.")) return true; 437 if (cname.startsWith("java.util.logging.")) return true; 438 if (cname.startsWith("java.lang.invoke.MethodHandle")) return true; 439 if (cname.startsWith("java.security.AccessController")) return true; 440 } 441 442 // check additional prefixes if any are specified. 443 if (skips.length > 0) { 444 for (int i=0; i<skips.length; i++) { 445 if (!skips[i].isEmpty() && cname.startsWith(skips[i])) { 446 return true; 447 } 448 } 449 } 450 451 return false; 452 } 453 454 static String getSimpleFormat(String key, Function<String, String> defaultPropertyGetter) { 455 // Using a lambda here causes 456 // jdk/test/java/lang/invoke/lambda/LogGeneratedClassesTest.java 457 // to fail - because that test has a testcase which somehow references 458 // PlatformLogger and counts the number of generated lambda classes 459 // So we explicitely use new PrivilegedAction<String> here. 460 String format = AccessController.doPrivileged( 461 new GetPropertyAction(key), null, 462 new PropertyPermission(DEFAULT_FORMAT_PROP_KEY, "read"), 463 new PropertyPermission(JUL_FORMAT_PROP_KEY, "read")); 464 if (format == null && defaultPropertyGetter != null) { 465 format = defaultPropertyGetter.apply(key); 466 } 467 if (format != null) { 468 try { 469 // validate the user-defined format string 470 String.format(format, ZonedDateTime.now(), "", "", "", "", ""); 471 } catch (IllegalArgumentException e) { 472 // illegal syntax; fall back to the default format 473 format = DEFAULT_FORMAT; 474 } 475 } else { 476 format = DEFAULT_FORMAT; 477 } 478 return format; 479 } 480 481 482 // Copied from java.util.logging.Formatter.formatMessage 483 static String formatMessage(String format, Object... parameters) { 484 // Do the formatting. 485 try { |