blob: 99936c555fa77732c317bcea9375c13f58738879 [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime.logging;
import java.io.PrintWriter;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.LoggingPermission;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.events.RuntimeEvent;
/**
* Wrapper class for Logging system. This is how you are supposed to register a logger and use it
*/
public final class DebugLogger {
/** Disabled logger used for all loggers that need an instance, but shouldn't output anything */
public static final DebugLogger DISABLED_LOGGER = new DebugLogger("disabled", Level.OFF, false);
private final Logger logger;
private final boolean isEnabled;
private int indent;
private static final int INDENT_SPACE = 4;
/** A quiet logger only logs {@link RuntimeEvent}s and does't output any text, regardless of level */
private final boolean isQuiet;
/**
* Constructor
*
* A logger can be paired with a property, e.g. {@code --log:codegen:info} is equivalent to {@code -Dnashorn.codegen.log}
*
* @param loggerName name of logger - this is the unique key with which it can be identified
* @param loggerLevel level of the logger
* @param isQuiet is this a quiet logger, i.e. enabled for things like e.g. RuntimeEvent:s, but quiet otherwise
*/
public DebugLogger(final String loggerName, final Level loggerLevel, final boolean isQuiet) {
this.logger = instantiateLogger(loggerName, loggerLevel);
this.isQuiet = isQuiet;
assert logger != null;
this.isEnabled = getLevel() != Level.OFF;
}
private static Logger instantiateLogger(final String name, final Level level) {
final Logger logger = java.util.logging.Logger.getLogger(name);
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
for (final Handler h : logger.getHandlers()) {
logger.removeHandler(h);
}
logger.setLevel(level);
logger.setUseParentHandlers(false);
final Handler c = new ConsoleHandler();
c.setFormatter(new Formatter() {
@Override
public String format(final LogRecord record) {
final StringBuilder sb = new StringBuilder();
sb.append('[')
.append(record.getLoggerName())
.append("] ")
.append(record.getMessage())
.append('\n');
return sb.toString();
}
});
logger.addHandler(c);
c.setLevel(level);
return null;
}
}, createLoggerControlAccCtxt());
return logger;
}
/**
* Do not currently support chaining this with parent logger. Logger level null
* means disabled
* @return level
*/
public Level getLevel() {
return logger.getLevel() == null ? Level.OFF : logger.getLevel();
}
/**
* Get the output writer for the logger. Loggers always default to
* stderr for output as they are used mainly to output debug info
*
* Can be inherited so this should not be static.
*
* @return print writer for log output.
*/
@SuppressWarnings("static-method")
public PrintWriter getOutputStream() {
return Context.getCurrentErr();
}
/**
* Add quotes around a string
* @param str string
* @return quoted string
*/
public static String quote(final String str) {
if (str.isEmpty()) {
return "''";
}
char startQuote = '\0';
char endQuote = '\0';
char quote = '\0';
if (str.startsWith("\\") || str.startsWith("\"")) {
startQuote = str.charAt(0);
}
if (str.endsWith("\\") || str.endsWith("\"")) {
endQuote = str.charAt(str.length() - 1);
}
if (startQuote == '\0' || endQuote == '\0') {
quote = startQuote == '\0' ? endQuote : startQuote;
}
if (quote == '\0') {
quote = '\'';
}
return (startQuote == '\0' ? quote : startQuote) + str + (endQuote == '\0' ? quote : endQuote);
}
/**
* Check if the logger is enabled
* @return true if enabled
*/
public boolean isEnabled() {
return isEnabled;
}
/**
* Check if the logger is enabled
* @param logger logger to check, null will return false
* @return true if enabled
*/
public static boolean isEnabled(final DebugLogger logger) {
return logger != null && logger.isEnabled();
}
/**
* If you want to change the indent level of your logger, call indent with a new position.
* Positions start at 0 and are increased by one for a new "tab"
*
* @param pos indent position
*/
public void indent(final int pos) {
if (isEnabled) {
indent += pos * INDENT_SPACE;
}
}
/**
* Add an indent position
*/
public void indent() {
indent += INDENT_SPACE;
}
/**
* Unindent a position
*/
public void unindent() {
indent -= INDENT_SPACE;
if (indent < 0) {
indent = 0;
}
}
private static void logEvent(final RuntimeEvent<?> event) {
if (event != null) {
final Global global = Context.getGlobal();
if (global.has("Debug")) {
final ScriptObject debug = (ScriptObject)global.get("Debug");
final ScriptFunction addRuntimeEvent = (ScriptFunction)debug.get("addRuntimeEvent");
ScriptRuntime.apply(addRuntimeEvent, debug, event);
}
}
}
/**
* Check if the event of given level will be logged.
* @see java.util.logging.Level
*
* @param level logging level
* @return true if event of given level will be logged.
*/
public boolean isLoggable(final Level level) {
return logger.isLoggable(level);
}
/**
* Shorthand for outputting a log string as log level {@link java.util.logging.Level#FINEST} on this logger
* @param str the string to log
*/
public void finest(final String str) {
log(Level.FINEST, str);
}
/**
* Shorthand for outputting a log string as log level {@link java.util.logging.Level#FINEST} on this logger
* @param event optional runtime event to log
* @param str the string to log
*/
public void finest(final RuntimeEvent<?> event, final String str) {
finest(str);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINEST} on this logger
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void finest(final Object... objs) {
log(Level.FINEST, objs);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINEST} on this logger
* @param event optional runtime event to log
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void finest(final RuntimeEvent<?> event, final Object... objs) {
finest(objs);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINER} on this logger
* @param str the string to log
*/
public void finer(final String str) {
log(Level.FINER, str);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINER} on this logger
* @param event optional runtime event to log
* @param str the string to log
*/
public void finer(final RuntimeEvent<?> event, final String str) {
finer(str);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINER} on this logger
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void finer(final Object... objs) {
log(Level.FINER, objs);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINER} on this logger
* @param event optional runtime event to log
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void finer(final RuntimeEvent<?> event, final Object... objs) {
finer(objs);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param str the string to log
*/
public void fine(final String str) {
log(Level.FINE, str);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param event optional runtime event to log
* @param str the string to log
*/
public void fine(final RuntimeEvent<?> event, final String str) {
fine(str);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void fine(final Object... objs) {
log(Level.FINE, objs);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param event optional runtime event to log
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void fine(final RuntimeEvent<?> event, final Object... objs) {
fine(objs);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#CONFIG} on this logger
* @param str the string to log
*/
public void config(final String str) {
log(Level.CONFIG, str);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#CONFIG} on this logger
* @param event optional runtime event to log
* @param str the string to log
*/
public void config(final RuntimeEvent<?> event, final String str) {
config(str);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#CONFIG} on this logger
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void config(final Object... objs) {
log(Level.CONFIG, objs);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#CONFIG} on this logger
* @param event optional runtime event to log
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void config(final RuntimeEvent<?> event, final Object... objs) {
config(objs);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#INFO} on this logger
* @param str the string to log
*/
public void info(final String str) {
log(Level.INFO, str);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#INFO} on this logger
* @param event optional runtime event to log
* @param str the string to log
*/
public void info(final RuntimeEvent<?> event, final String str) {
info(str);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void info(final Object... objs) {
log(Level.INFO, objs);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param event optional runtime event to log
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void info(final RuntimeEvent<?> event, final Object... objs) {
info(objs);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#WARNING} on this logger
* @param str the string to log
*/
public void warning(final String str) {
log(Level.WARNING, str);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#WARNING} on this logger
* @param event optional runtime event to log
* @param str the string to log
*/
public void warning(final RuntimeEvent<?> event, final String str) {
warning(str);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void warning(final Object... objs) {
log(Level.WARNING, objs);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
* @param event optional runtime event to log
*/
public void warning(final RuntimeEvent<?> event, final Object... objs) {
warning(objs);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#SEVERE} on this logger
* @param str the string to log
*/
public void severe(final String str) {
log(Level.SEVERE, str);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#SEVERE} on this logger
* @param str the string to log
* @param event optional runtime event to log
*/
public void severe(final RuntimeEvent<?> event, final String str) {
severe(str);
logEvent(event);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#SEVERE} on this logger
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void severe(final Object... objs) {
log(Level.SEVERE, objs);
}
/**
* Shorthand for outputting a log string as log level
* {@link java.util.logging.Level#FINE} on this logger
* @param event optional runtime event to log
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
*/
public void severe(final RuntimeEvent<?> event, final Object... objs) {
severe(objs);
logEvent(event);
}
/**
* Output log line on this logger at a given level of verbosity
* @see java.util.logging.Level
*
* @param level minimum log level required for logging to take place
* @param str string to log
*/
public void log(final Level level, final String str) {
if (isEnabled && !isQuiet && logger.isLoggable(level)) {
final StringBuilder sb = new StringBuilder();
for (int i = 0 ; i < indent ; i++) {
sb.append(' ');
}
sb.append(str);
logger.log(level, sb.toString());
}
}
/**
* Output log line on this logger at a given level of verbosity
* @see java.util.logging.Level
*
* @param level minimum log level required for logging to take place
* @param objs objects for which to invoke toString and concatenate to log
*/
public void log(final Level level, final Object... objs) {
if (isEnabled && !isQuiet && logger.isLoggable(level)) {
final StringBuilder sb = new StringBuilder();
for (final Object obj : objs) {
sb.append(obj);
}
log(level, sb.toString());
}
}
/**
* Access control context for logger level and instantiation permissions
* @return access control context
*/
private static AccessControlContext createLoggerControlAccCtxt() {
final Permissions perms = new Permissions();
perms.add(new LoggingPermission("control", null));
return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
}
}