
org.opalj.log.OPALLogger.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package log
import scala.annotation.elidable
import scala.annotation.elidable.ASSERTION
/**
* Facilitates the logging of messages that are relevant for the end user.
*
* ==Usage==
* To use OPAL's logging facility use the companion object ([[OPALLogger$]]).
*
* @note The OPALLogger framework is not intended to be used by developers to help
* debug analysis; it is intended to be used to inform (end)-users about the
* analysis progress.
*
* @author Michael Eichberg
*/
trait OPALLogger {
/**
* The given message is logged.
*/
def log(message: LogMessage)(implicit ctx: LogContext): Unit
/**
* The given message is only logged once. This is particularly useful if an analysis
* may hit a specific problem multiple times, but it is sufficient/meaningful to log
* it only once.
*
* This method should not be used if the message may only be generated at most
* once. The respective message will be cached in the log context.
*/
final def logOnce(message: LogMessage)(implicit ctx: LogContext): Unit = {
if (ctx.incrementAndGetCount(message) == 1) log(message)
}
}
/**
* OPAL's logging facility.
*
* ==Usage==
* ===Basic===
* E.g., using the global context and the convenience methods.
* {{{
* implicit val logContext : LogContext = org.opalj.log.GlobalContext
* OPALLogger.info("project", "the project is garbage collected")
* }}}
* or
* {{{
* OPALLogger.info("project", "the project is garbage collected")(org.opalj.log.GlobalContext)
* }}}
* ===Advanced===
* Logging a message only once.
* {{{
* implicit val logContext : LogContext = org.opalj.log.GlobalContext
* OPALLogger.logOnce(Warn("project configuration", "the method cannot be resolved"))
* }}}
*
* ==Initialization==
* If the [[GlobalLogContext]] should not use the default [[ConsoleOPALLogger]] then the
* logger can be changed using `updateLogger`.
*
* ==Thread Safety==
* Thread safe.
*
* @author Michael Eichberg
*/
object OPALLogger extends OPALLogger {
@volatile private[this] var loggers: Array[OPALLogger] = new Array(32);
def updateLogger(ctx: LogContext, logger: OPALLogger): Unit = this.synchronized {
val id = ctx.id
assert(id != -1, "context is not yet registered")
assert(id != -2, "context is already unregistered")
loggers(id) = logger
}
/**
* Registers the given context with the OPAL logging facility and associates the
* specified logger with the context.
*/
def register(
ctx: LogContext,
logger: OPALLogger = new ConsoleOPALLogger(true)
): Unit = this.synchronized {
if (ctx.id == -1) {
val id = nextId
if (nextId == loggers.length) {
val newLoggers: Array[OPALLogger] = new Array(loggers.length * 2 + 1)
System.arraycopy(loggers, 0, newLoggers, 0, loggers.length)
loggers = newLoggers
}
loggers(id) = logger
ctx.id = id
nextId += 1
} else if (ctx.id >= 0) {
throw new RuntimeException("reregistration of a log context is not supported")
} else {
throw new RuntimeException("log contexts cannot be reregistered")
}
}
def unregister(ctx: LogContext): Unit = this.synchronized {
if (ctx == GlobalLogContext) {
val message = "unregistering the core global log context is not supported"
throw new IllegalArgumentException(message)
}
val ctxId = ctx.id
// try to reuse log context id if possible
if (ctxId + 1 == nextId) nextId = ctxId
loggers(ctxId) = null
ctx.id = -2
}
def isUnregistered(ctx: LogContext): Boolean = this.synchronized { ctx.id == -2 }
def logger(ctx: LogContext): OPALLogger = {
val ctxId = ctx.id
if (ctxId == -2) {
if (GlobalLogContext eq ctx)
throw new UnknownError("the global log context was unregistered")
else
throw new IllegalArgumentException(s"the log context $ctx is already unregistered")
}
this.synchronized { loggers(ctxId) }
}
def globalLogger(): OPALLogger = this.synchronized { loggers(GlobalLogContext.id) }
// stores the next context id - access must be explicitly synchronized!
private[log] var nextId: Int = 0
// IMPLEMENTATION OF THE LOGGING FACILITIES
def log(message: LogMessage)(implicit ctx: LogContext): Unit = {
logger(ctx).log(message)
}
/**
* Debug message are only included in the code if assertions are turned on. If
* debug message are logged, then they are logged as Info-level messages.
*/
@elidable(ASSERTION)
final def debug(category: String, message: String)(implicit ctx: LogContext): Unit = {
log(Info(category, message))
}
/**
* Debug message are only included in the code if assertions are turned on and the predicate
* `p` evaluates to `true`.
* If debug message are logged, then they are logged as Info-level messages.
*/
@elidable(ASSERTION)
final def debug(
p: ⇒ Boolean,
category: String,
message: ⇒ String
)(
implicit
ctx: LogContext
): Unit = {
if (p) {
log(Info(category, message))
}
}
/**
* Log some general information. General information may be related, e.g., to the overall
* progress of the analysis, the results of an analysis, the major configuration
* settings.
*
* This method is primarily a convenience method that creates an [[Info]] message
* which is the logged.
*
* @note Do not use this method if the analysis may create the same message
* multiple times. In this case use [[logOnce]].
*/
final def info(category: String, message: String)(implicit ctx: LogContext): Unit = {
log(Info(category, message))
}
/**
* Logs a message in the category "`progress`".
*/
final def progress(message: String)(implicit ctx: LogContext): Unit = {
log(Info("progress", message))
}
/**
* Log a warning. Warnings are typically related to incomplete project configurations
* that may affect the overall precision of the analysis, but which are not rendering
* the analysis meaningless.
*
* This method is primarily a convenience method that creates an [[Warn]] message
* which is the logged.
*
* @note Do not use this method if the analysis may create the same message
* multiple times. In this case use [[logOnce]].
*/
final def warn(category: String, message: String)(implicit ctx: LogContext): Unit = {
log(Warn(category, message))
}
/**
* Log an error message. Error message should either be related to internal errors
* or to project configurations that are so badly broken that a meaningful analysis is
* not possible.
*
* This method is primarily a convenience method that creates an [[Error]] message
* which is the logged.
*/
final def error(category: String, message: String)(implicit ctx: LogContext): Unit = {
log(Error(category, message))
}
/**
* Log an error message. Error message should either be related to internal errors
* or to project configurations that are so badly broken that a meaningful analysis is
* not possible.
*
* This method is primarily a convenience method that creates an [[Error]] message
* which is the logged.
*/
final def error(
category: String,
message: String,
t: Throwable
)(
implicit
ctx: LogContext
): Unit = {
log(Error(category, message, t))
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy