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 26 package jdk.nashorn.internal.runtime.logging; 27 28 import java.io.PrintWriter; 29 import java.security.AccessControlContext; 30 import java.security.AccessController; 31 import java.security.Permissions; 32 import java.security.PrivilegedAction; 33 import java.security.ProtectionDomain; 34 import java.util.logging.ConsoleHandler; 35 import java.util.logging.Formatter; 36 import java.util.logging.Handler; 37 import java.util.logging.Level; 38 import java.util.logging.LogRecord; 39 import java.util.logging.Logger; 40 import java.util.logging.LoggingPermission; 41 import jdk.nashorn.internal.objects.Global; 42 import jdk.nashorn.internal.runtime.Context; 43 import jdk.nashorn.internal.runtime.ScriptFunction; 44 import jdk.nashorn.internal.runtime.ScriptObject; 45 import jdk.nashorn.internal.runtime.ScriptRuntime; 46 import 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 52 public 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 event of given level will be logged. 231 * @see java.util.logging.Level 232 * 233 * @param level logging level 234 * @return true if event of given level will be logged. 235 */ 236 public boolean isLoggable(final Level level) { 237 return logger.isLoggable(level); 238 } 239 240 /** 241 * Shorthand for outputting a log string as log level {@link java.util.logging.Level#FINEST} on this logger 242 * @param str the string to log 243 */ 244 public void finest(final String str) { 245 log(Level.FINEST, str); 246 } 247 248 /** 249 * Shorthand for outputting a log string as log level {@link java.util.logging.Level#FINEST} on this logger 250 * @param event optional runtime event to log 251 * @param str the string to log 252 */ 253 public void finest(final RuntimeEvent<?> event, final String str) { 254 finest(str); 255 logEvent(event); 256 } 257 258 /** 259 * Shorthand for outputting a log string as log level 260 * {@link java.util.logging.Level#FINEST} on this logger 261 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 262 */ 263 public void finest(final Object... objs) { 264 log(Level.FINEST, objs); 265 } 266 267 /** 268 * Shorthand for outputting a log string as log level 269 * {@link java.util.logging.Level#FINEST} on this logger 270 * @param event optional runtime event to log 271 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 272 */ 273 public void finest(final RuntimeEvent<?> event, final Object... objs) { 274 finest(objs); 275 logEvent(event); 276 } 277 278 /** 279 * Shorthand for outputting a log string as log level 280 * {@link java.util.logging.Level#FINER} on this logger 281 * @param str the string to log 282 */ 283 public void finer(final String str) { 284 log(Level.FINER, str); 285 } 286 287 /** 288 * Shorthand for outputting a log string as log level 289 * {@link java.util.logging.Level#FINER} on this logger 290 * @param event optional runtime event to log 291 * @param str the string to log 292 */ 293 public void finer(final RuntimeEvent<?> event, final String str) { 294 finer(str); 295 logEvent(event); 296 } 297 298 /** 299 * Shorthand for outputting a log string as log level 300 * {@link java.util.logging.Level#FINER} on this logger 301 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 302 */ 303 public void finer(final Object... objs) { 304 log(Level.FINER, objs); 305 } 306 307 /** 308 * Shorthand for outputting a log string as log level 309 * {@link java.util.logging.Level#FINER} on this logger 310 * @param event optional runtime event to log 311 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 312 */ 313 public void finer(final RuntimeEvent<?> event, final Object... objs) { 314 finer(objs); 315 logEvent(event); 316 } 317 318 /** 319 * Shorthand for outputting a log string as log level 320 * {@link java.util.logging.Level#FINE} on this logger 321 * @param str the string to log 322 */ 323 public void fine(final String str) { 324 log(Level.FINE, str); 325 } 326 327 /** 328 * Shorthand for outputting a log string as log level 329 * {@link java.util.logging.Level#FINE} on this logger 330 * @param event optional runtime event to log 331 * @param str the string to log 332 */ 333 public void fine(final RuntimeEvent<?> event, final String str) { 334 fine(str); 335 logEvent(event); 336 } 337 338 /** 339 * Shorthand for outputting a log string as log level 340 * {@link java.util.logging.Level#FINE} on this logger 341 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 342 */ 343 public void fine(final Object... objs) { 344 log(Level.FINE, objs); 345 } 346 347 /** 348 * Shorthand for outputting a log string as log level 349 * {@link java.util.logging.Level#FINE} on this logger 350 * @param event optional runtime event to log 351 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 352 */ 353 public void fine(final RuntimeEvent<?> event, final Object... objs) { 354 fine(objs); 355 logEvent(event); 356 } 357 358 /** 359 * Shorthand for outputting a log string as log level 360 * {@link java.util.logging.Level#CONFIG} on this logger 361 * @param str the string to log 362 */ 363 public void config(final String str) { 364 log(Level.CONFIG, str); 365 } 366 367 /** 368 * Shorthand for outputting a log string as log level 369 * {@link java.util.logging.Level#CONFIG} on this logger 370 * @param event optional runtime event to log 371 * @param str the string to log 372 */ 373 public void config(final RuntimeEvent<?> event, final String str) { 374 config(str); 375 logEvent(event); 376 } 377 378 /** 379 * Shorthand for outputting a log string as log level 380 * {@link java.util.logging.Level#CONFIG} on this logger 381 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 382 */ 383 public void config(final Object... objs) { 384 log(Level.CONFIG, objs); 385 } 386 387 /** 388 * Shorthand for outputting a log string as log level 389 * {@link java.util.logging.Level#CONFIG} on this logger 390 * @param event optional runtime event to log 391 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 392 */ 393 public void config(final RuntimeEvent<?> event, final Object... objs) { 394 config(objs); 395 logEvent(event); 396 } 397 398 /** 399 * Shorthand for outputting a log string as log level 400 * {@link java.util.logging.Level#INFO} on this logger 401 * @param str the string to log 402 */ 403 public void info(final String str) { 404 log(Level.INFO, str); 405 } 406 407 /** 408 * Shorthand for outputting a log string as log level 409 * {@link java.util.logging.Level#INFO} on this logger 410 * @param event optional runtime event to log 411 * @param str the string to log 412 */ 413 public void info(final RuntimeEvent<?> event, final String str) { 414 info(str); 415 logEvent(event); 416 } 417 418 /** 419 * Shorthand for outputting a log string as log level 420 * {@link java.util.logging.Level#FINE} on this logger 421 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 422 */ 423 public void info(final Object... objs) { 424 log(Level.INFO, objs); 425 } 426 427 /** 428 * Shorthand for outputting a log string as log level 429 * {@link java.util.logging.Level#FINE} on this logger 430 * @param event optional runtime event to log 431 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 432 */ 433 public void info(final RuntimeEvent<?> event, final Object... objs) { 434 info(objs); 435 logEvent(event); 436 } 437 438 /** 439 * Shorthand for outputting a log string as log level 440 * {@link java.util.logging.Level#WARNING} on this logger 441 * @param str the string to log 442 */ 443 public void warning(final String str) { 444 log(Level.WARNING, str); 445 } 446 447 /** 448 * Shorthand for outputting a log string as log level 449 * {@link java.util.logging.Level#WARNING} on this logger 450 * @param event optional runtime event to log 451 * @param str the string to log 452 */ 453 public void warning(final RuntimeEvent<?> event, final String str) { 454 warning(str); 455 logEvent(event); 456 } 457 458 /** 459 * Shorthand for outputting a log string as log level 460 * {@link java.util.logging.Level#FINE} on this logger 461 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 462 */ 463 public void warning(final Object... objs) { 464 log(Level.WARNING, objs); 465 } 466 467 /** 468 * Shorthand for outputting a log string as log level 469 * {@link java.util.logging.Level#FINE} on this logger 470 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 471 * @param event optional runtime event to log 472 */ 473 public void warning(final RuntimeEvent<?> event, final Object... objs) { 474 warning(objs); 475 logEvent(event); 476 } 477 478 /** 479 * Shorthand for outputting a log string as log level 480 * {@link java.util.logging.Level#SEVERE} on this logger 481 * @param str the string to log 482 */ 483 public void severe(final String str) { 484 log(Level.SEVERE, str); 485 } 486 487 /** 488 * Shorthand for outputting a log string as log level 489 * {@link java.util.logging.Level#SEVERE} on this logger 490 * @param str the string to log 491 * @param event optional runtime event to log 492 */ 493 public void severe(final RuntimeEvent<?> event, final String str) { 494 severe(str); 495 logEvent(event); 496 } 497 498 /** 499 * Shorthand for outputting a log string as log level 500 * {@link java.util.logging.Level#SEVERE} on this logger 501 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 502 */ 503 public void severe(final Object... objs) { 504 log(Level.SEVERE, objs); 505 } 506 507 /** 508 * Shorthand for outputting a log string as log level 509 * {@link java.util.logging.Level#FINE} on this logger 510 * @param event optional runtime event to log 511 * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead 512 */ 513 public void severe(final RuntimeEvent<?> event, final Object... objs) { 514 severe(objs); 515 logEvent(event); 516 } 517 518 /** 519 * Output log line on this logger at a given level of verbosity 520 * @see java.util.logging.Level 521 * 522 * @param level minimum log level required for logging to take place 523 * @param str string to log 524 */ 525 public void log(final Level level, final String str) { 526 if (isEnabled && !isQuiet && logger.isLoggable(level)) { 527 final StringBuilder sb = new StringBuilder(); 528 for (int i = 0 ; i < indent ; i++) { 529 sb.append(' '); 530 } 531 sb.append(str); 532 logger.log(level, sb.toString()); 533 } 534 } 535 536 /** 537 * Output log line on this logger at a given level of verbosity 538 * @see java.util.logging.Level 539 * 540 * @param level minimum log level required for logging to take place 541 * @param objs objects for which to invoke toString and concatenate to log 542 */ 543 public void log(final Level level, final Object... objs) { 544 if (isEnabled && !isQuiet && logger.isLoggable(level)) { 545 final StringBuilder sb = new StringBuilder(); 546 for (final Object obj : objs) { 547 sb.append(obj); 548 } 549 log(level, sb.toString()); 550 } 551 } 552 553 /** 554 * Access control context for logger level and instantiation permissions 555 * @return access control context 556 */ 557 private static AccessControlContext createLoggerControlAccCtxt() { 558 final Permissions perms = new Permissions(); 559 perms.add(new LoggingPermission("control", null)); 560 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 561 } 562 563 }