org.opencastproject.util.Log Maven / Gradle / Ivy
/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.util;
import static java.lang.String.format;
import org.opencastproject.util.data.Prelude;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.UUID;
/**
* A logger that maintains a "unit of work" context to facilitate the grouping of log statements.
*
* Log methods that take message formats and arguments use the
* {@link String#format(java.util.Locale, String, Object...)} string format syntax.
*/
public final class Log {
/** The number of seconds in a minute. */
private static final int SECONDS_IN_MINUTES = 60;
/** The number of seconds in an hour. */
private static final int SECONDS_IN_HOURS = 3600;
private static final String FQCN = Log.class.getName();
private static final String JVM_SESSION = randomString();
/** Hold the context stack. */
private static final ThreadLocal> ctx = new ThreadLocal>() {
@Override protected Stack initialValue() {
Stack stack = new Stack<>();
stack.add(JVM_SESSION);
return stack;
}
};
/** Hold the current unit of work hierarchy as a string ready to use for the log methods. */
private static final ThreadLocal current = new ThreadLocal() {
@Override protected String initialValue() {
return ctxAsString();
}
};
private final Logger logger;
private final boolean isLocationAware;
/** Create a new logger from an slf4j logger. */
public Log(Logger logger) {
this.logger = logger;
this.isLocationAware = logger instanceof LocationAwareLogger;
}
/**
* Create a new log instance based on an slf4j logger for class clazz
.
*
* @see org.slf4j.LoggerFactory#getLogger(Class)
*/
public static Log mk(Class> clazz) {
return new Log(LoggerFactory.getLogger(clazz));
}
private static void updateCurrent() {
current.set(ctxAsString());
}
private static String ctxAsString() {
return "[>" + String.join("_", ctx.get()) + "] ";
}
private static String randomString() {
return UUID.randomUUID().toString().split("-")[0];
}
/** Start a new unit of work. */
public void startUnitOfWork() {
ctx.get().push(randomString());
updateCurrent();
}
/** End a unit of work. */
public void endUnitOfWork() {
if (ctx.get().size() > 1) {
ctx.get().pop();
updateCurrent();
}
}
/** Continue a log context. */
public void continueContext(Collection init) {
Stack stack = new Stack<>();
stack.addAll(init);
ctx.set(stack);
updateCurrent();
}
/**
* Renders a string representation of seconds that is easier to read by showing hours, minutes and seconds.
*
* @param seconds
* The number of seconds that you want to represent in hours, minutes and remainder seconds.
* @return A human readable string representation of seconds into hours, minutes and seconds.
*/
public static String getHumanReadableTimeString(long seconds) {
String result = "";
long hours = seconds / SECONDS_IN_HOURS;
if (hours == 1) {
result += hours + " hour ";
} else if (hours > 1) {
result += hours + " hours ";
}
long minutes = (seconds % SECONDS_IN_HOURS) / SECONDS_IN_MINUTES;
if (minutes == 1) {
result += minutes + " minute ";
} else if (minutes > 1) {
result += minutes + " minutes ";
}
long remainderSeconds = (seconds % SECONDS_IN_HOURS) % SECONDS_IN_MINUTES;
if (remainderSeconds == 1) {
result += remainderSeconds + " second";
} else if (remainderSeconds > 1) {
result += remainderSeconds + " seconds";
}
result = StringUtils.trim(result);
return result;
}
/** Return the current log context. */
public List getContext() {
return Collections.unmodifiableList(ctx.get());
}
public void debug(String msg) {
log(LocationAwareLogger.DEBUG_INT, null, msg);
}
public void debug(String msg, Object... args) {
log(LocationAwareLogger.DEBUG_INT, null, msg, args);
}
public void info(String msg) {
log(LocationAwareLogger.INFO_INT, null, msg);
}
public void info(String msg, Object... args) {
log(LocationAwareLogger.INFO_INT, null, msg, args);
}
public void warn(String msg) {
log(LocationAwareLogger.WARN_INT, null, msg);
}
public void warn(String msg, Object... args) {
log(LocationAwareLogger.WARN_INT, null, msg, args);
}
public void warn(Throwable t, String msg) {
log(LocationAwareLogger.WARN_INT, t, msg);
}
public void error(String msg) {
log(LocationAwareLogger.ERROR_INT, null, msg);
}
public void error(String msg, Object... args) {
log(LocationAwareLogger.ERROR_INT, null, msg, args);
}
public void error(Throwable t, String msg, Object... args) {
log(LocationAwareLogger.ERROR_INT, t, msg, args);
}
/** t
maybe null */
private void log(int level, Throwable t, String format, Object... args) {
final String msg = current.get() + format(convertCurlyBraces(format), args);
if (isLocationAware) {
((LocationAwareLogger) logger).log(null, FQCN, level, msg, null, t);
} else {
switch (level) {
case LocationAwareLogger.INFO_INT:
logger.info(msg);
break;
case LocationAwareLogger.WARN_INT:
logger.warn(msg);
break;
case LocationAwareLogger.ERROR_INT:
logger.error(msg);
break;
default:
Prelude.unexhaustiveMatch();
}
}
}
private static String convertCurlyBraces(String format) {
if (format == null)
return "(null message)";
return format.replaceAll("\\{\\}", "%s");
}
}