1 /* 2 * Copyright (c) 2016, 2019, 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.ByteArrayOutputStream; 24 import java.io.IOException; 25 import java.io.PrintStream; 26 import java.io.UncheckedIOException; 27 import java.util.Collections; 28 import java.util.Enumeration; 29 import java.util.ResourceBundle; 30 import java.util.concurrent.ConcurrentHashMap; 31 import java.util.concurrent.atomic.AtomicBoolean; 32 import java.util.concurrent.atomic.AtomicLong; 33 import java.util.function.Supplier; 34 import java.lang.System.Logger; 35 import java.lang.System.Logger.Level; 36 import java.util.EnumSet; 37 import java.util.HashMap; 38 import java.util.Locale; 39 import java.util.Map; 40 import jdk.internal.logger.SimpleConsoleLogger; 41 import jdk.internal.logger.SurrogateLogger; 42 import sun.util.logging.PlatformLogger; 43 44 /** 45 * @test 46 * @bug 8140364 47 * @summary JDK implementation specific unit test for SimpleConsoleLogger. 48 * Tests the behavior of SimpleConsoleLogger. 49 * @modules java.base/sun.util.logging 50 * java.base/jdk.internal.logger 51 * @build SimpleConsoleLoggerTest 52 * @run main/othervm SimpleConsoleLoggerTest 53 * @run main/othervm -Djdk.system.logger.level=OFF SimpleConsoleLoggerTest 54 * @run main/othervm -Djdk.system.logger.level=ERROR SimpleConsoleLoggerTest 55 * @run main/othervm -Djdk.system.logger.level=WARNING SimpleConsoleLoggerTest 56 * @run main/othervm -Djdk.system.logger.level=INFO SimpleConsoleLoggerTest 57 * @run main/othervm -Djdk.system.logger.level=DEBUG SimpleConsoleLoggerTest 58 * @run main/othervm -Djdk.system.logger.level=TRACE SimpleConsoleLoggerTest 59 * @run main/othervm -Djdk.system.logger.level=ALL SimpleConsoleLoggerTest 60 * @run main/othervm -Djdk.system.logger.level=WOMBAT SimpleConsoleLoggerTest 61 * @run main/othervm -Djdk.system.logger.level SimpleConsoleLoggerTest 62 * @run main/othervm -Djdk.system.logger.level=FINEST SimpleConsoleLoggerTest 63 * @run main/othervm -Djdk.system.logger.level=DEBUG -Djava.util.logging.SimpleFormatter.format=++++_%2$s%n%4$s:_%5$s%6$s%n SimpleConsoleLoggerTest 64 * @run main/othervm -Djdk.system.logger.level=DEBUG -Djdk.system.logger.format=++++_%2$s%n%4$s:_%5$s%6$s%n SimpleConsoleLoggerTest 65 * 66 * @author danielfuchs 67 */ 68 public class SimpleConsoleLoggerTest { 69 70 static final RuntimePermission LOGGERFINDER_PERMISSION = 71 new RuntimePermission("loggerFinder"); 72 final static boolean VERBOSE = false; 73 74 public static class MyBundle extends ResourceBundle { 75 76 final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>(); 77 78 @Override 79 protected Object handleGetObject(String key) { 80 if (key.contains(" (translated)")) { 81 throw new RuntimeException("Unexpected key: " + key); 82 } 83 return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)"); 84 } 85 86 @Override 87 public Enumeration<String> getKeys() { 88 return Collections.enumeration(map.keySet()); 89 } 90 91 } 92 public static class MyLoggerBundle extends MyBundle { 93 94 } 95 96 97 static class ErrorStream extends PrintStream { 98 99 static AtomicBoolean forward = new AtomicBoolean(); 100 ByteArrayOutputStream out; 101 String saved = ""; 102 public ErrorStream(ByteArrayOutputStream out) { 103 super(out); 104 this.out = out; 105 } 106 107 @Override 108 public void write(int b) { 109 super.write(b); 110 if (forward.get()) err.write(b); 111 } 112 113 @Override 114 public void write(byte[] b) { 115 super.write(b); 116 if (forward.get()) err.write(b); 117 } 118 119 @Override 120 public void write(byte[] buf, int off, int len) { 121 super.write(buf, off, len); 122 if (forward.get()) err.write(buf, off, len); 123 } 124 125 public String peek() { 126 flush(); 127 return out.toString(); 128 } 129 130 public String drain() { 131 flush(); 132 String res = out.toString(); 133 out.reset(); 134 return res; 135 } 136 137 public void store() { 138 flush(); 139 saved = out.toString(); 140 out.reset(); 141 } 142 143 public void restore() { 144 out.reset(); 145 try { 146 out.write(saved.getBytes()); 147 } catch(IOException io) { 148 throw new UncheckedIOException(io); 149 } 150 } 151 152 static final PrintStream err = System.err; 153 static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream()); 154 } 155 156 private static StringBuilder appendProperty(StringBuilder b, String name) { 157 String value = System.getProperty(name); 158 if (value == null) return b; 159 return b.append(name).append("=").append(value).append('\n'); 160 } 161 162 public static void main(String[] args) { 163 Locale.setDefault(Locale.ENGLISH); 164 System.setErr(ErrorStream.errorStream); 165 try { 166 test(args); 167 } finally { 168 try { 169 System.setErr(ErrorStream.err); 170 } catch (Error | RuntimeException x) { 171 x.printStackTrace(ErrorStream.err); 172 } 173 } 174 } 175 176 177 public static void test(String[] args) { 178 179 ErrorStream.errorStream.restore(); 180 String l = System.getProperty("jdk.system.logger.level"); 181 String f = System.getProperty("jdk.system.logger.format"); 182 String jf = System.getProperty("java.util.logging.SimpleFormatter.format"); 183 System.out.println("Running test: " 184 + "\n\tjdk.system.logger.level=\"" + l + "\"" 185 + "\n\tjdk.system.logger.format=\"" + f + "\"" 186 + "\n\tjava.util.logging.SimpleFormatter.format=\"" + jf + "\""); 187 188 test(l,f,jf); 189 System.out.println("\nPASSED: tested " + SEQUENCER.get() + " test cases"); 190 } 191 192 static final AtomicLong SEQUENCER = new AtomicLong(); 193 public static void test(String defaultLevel, String defaultFormat, String julFormat) { 194 195 final Map<Logger, String> loggerDescMap = new HashMap<>(); 196 197 SimpleConsoleLogger simple = SimpleConsoleLogger.makeSimpleLogger("test.logger"); 198 loggerDescMap.put(simple, "SimpleConsoleLogger.makeSimpleLogger(\"test.logger\")"); 199 SimpleConsoleLogger temporary = SurrogateLogger.makeSurrogateLogger("test.logger"); 200 loggerDescMap.put(temporary, "SurrogateLogger.makeSimpleLogger(\"test.logger\")"); 201 202 Level level; 203 try { 204 level = defaultLevel == null ? null : Level.valueOf(defaultLevel); 205 } catch (IllegalArgumentException ex) { 206 level = null; 207 } 208 testLogger(loggerDescMap, simple, level, false, defaultFormat); 209 testLogger(loggerDescMap, temporary, null, true, julFormat); 210 } 211 212 public static class Foo { 213 214 } 215 216 static void verbose(String msg) { 217 if (VERBOSE) { 218 System.out.println(msg); 219 } 220 } 221 222 static String getName(Level level, boolean usePlatformLevel) { 223 if (usePlatformLevel) { 224 return PlatformLogger.toPlatformLevel(level).name(); 225 } else { 226 return level.getName(); 227 } 228 } 229 230 // Calls the 8 methods defined on Logger and verify the 231 // parameters received by the underlying TestProvider.LoggerImpl 232 // logger. 233 private static void testLogger(Map<Logger, String> loggerDescMap, 234 SimpleConsoleLogger simple, 235 Level defaultLevel, 236 boolean usePlatformLevel, 237 String defaultFormat) { 238 239 System.out.println("Testing " + loggerDescMap.get(simple) + " [" + simple +"]"); 240 241 String formatStrMarker = defaultFormat == null ? "" 242 : defaultFormat.startsWith("++++") ? "++++" : ""; 243 String unexpectedMarker = defaultFormat == null ? "++++" 244 : defaultFormat.startsWith("++++") ? "????" : "++++"; 245 String formatStrSpec = defaultFormat == null ? "[date]" 246 : defaultFormat.startsWith("++++") ? "++++" : "????"; 247 String sep = defaultFormat == null ? ": " : ":_"; 248 String sepw = defaultFormat == null ? " " : "_"; 249 250 Foo foo = new Foo(); 251 String fooMsg = foo.toString(); 252 for (Level loggerLevel : defaultLevel == null 253 ? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) { 254 for (Level messageLevel : Level.values()) { 255 ErrorStream.errorStream.drain(); 256 String desc = "logger.log(messageLevel, foo): loggerLevel=" 257 + loggerLevel+", messageLevel="+messageLevel; 258 SEQUENCER.incrementAndGet(); 259 simple.log(messageLevel, foo); 260 if (loggerLevel == Level.OFF || messageLevel == Level.OFF 261 || messageLevel.compareTo(loggerLevel) < 0) { 262 if (!ErrorStream.errorStream.peek().isEmpty()) { 263 throw new RuntimeException("unexpected event in queue for " 264 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 265 } 266 } else { 267 String logged = ErrorStream.errorStream.drain(); 268 String expected = getName(messageLevel, usePlatformLevel) + sep + fooMsg; 269 if (!logged.contains("SimpleConsoleLoggerTest testLogger") 270 || !logged.contains(formatStrMarker) 271 || logged.contains(unexpectedMarker) 272 || !logged.contains(expected)) { 273 throw new RuntimeException("mismatch for " + desc 274 + "\n\texpected:" + "\n<<<<\n" 275 + formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n" 276 + expected 277 + "\n>>>>" 278 + "\n\t actual:" 279 + "\n<<<<\n" + logged + ">>>>\n"); 280 } else { 281 verbose("Got expected results for " 282 + desc + "\n<<<<\n" + logged + ">>>>\n"); 283 } 284 } 285 } 286 } 287 288 String msg = "blah"; 289 for (Level loggerLevel : defaultLevel == null 290 ? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) { 291 for (Level messageLevel : Level.values()) { 292 String desc = "logger.log(messageLevel, \"blah\"): loggerLevel=" 293 + loggerLevel+", messageLevel="+messageLevel; 294 SEQUENCER.incrementAndGet(); 295 simple.log(messageLevel, msg); 296 if (loggerLevel == Level.OFF || messageLevel == Level.OFF 297 || messageLevel.compareTo(loggerLevel) < 0) { 298 if (!ErrorStream.errorStream.peek().isEmpty()) { 299 throw new RuntimeException("unexpected event in queue for " 300 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 301 } 302 } else { 303 String logged = ErrorStream.errorStream.drain(); 304 String expected = getName(messageLevel, usePlatformLevel) + sep + msg; 305 if (!logged.contains("SimpleConsoleLoggerTest testLogger") 306 || !logged.contains(formatStrMarker) 307 || logged.contains(unexpectedMarker) 308 || !logged.contains(expected)) { 309 throw new RuntimeException("mismatch for " + desc 310 + "\n\texpected:" + "\n<<<<\n" 311 + formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n" 312 + expected 313 + "\n>>>>" 314 + "\n\t actual:" 315 + "\n<<<<\n" + logged + ">>>>\n"); 316 } else { 317 verbose("Got expected results for " 318 + desc + "\n<<<<\n" + logged + ">>>>\n"); 319 } 320 } 321 } 322 } 323 324 Supplier<String> fooSupplier = new Supplier<String>() { 325 @Override 326 public String get() { 327 return this.toString(); 328 } 329 }; 330 331 for (Level loggerLevel : defaultLevel == null 332 ? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) { 333 for (Level messageLevel : Level.values()) { 334 String desc = "logger.log(messageLevel, fooSupplier): loggerLevel=" 335 + loggerLevel+", messageLevel="+messageLevel; 336 SEQUENCER.incrementAndGet(); 337 simple.log(messageLevel, fooSupplier); 338 if (loggerLevel == Level.OFF || messageLevel == Level.OFF 339 || messageLevel.compareTo(loggerLevel) < 0) { 340 if (!ErrorStream.errorStream.peek().isEmpty()) { 341 throw new RuntimeException("unexpected event in queue for " 342 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 343 } 344 } else { 345 String logged = ErrorStream.errorStream.drain(); 346 String expected = getName(messageLevel, usePlatformLevel) + sep + fooSupplier.get(); 347 if (!logged.contains("SimpleConsoleLoggerTest testLogger") 348 || !logged.contains(formatStrMarker) 349 || logged.contains(unexpectedMarker) 350 || !logged.contains(expected)) { 351 throw new RuntimeException("mismatch for " + desc 352 + "\n\texpected:" + "\n<<<<\n" 353 + formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n" 354 + expected 355 + "\n>>>>" 356 + "\n\t actual:" 357 + "\n<<<<\n" + logged + ">>>>\n"); 358 } else { 359 verbose("Got expected results for " 360 + desc + "\n<<<<\n" + logged + ">>>>\n"); 361 } 362 } 363 } 364 } 365 366 367 String format = "two params [{1} {2}]"; 368 Object arg1 = foo; 369 Object arg2 = msg; 370 for (Level loggerLevel : defaultLevel == null 371 ? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) { 372 for (Level messageLevel : Level.values()) { 373 String desc = "logger.log(messageLevel, format, params...): loggerLevel=" 374 + loggerLevel+", messageLevel="+messageLevel; 375 SEQUENCER.incrementAndGet(); 376 simple.log(messageLevel, format, foo, msg); 377 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 378 if (!ErrorStream.errorStream.peek().isEmpty()) { 379 throw new RuntimeException("unexpected event in queue for " 380 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 381 } 382 } else { 383 String logged = ErrorStream.errorStream.drain(); 384 String msgFormat = format; 385 String text = java.text.MessageFormat.format(msgFormat, foo, msg); 386 String expected = getName(messageLevel, usePlatformLevel) + sep + text; 387 if (!logged.contains("SimpleConsoleLoggerTest testLogger") 388 || !logged.contains(formatStrMarker) 389 || !logged.contains(expected)) { 390 throw new RuntimeException("mismatch for " + desc 391 + "\n\texpected:" + "\n<<<<\n" 392 + formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n" 393 + expected 394 + "\n>>>>" 395 + "\n\t actual:" 396 + "\n<<<<\n" + logged + ">>>>\n"); 397 } else { 398 verbose("Got expected results for " 399 + desc + "\n<<<<\n" + logged + ">>>>\n"); 400 } 401 } 402 } 403 } 404 405 Throwable thrown = new Exception("OK: log me!"); 406 for (Level loggerLevel : defaultLevel == null 407 ? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) { 408 for (Level messageLevel : Level.values()) { 409 String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel=" 410 + loggerLevel+", messageLevel="+messageLevel; 411 SEQUENCER.incrementAndGet(); 412 simple.log(messageLevel, msg, thrown); 413 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 414 if (!ErrorStream.errorStream.peek().isEmpty()) { 415 throw new RuntimeException("unexpected event in queue for " 416 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 417 } 418 } else { 419 String logged = ErrorStream.errorStream.drain(); 420 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 421 thrown.printStackTrace(new PrintStream(baos)); 422 String text = baos.toString(); 423 String expected = getName(messageLevel, usePlatformLevel) + sep + msg; 424 if (!logged.contains("SimpleConsoleLoggerTest testLogger") 425 || !logged.contains(formatStrMarker) 426 || !logged.contains(expected) 427 || logged.contains(unexpectedMarker) 428 || !logged.contains(text)) { 429 throw new RuntimeException("mismatch for " + desc 430 + "\n\texpected:" + "\n<<<<\n" 431 + formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n" 432 + msg +"\n" 433 + text 434 + ">>>>" 435 + "\n\t actual:" 436 + "\n<<<<\n" + logged + ">>>>\n"); 437 } else { 438 verbose("Got expected results for " 439 + desc + "\n<<<<\n" + logged + ">>>>\n"); 440 } 441 } 442 } 443 } 444 445 446 for (Level loggerLevel : defaultLevel == null 447 ? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) { 448 for (Level messageLevel : Level.values()) { 449 String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel=" 450 + loggerLevel+", messageLevel="+messageLevel; 451 SEQUENCER.incrementAndGet(); 452 simple.log(messageLevel, fooSupplier, thrown); 453 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 454 if (!ErrorStream.errorStream.peek().isEmpty()) { 455 throw new RuntimeException("unexpected event in queue for " 456 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 457 } 458 } else { 459 String logged = ErrorStream.errorStream.drain(); 460 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 461 thrown.printStackTrace(new PrintStream(baos)); 462 String text = baos.toString(); 463 String expected = getName(messageLevel, usePlatformLevel) + sep + fooSupplier.get(); 464 if (!logged.contains("SimpleConsoleLoggerTest testLogger") 465 || !logged.contains(formatStrMarker) 466 || !logged.contains(expected) 467 || logged.contains(unexpectedMarker) 468 || !logged.contains(text)) { 469 throw new RuntimeException("mismatch for " + desc 470 + "\n\texpected:" + "\n<<<<\n" 471 + formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n" 472 + expected +"\n" 473 + text 474 + ">>>>" 475 + "\n\t actual:" 476 + "\n<<<<\n" + logged + ">>>>\n"); 477 } else { 478 verbose("Got expected results for " 479 + desc + "\n<<<<\n" + logged + ">>>>\n"); 480 } 481 } 482 } 483 } 484 485 ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); 486 for (Level loggerLevel : defaultLevel == null 487 ? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) { 488 for (Level messageLevel : Level.values()) { 489 String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel=" 490 + loggerLevel+", messageLevel="+messageLevel; 491 SEQUENCER.incrementAndGet(); 492 simple.log(messageLevel, bundle, format, foo, msg); 493 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 494 if (!ErrorStream.errorStream.peek().isEmpty()) { 495 throw new RuntimeException("unexpected event in queue for " 496 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 497 } 498 } else { 499 String logged = ErrorStream.errorStream.drain(); 500 String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg); 501 if (!logged.contains("SimpleConsoleLoggerTest testLogger") 502 || !logged.contains(formatStrMarker) 503 || logged.contains(unexpectedMarker) 504 || !logged.contains(getName(messageLevel, usePlatformLevel) + sep + text)) { 505 throw new RuntimeException("mismatch for " + desc 506 + "\n\texpected:" + "\n<<<<\n" 507 + formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n" 508 + getName(messageLevel, usePlatformLevel) + " " + text 509 + "\n>>>>" 510 + "\n\t actual:" 511 + "\n<<<<\n" + logged + ">>>>\n"); 512 } else { 513 verbose("Got expected results for " 514 + desc + "\n<<<<\n" + logged + ">>>>\n"); 515 } 516 } 517 } 518 } 519 520 for (Level loggerLevel : defaultLevel == null 521 ? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) { 522 for (Level messageLevel : Level.values()) { 523 String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel=" 524 + loggerLevel+", messageLevel="+messageLevel; 525 SEQUENCER.incrementAndGet(); 526 simple.log(messageLevel, bundle, msg, thrown); 527 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 528 if (!ErrorStream.errorStream.peek().isEmpty()) { 529 throw new RuntimeException("unexpected event in queue for " 530 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 531 } 532 } else { 533 String logged = ErrorStream.errorStream.drain(); 534 String textMsg = bundle.getString(msg); 535 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 536 thrown.printStackTrace(new PrintStream(baos)); 537 String text = baos.toString(); 538 String expected = getName(messageLevel, usePlatformLevel) + sep + textMsg; 539 if (!logged.contains("SimpleConsoleLoggerTest testLogger") 540 || !logged.contains(formatStrMarker) 541 || !logged.contains(expected) 542 || logged.contains(unexpectedMarker) 543 || !logged.contains(text)) { 544 throw new RuntimeException("mismatch for " + desc 545 + "\n\texpected:" + "\n<<<<\n" 546 + formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n" 547 + expected +"\n" 548 + text 549 + ">>>>" 550 + "\n\t actual:" 551 + "\n<<<<\n" + logged + ">>>>\n"); 552 } else { 553 verbose("Got expected results for " 554 + desc + "\n<<<<\n" + logged + ">>>>\n"); 555 } 556 } 557 } 558 } 559 560 } 561 }