package org.ovirt.engine.core.utils.log; import org.ovirt.engine.core.utils.log.Logged.LogLevel; import org.slf4j.Logger; /** * Utilities for logging commands using the {@link Logged} annotation for configuring the logging behavior of a class.<br> * Usage example: * * <pre> * public void execute() { * String logId = LoggedUtils.createLogId(); * <b>LoggedUtils.logEntry(log, logId, this);</b> * * try { * Object returnValue = executeCommand(); * <b>LoggedUtils.logReturn(log, logId, this, returnValue);</b> * return returnValue; * } catch (Exception e) { * <b>LoggedUtils.logError(log, logId, this, e);</b> * throw e; * } * } * </pre> */ public class LoggedUtils { /* --- Constants --- */ /** * Log format for entry log message. */ protected static final String ENTRY_LOG = "START, {}, log id: {}"; /** * Log format for return log message with no return value. */ protected static final String EXIT_LOG_VOID = "FINISH, {}, log id: {}"; /** * Log format for return log message with a return value. */ protected static final String EXIT_LOG_RETURN_VALUE = "FINISH, {}, return: {}, log id: {}"; /** * Log format for exception log message with exception message. */ protected static final String ERROR_LOG = "ERROR, {}, exception: {}, log id: {}"; /* --- Public Methods --- */ /** * Get the object (reference) id for an object. This should be a rather unique value, since it's based on the * object's hash code. * * @param obj * The object to get the id for (can be <code>null</code>). * @return The id, in HEX representation (for <code>null</code> it's 0). * @see System#identityHashCode(Object) */ public static String getObjectId(Object obj) { return Integer.toHexString(System.identityHashCode(obj)); } /** * Log entry into a command to the given log, using the specification in the {@link Logged} annotation on the * object's. If there's no {@link Logged} annotation present, or if the log level isn't sufficient to log, then * nothing happens. * * @param log * The log to log to. * @param id * ID of the message, for finding the return message. * @param obj * The object to log for. */ public static void logEntry(Logger log, String id, Object obj) { Logged logged = getAnnotation(obj); if (logged != null) { log(log, logged.executionLevel(), ENTRY_LOG, determineMessage(log, logged, obj), id); } } /** * Log return of a command to the given log, using the specification in the {@link Logged} annotation on the * object's. If there's no {@link Logged} annotation present, or if the log level isn't sufficient to log, then * nothing happens. * * @param log * The log to log to. * @param id * ID of the message, for finding the entry message. * @param obj * The object to log for. */ public static void logReturn(Logger log, String id, Object obj, Object returnValue) { Logged logged = getAnnotation(obj); if (logged != null) { LogLevel logLevel = logged.executionLevel(); if (isLogLevelOn(log, logLevel)) { if (returnValue == null || !isLogLevelOn(log, LogLevel.getMinimalLevel(logLevel, logged.returnLevel()))) { log(log, logLevel, EXIT_LOG_VOID, obj.getClass().getSimpleName(), id); } else { log(log, logLevel, EXIT_LOG_RETURN_VALUE, obj.getClass().getSimpleName(), returnValue, id); } } } } /** * Log error of a command to the given log, using the specification in the {@link Logged} annotation on the * object's. If there's no {@link Logged} annotation present, or if the log level isn't sufficient to log, then * nothing happens. * * @param log * The log to log to. * @param id * ID of the message, for finding the entry message. * @param obj * The object to log for. */ public static void logError(Logger log, String id, Object obj, Throwable t) { Logged logged = getAnnotation(obj); if (logged != null && isLogLevelOn(log, logged.errorLevel())) { log(log, logged.errorLevel(), ERROR_LOG, determineMessage(log, logged, obj), t.getMessage(), id); log.error("Exception", t); } } /* --- Helper Methods --- */ /** * Log a message to the log, taking into account the log level (checking if it's active, and using it in the log). * The message may contain parameters as in the {@code MessageFormat} specification, and they will undergo * transformation only if the log level is sufficient for the given log. * * @param log * The log to log to. * @param logLevel * The log level (may not be null). * @param message * The message to log. * @param parameters * Optional parameters for the message. * @see MessageFormat#format(Object[], StringBuffer, java.text.FieldPosition) */ protected static void log(Logger log, Logged.LogLevel logLevel, String message, Object... parameters) { try { switch (logLevel) { case FATAL: case ERROR: log.error(message, parameters); break; case WARN: log.warn(message, parameters); break; case INFO: log.info(message, parameters); break; case DEBUG: log.debug(message, parameters); break; case TRACE: log.trace(message, parameters); break; case OFF: default: break; } } catch (Throwable th) { try { log.error("Cannot perform logging", th); } catch (Throwable th1) { // Cannot really do any logging - better not try again, we have a serious logging problem } } } /** * Return the annotation present on the object's class. Since the annotation is inherited, it can be places on a * superclass and retrieved from there. * * @param obj * The object to get the annotation for. * @return The {@link Logged} annotation from the class, or <code>null</code> if there isn't any. */ protected static Logged getAnnotation(Object obj) { return obj == null ? null : obj.getClass().getAnnotation(Logged.class); } /** * Determine if the given log level is active for the given log. * * @param log * The log to check. * @param logLevel * The log level to check if active. * @return Whether the log level is active on the log or not. */ protected static boolean isLogLevelOn(Logger log, Logged.LogLevel logLevel) { switch (logLevel) { case FATAL: case ERROR: return log.isErrorEnabled(); case WARN: return log.isWarnEnabled(); case INFO: return log.isInfoEnabled(); case DEBUG: return log.isDebugEnabled(); case TRACE: return log.isTraceEnabled(); case OFF: default: return false; } } /** * Determine the message to print, according to whether the parameters should be expanded or not. * * @param log * The log is needed to check if the parameters should be printed according to their level. * @param logged * The logging definition, for determining whether to expand the parameters or not. * @param obj * The object for which to get the message. * * @return The message for logging. */ protected static Object determineMessage(Logger log, Logged logged, Object obj) { if (logged != null && !isLogLevelOn(log, LogLevel.getMinimalLevel(logged.parametersLevel(), logged.executionLevel()))) { return obj.getClass().getName(); } return obj; } }