com.atlassian.bamboo.specs.util.Logger Maven / Gradle / Ivy
package com.atlassian.bamboo.specs.util;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.io.PrintStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Optional;
/**
* Simple utility for logging to {@link System#out} at various log levels. Logging level is controlled by a system
* property {@link Logger#LOG_LEVEL_PROPERTY}.
*
* @see Logger#LOG_LEVEL_PROPERTY
* @see Logger.LogLevel
*/
public final class Logger {
/**
* Available levels of logging.
*/
public enum LogLevel {
/**
* Default logging level.
*/
INFO(1),
/**
* More detailed logging level.
*/
DEBUG(2),
/**
* Most detailed logging level.
*/
TRACE(3);
LogLevel(int detailLevel) {
this.detailLevel = detailLevel;
}
private final int detailLevel;
/**
* Whether the message at given {@code logLevel} should be logged.
*
* For example, if current logging level is {@link LogLevel#DEBUG}, the method will return true for logging at
* {@link LogLevel#INFO}, but false for logging at {@link LogLevel#TRACE}.
*
* @param logLevel log level of the message
* @return true if current log level is considered detailed enough for the passed {@code logLevel} to log the
* message, false otherwise
*/
public boolean shouldLog(LogLevel logLevel) {
return this.detailLevel >= logLevel.detailLevel;
}
}
private static final ThreadLocal DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS"));
/**
* Name of system property which will indicate logging level. The value should be set to one of the values of
* {@link LogLevel} enum (case insensitive).
*
* Usage example:
*
{@code java -Dbamboo.specs.log.level=DEBUG ...}
*/
public static final String LOG_LEVEL_PROPERTY = "bamboo.specs.log.level";
/**
* Name of system property which will indicate if date should be hidden in the log. Default is false.
*
* Usage example:
*
{@code java -Dbamboo.specs.log.hideDate=true ...}
*/
public static final String LOG_HIDE_DATE = "bamboo.specs.log.hideDate";
/**
* Current log level.
*/
public static final LogLevel LOG_LEVEL = getLogLevel();
private final Class> forClass;
private final PrintStream printStream;
private Logger(@NotNull Class> forClass) {
this(forClass, System.out);
}
// for testing
Logger(@NotNull Class> forClass, @NotNull PrintStream printStream) {
this.forClass = forClass;
this.printStream = printStream;
}
/**
* Returns an instance of the logger for the given {@code class}.
*/
@NotNull
public static Logger getLogger(@NotNull Class> forClass) {
return new Logger(forClass);
}
/**
* Logs the given {@code message} at {@link LogLevel#INFO} level. The message is formatted using
* {@link String#format(String, Object...)}.
*/
public void info(@NotNull String message, @NotNull Object... args) {
log(LogLevel.INFO, message, args);
}
/**
* Logs the given {@code throwable} at {@link LogLevel#INFO} level.
*/
public void info(@NotNull Throwable throwable) {
log(LogLevel.INFO, throwable);
}
/**
* Logs the given {@code throwable} and {@code message} at {@link LogLevel#INFO} level. The message is formatted
* using {@link String#format(String, Object...)}.
*/
public void info(@NotNull Throwable throwable, @NotNull String message, @NotNull Object... args) {
log(LogLevel.INFO, throwable, message, args);
}
/**
* Logs the given {@code message} at {@link LogLevel#DEBUG} level. The message is formatted using
* {@link String#format(String, Object...)}.
*/
public void debug(@NotNull String message, @NotNull Object... args) {
log(LogLevel.DEBUG, message, args);
}
/**
* Logs the given {@code throwable} at {@link LogLevel#DEBUG} level.
*/
public void debug(@NotNull Throwable throwable) {
log(LogLevel.DEBUG, throwable);
}
/**
* Logs the given {@code throwable} and {@code message} at {@link LogLevel#DEBUG} level. The message is formatted
* using {@link String#format(String, Object...)}.
*/
public void debug(@NotNull Throwable throwable, @NotNull String message, @NotNull Object... args) {
log(LogLevel.DEBUG, throwable, message, args);
}
/**
* Logs the given {@code message} at {@link LogLevel#TRACE} level. The message is formatted using
* {@link String#format(String, Object...)}.
*/
public void trace(@NotNull String message, @NotNull Object... args) {
log(LogLevel.TRACE, message, args);
}
/**
* Logs the given {@code throwable} at {@link LogLevel#TRACE} level.
*/
public void trace(@NotNull Throwable throwable) {
log(LogLevel.TRACE, throwable);
}
/**
* Logs the given {@code throwable} and {@code message} at {@link LogLevel#TRACE} level. The message is formatted
* using {@link String#format(String, Object...)}.
*/
public void trace(@NotNull Throwable throwable, @NotNull String message, @NotNull Object... args) {
log(LogLevel.TRACE, throwable, message, args);
}
/**
* Logs the given {@code message} at {@code logLevel}. The message is formatted using
* {@link String#format(String, Object...)}.
*/
public void log(@NotNull LogLevel logLevel, @NotNull String message, @NotNull Object... args) {
if (getLogLevel().shouldLog(logLevel)) {
prefixLogLine(logLevel);
printStream.println(ArrayUtils.isNotEmpty(args) ? String.format(message, args) : message);
}
}
/**
* Logs the given {@code throwable} at {@code logLevel} level.
*/
public void log(@NotNull LogLevel logLevel, @NotNull Throwable throwable) {
if (getLogLevel().shouldLog(logLevel)) {
prefixLogLine(logLevel);
throwable.printStackTrace(printStream);
}
}
/**
* Logs the given {@code throwable} and {@code message} at {@code logLevel}. The message is formatted
* using {@link String#format(String, Object...)}.
*/
public void log(@NotNull LogLevel logLevel, @NotNull Throwable throwable, @NotNull String message, @NotNull Object... args) {
log(logLevel, message, args);
log(logLevel, throwable);
}
/**
* Returns true if logging at {@link LogLevel#INFO} is enabled.
*/
public boolean isInfoEnabled() {
return getLogLevel().shouldLog(LogLevel.INFO);
}
/**
* Returns true if logging at {@link LogLevel#DEBUG} is enabled.
*/
public boolean isDebugEnabled() {
return getLogLevel().shouldLog(LogLevel.DEBUG);
}
/**
* Returns true if logging at {@link LogLevel#TRACE} is enabled.
*/
public boolean isTraceEnabled() {
return getLogLevel().shouldLog(LogLevel.TRACE);
}
/**
* Print prefix of the log line.
*
* @param logLevel at which log level is the message being printed
*/
private void prefixLogLine(LogLevel logLevel) {
final boolean hideDate = Boolean.parseBoolean(System.getProperty(LOG_HIDE_DATE));
if (hideDate) {
printStream.print(String.format("%s [%s] ", logLevel.name(), forClass.getSimpleName()));
} else {
printStream.print(String.format("%s %s [%s] ", DATE_FORMAT.get().format(new Date()), logLevel.name(), forClass.getSimpleName()));
}
}
/**
* Get current log level.
*/
@NotNull
private static LogLevel getLogLevel() {
return Optional.ofNullable(System.getProperty(LOG_LEVEL_PROPERTY))
.filter(StringUtils::isNotEmpty)
.flatMap(levelStr -> Arrays.stream(LogLevel.values())
.filter(logLevel -> logLevel.name().equalsIgnoreCase(levelStr))
.findFirst())
.orElse(LogLevel.INFO);
}
}