DebugLogger.java revision 953:221a84ef44c0
1/* 2 * Copyright (c) 2010, 2013, 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 26package jdk.nashorn.internal.runtime.logging; 27 28import java.io.PrintWriter; 29import java.security.AccessControlContext; 30import java.security.AccessController; 31import java.security.Permissions; 32import java.security.PrivilegedAction; 33import java.security.ProtectionDomain; 34import java.util.logging.ConsoleHandler; 35import java.util.logging.Formatter; 36import java.util.logging.Handler; 37import java.util.logging.Level; 38import java.util.logging.LogRecord; 39import java.util.logging.Logger; 40import java.util.logging.LoggingPermission; 41import jdk.nashorn.internal.objects.Global; 42import jdk.nashorn.internal.runtime.Context; 43import jdk.nashorn.internal.runtime.ScriptFunction; 44import jdk.nashorn.internal.runtime.ScriptObject; 45import jdk.nashorn.internal.runtime.ScriptRuntime; 46import jdk.nashorn.internal.runtime.events.RuntimeEvent; 47 48/** 49 * Wrapper class for Logging system. This is how you are supposed to register a logger and use it 50 */ 51 52public final class DebugLogger { 53 54 /** Disabled logger used for all loggers that need an instance, but shouldn't output anything */ 55 public static final DebugLogger DISABLED_LOGGER = new DebugLogger("disabled", Level.OFF, false); 56 57 private final Logger logger; 58 private final boolean isEnabled; 59 60 private int indent; 61 62 private static final int INDENT_SPACE = 4; 63 64 /** A quiet logger only logs {@link RuntimeEvent}s and does't output any text, regardless of level */ 65 private final boolean isQuiet; 66 67 /** 68 * Constructor 69 * 70 * A logger can be paired with a property, e.g. {@code --log:codegen:info} is equivalent to {@code -Dnashorn.codegen.log} 71 * 72 * @param loggerName name of logger - this is the unique key with which it can be identified 73 * @param loggerLevel level of the logger 74 * @param isQuiet is this a quiet logger, i.e. enabled for things like e.g. RuntimeEvent:s, but quiet otherwise 75 */ 76 public DebugLogger(final String loggerName, final Level loggerLevel, final boolean isQuiet) { 77 this.logger = instantiateLogger(loggerName, loggerLevel); 78 this.isQuiet = isQuiet; 79 assert logger != null; 80 this.isEnabled = getLevel() != Level.OFF; 81 } 82 83 private static Logger instantiateLogger(final String name, final Level level) { 84 final Logger logger = java.util.logging.Logger.getLogger(name); 85 AccessController.doPrivileged(new PrivilegedAction<Void>() { 86 @Override 87 public Void run() { 88 for (final Handler h : logger.getHandlers()) { 89 logger.removeHandler(h); 90 } 91 92 logger.setLevel(level); 93 logger.setUseParentHandlers(false); 94 final Handler c = new ConsoleHandler(); 95 96 c.setFormatter(new Formatter() { 97 @Override 98 public String format(final LogRecord record) { 99 final StringBuilder sb = new StringBuilder(); 100 101 sb.append('[') 102 .append(record.getLoggerName()) 103 .append("] ") 104 .append(record.getMessage()) 105 .append('\n'); 106 107 return sb.toString(); 108 } 109 }); 110 logger.addHandler(c); 111 c.setLevel(level); 112 return null; 113 } 114 }, createLoggerControlAccCtxt()); 115 116 return logger; 117 } 118 119 /** 120 * Do not currently support chaining this with parent logger. Logger level null 121 * means disabled 122 * @return level 123 */ 124 public Level getLevel() { 125 return logger.getLevel() == null ? Level.OFF : logger.getLevel(); 126 } 127 128 /** 129 * Get the output writer for the logger. Loggers always default to 130 * stderr for output as they are used mainly to output debug info 131 * 132 * Can be inherited so this should not be static. 133 * 134 * @return print writer for log output. 135 */ 136 @SuppressWarnings("static-method") 137 public PrintWriter getOutputStream() { 138 return Context.getCurrentErr(); 139 } 140 141 /** 142 * Add quotes around a string 143 * @param str string 144 * @return quoted string 145 */ 146 public static String quote(final String str) { 147 if (str.isEmpty()) { 148 return "''"; 149 } 150 151 char startQuote = '\0'; 152 char endQuote = '\0'; 153 char quote = '\0'; 154 155 if (str.startsWith("\\") || str.startsWith("\"")) { 156 startQuote = str.charAt(0); 157 } 158 if (str.endsWith("\\") || str.endsWith("\"")) { 159 endQuote = str.charAt(str.length() - 1); 160 } 161 162 if (startQuote == '\0' || endQuote == '\0') { 163 quote = startQuote == '\0' ? endQuote : startQuote; 164 } 165 if (quote == '\0') { 166 quote = '\''; 167 } 168 169 return (startQuote == '\0' ? quote : startQuote) + str + (endQuote == '\0' ? quote : endQuote); 170 } 171 172 /** 173 * Check if the logger is enabled 174 * @return true if enabled 175 */ 176 public boolean isEnabled() { 177 return isEnabled; 178 } 179 180 /** 181 * Check if the logger is enabled 182 * @param logger logger to check, null will return false 183 * @return true if enabled 184 */ 185 public static boolean isEnabled(final DebugLogger logger) { 186 return logger != null && logger.isEnabled(); 187 } 188 189 /** 190 * If you want to change the indent level of your logger, call indent with a new position. 191 * Positions start at 0 and are increased by one for a new "tab" 192 * 193 * @param pos indent position 194 */ 195 public void indent(final int pos) { 196 if (isEnabled) { 197 indent += pos * INDENT_SPACE; 198 } 199 } 200 201 /** 202 * Add an indent position 203 */ 204 public void indent() { 205 indent += INDENT_SPACE; 206 } 207 208 /** 209 * Unindent a position 210 */ 211 public void unindent() { 212 indent -= INDENT_SPACE; 213 if (indent < 0) { 214 indent = 0; 215 } 216 } 217 218 private static void logEvent(final RuntimeEvent<?> event) { 219 if (event != null) { 220 final Global global = Context.getGlobal(); 221 if (global.has("Debug")) { 222 final ScriptObject debug = (ScriptObject)global.get("Debug"); 223 final ScriptFunction addRuntimeEvent = (ScriptFunction)debug.get("addRuntimeEvent"); 224 ScriptRuntime.apply(addRuntimeEvent, debug, event); 225 } 226 } 227 } 228 229 /** 230 * Check if the logger is above the level of detail given 231 * @see java.util.logging.Level 232 * 233 * The higher the level, the more severe the warning 234 * 235 * @param level logging level 236 * @return true if level is above the given one 237 */ 238 public boolean levelCoarserThan(final Level level) { 239 return getLevel().intValue() > level.intValue(); 240 } 241 242 /** 243 * Check if the logger is above or equal to the level 244 * of detail given 245 * @see java.util.logging.Level 246 * 247 * The higher the level, the more severe the warning 248 * 249 * @param level logging level 250 * @return true if level is above the given one 251 */ 252 public boolean levelCoarserThanOrEqual(final Level level) { 253 return getLevel().intValue() >= level.intValue(); 254 } 255 256 /** 257 * Check if the logger is below the level of detail given 258 * @see java.util.logging.Level 259 * 260 * The higher the level, the more severe the warning 261 * 262 * @param level logging level 263 * @return true if level is above the given one 264 */ 265 public boolean levelFinerThan(final Level level) { 266 return getLevel().intValue() < level.intValue(); 267 } 268 269 /** 270 * Check if the logger is below or equal to the level 271 * of detail given 272 * @see java.util.logging.Level 273 * 274 * The higher the level, the more severe the warning 275 * 276 * @param level logging level 277 * @return true if level is above the given one 278 */ 279 public boolean levelFinerThanOrEqual(final Level level) { 280 return getLevel().intValue() <= level.intValue(); 281 } 282 283 /** 284 * Shorthand for outputting a log string as log level {@link java.util.logging.Level#FINEST} on this logger 285 * @param str the string to log 286 */ 287 public void finest(final String str) { 288 log(Level.FINEST, str); 289 } 290 291 /** 292 * Shorthand for outputting a log string as log level {@link java.util.logging.Level#FINEST} on this logger 293 * @param event optional runtime event to log 294 * @param str the string to log 295 */ 296 public void finest(final RuntimeEvent<?> event, final String str) { 297 finest(str); 298 logEvent(event); 299 } 300 301 /** 302 * Shorthand for outputting a log string as log level 303 * {@link java.util.logging.Level#FINEST} on this logger 304 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 305 */ 306 public void finest(final Object... objs) { 307 log(Level.FINEST, objs); 308 } 309 310 /** 311 * Shorthand for outputting a log string as log level 312 * {@link java.util.logging.Level#FINEST} on this logger 313 * @param event optional runtime event to log 314 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 315 */ 316 public void finest(final RuntimeEvent<?> event, final Object... objs) { 317 finest(objs); 318 logEvent(event); 319 } 320 321 /** 322 * Shorthand for outputting a log string as log level 323 * {@link java.util.logging.Level#FINER} on this logger 324 * @param str the string to log 325 */ 326 public void finer(final String str) { 327 log(Level.FINER, str); 328 } 329 330 /** 331 * Shorthand for outputting a log string as log level 332 * {@link java.util.logging.Level#FINER} on this logger 333 * @param event optional runtime event to log 334 * @param str the string to log 335 */ 336 public void finer(final RuntimeEvent<?> event, final String str) { 337 finer(str); 338 logEvent(event); 339 } 340 341 /** 342 * Shorthand for outputting a log string as log level 343 * {@link java.util.logging.Level#FINER} on this logger 344 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 345 */ 346 public void finer(final Object... objs) { 347 log(Level.FINER, objs); 348 } 349 350 /** 351 * Shorthand for outputting a log string as log level 352 * {@link java.util.logging.Level#FINER} on this logger 353 * @param event optional runtime event to log 354 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 355 */ 356 public void finer(final RuntimeEvent<?> event, final Object... objs) { 357 finer(objs); 358 logEvent(event); 359 } 360 361 /** 362 * Shorthand for outputting a log string as log level 363 * {@link java.util.logging.Level#FINE} on this logger 364 * @param str the string to log 365 */ 366 public void fine(final String str) { 367 log(Level.FINE, str); 368 } 369 370 /** 371 * Shorthand for outputting a log string as log level 372 * {@link java.util.logging.Level#FINE} on this logger 373 * @param event optional runtime event to log 374 * @param str the string to log 375 */ 376 public void fine(final RuntimeEvent<?> event, final String str) { 377 fine(str); 378 logEvent(event); 379 } 380 381 /** 382 * Shorthand for outputting a log string as log level 383 * {@link java.util.logging.Level#FINE} on this logger 384 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 385 */ 386 public void fine(final Object... objs) { 387 log(Level.FINE, objs); 388 } 389 390 /** 391 * Shorthand for outputting a log string as log level 392 * {@link java.util.logging.Level#FINE} on this logger 393 * @param event optional runtime event to log 394 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 395 */ 396 public void fine(final RuntimeEvent<?> event, final Object... objs) { 397 fine(objs); 398 logEvent(event); 399 } 400 401 /** 402 * Shorthand for outputting a log string as log level 403 * {@link java.util.logging.Level#CONFIG} on this logger 404 * @param str the string to log 405 */ 406 public void config(final String str) { 407 log(Level.CONFIG, str); 408 } 409 410 /** 411 * Shorthand for outputting a log string as log level 412 * {@link java.util.logging.Level#CONFIG} on this logger 413 * @param event optional runtime event to log 414 * @param str the string to log 415 */ 416 public void config(final RuntimeEvent<?> event, final String str) { 417 config(str); 418 logEvent(event); 419 } 420 421 /** 422 * Shorthand for outputting a log string as log level 423 * {@link java.util.logging.Level#CONFIG} on this logger 424 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 425 */ 426 public void config(final Object... objs) { 427 log(Level.CONFIG, objs); 428 } 429 430 /** 431 * Shorthand for outputting a log string as log level 432 * {@link java.util.logging.Level#CONFIG} on this logger 433 * @param event optional runtime event to log 434 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 435 */ 436 public void config(final RuntimeEvent<?> event, final Object... objs) { 437 config(objs); 438 logEvent(event); 439 } 440 441 /** 442 * Shorthand for outputting a log string as log level 443 * {@link java.util.logging.Level#INFO} on this logger 444 * @param str the string to log 445 */ 446 public void info(final String str) { 447 log(Level.INFO, str); 448 } 449 450 /** 451 * Shorthand for outputting a log string as log level 452 * {@link java.util.logging.Level#INFO} on this logger 453 * @param event optional runtime event to log 454 * @param str the string to log 455 */ 456 public void info(final RuntimeEvent<?> event, final String str) { 457 info(str); 458 logEvent(event); 459 } 460 461 /** 462 * Shorthand for outputting a log string as log level 463 * {@link java.util.logging.Level#FINE} on this logger 464 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 465 */ 466 public void info(final Object... objs) { 467 log(Level.INFO, objs); 468 } 469 470 /** 471 * Shorthand for outputting a log string as log level 472 * {@link java.util.logging.Level#FINE} on this logger 473 * @param event optional runtime event to log 474 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 475 */ 476 public void info(final RuntimeEvent<?> event, final Object... objs) { 477 info(objs); 478 logEvent(event); 479 } 480 481 /** 482 * Shorthand for outputting a log string as log level 483 * {@link java.util.logging.Level#WARNING} on this logger 484 * @param str the string to log 485 */ 486 public void warning(final String str) { 487 log(Level.WARNING, str); 488 } 489 490 /** 491 * Shorthand for outputting a log string as log level 492 * {@link java.util.logging.Level#WARNING} on this logger 493 * @param event optional runtime event to log 494 * @param str the string to log 495 */ 496 public void warning(final RuntimeEvent<?> event, final String str) { 497 warning(str); 498 logEvent(event); 499 } 500 501 /** 502 * Shorthand for outputting a log string as log level 503 * {@link java.util.logging.Level#FINE} on this logger 504 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 505 */ 506 public void warning(final Object... objs) { 507 log(Level.WARNING, objs); 508 } 509 510 /** 511 * Shorthand for outputting a log string as log level 512 * {@link java.util.logging.Level#FINE} on this logger 513 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 514 * @param event optional runtime event to log 515 */ 516 public void warning(final RuntimeEvent<?> event, final Object... objs) { 517 warning(objs); 518 logEvent(event); 519 } 520 521 /** 522 * Shorthand for outputting a log string as log level 523 * {@link java.util.logging.Level#SEVERE} on this logger 524 * @param str the string to log 525 */ 526 public void severe(final String str) { 527 log(Level.SEVERE, str); 528 } 529 530 /** 531 * Shorthand for outputting a log string as log level 532 * {@link java.util.logging.Level#SEVERE} on this logger 533 * @param str the string to log 534 * @param event optional runtime event to log 535 */ 536 public void severe(final RuntimeEvent<?> event, final String str) { 537 severe(str); 538 logEvent(event); 539 } 540 541 /** 542 * Shorthand for outputting a log string as log level 543 * {@link java.util.logging.Level#SEVERE} on this logger 544 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 545 */ 546 public void severe(final Object... objs) { 547 log(Level.SEVERE, objs); 548 } 549 550 /** 551 * Shorthand for outputting a log string as log level 552 * {@link java.util.logging.Level#FINE} on this logger 553 * @param event optional runtime event to log 554 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 555 */ 556 public void severe(final RuntimeEvent<?> event, final Object... objs) { 557 severe(objs); 558 logEvent(event); 559 } 560 561 /** 562 * Output log line on this logger at a given level of verbosity 563 * @see java.util.logging.Level 564 * 565 * @param level minimum log level required for logging to take place 566 * @param str string to log 567 */ 568 public void log(final Level level, final String str) { 569 if (isEnabled && !isQuiet) { 570 final StringBuilder sb = new StringBuilder(); 571 for (int i = 0 ; i < indent ; i++) { 572 sb.append(' '); 573 } 574 sb.append(str); 575 logger.log(level, sb.toString()); 576 } 577 } 578 579 /** 580 * Output log line on this logger at a given level of verbosity 581 * @see java.util.logging.Level 582 * 583 * @param level minimum log level required for logging to take place 584 * @param objs objects for which to invoke toString and concatenate to log 585 */ 586 public void log(final Level level, final Object... objs) { 587 if (isEnabled && !isQuiet) { 588 final StringBuilder sb = new StringBuilder(); 589 for (final Object obj : objs) { 590 sb.append(obj); 591 } 592 log(level, sb.toString()); 593 } 594 } 595 596 /** 597 * Access control context for logger level and instantiation permissions 598 * @return access control context 599 */ 600 private static AccessControlContext createLoggerControlAccCtxt() { 601 final Permissions perms = new Permissions(); 602 perms.add(new LoggingPermission("control", null)); 603 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 604 } 605 606} 607