Skip to content

Commit

Permalink
Logging improvements
Browse files Browse the repository at this point in the history
* Add NullLogger for do-nothing logging
* Make sure each logger has a PlatformContext
* Add documentation to classes and methods
  • Loading branch information
reidspencer committed Jan 13, 2025
1 parent 134d3f1 commit ef4ff06
Showing 1 changed file with 52 additions and 17 deletions.
69 changes: 52 additions & 17 deletions utils/shared/src/main/scala/com/ossuminc/riddl/utils/Logging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@

package com.ossuminc.riddl.utils

import com.ossuminc.riddl.utils.StringHelpers.*
import com.ossuminc.riddl.utils.StringHelpers.*

import scala.annotation.unused
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.io.AnsiColor.*
import scala.scalajs.js.annotation._

/** Companion class to Logging trait. Provides the definitions of logging levels via the Lvl trait.
*/
@JSExportTopLevel("Logger")
object Logging {
sealed trait Lvl {
override def toString: String =
override def toString: String =
this.getClass.getSimpleName.toLowerCase.dropRightWhile(_ == '$')
}

Expand All @@ -29,13 +31,18 @@ object Logging {
case object Missing extends Lvl
case object Info extends Lvl

val nl: String = "\n"
}

/** Base trait for all styles of loggers. Logger requires the "write" method be implemented which
* provides a style-specific way to handle the logging of a message.
*/
trait Logger(using pc: PlatformContext) {
import Logging.*

/** Syntactic sugar for write(Severe, s) */
final def severe(s: => String): Unit = { write(Severe, s) }

/** Syntactic sugar for write(Severe, <message-derived-from-exception>) */
final def severe(s: => String, xcptn: Throwable): Unit = {
val message =
s"""$s: $xcptn
Expand All @@ -44,14 +51,22 @@ trait Logger(using pc: PlatformContext) {
write(Severe, message)
}

/** Syntactic sugar for write(Error, s) */
final def error(s: => String): Unit = { write(Error, s) }

/** Syntactic sugar for write(Warning, s) */
final def warn(s: => String): Unit = { write(Warning, s) }

/** Syntactic sugar for write(Usage, s) */
final def usage(s: => String): Unit = { write(Usage, s) }

/** Syntactic sugar for write(Style, s) */
final def style(s: => String): Unit = { write(Style, s) }

/** Syntactic sugar for write(Missing, s) */
final def missing(s: => String): Unit = { write(Missing, s) }

/** Syntactic sugar for write(Info, s) */
final def info(s: => String): Unit = { write(Info, s) }

private var nSevere = 0
Expand All @@ -74,10 +89,10 @@ trait Logger(using pc: PlatformContext) {
case Logging.Missing => s"$GREEN"
case Logging.Info => s"$BLUE"
}
val lines = s.split(nl)
val lines = s.split(pc.newline)
val head = s"$prefix$BOLD[$level] ${lines.head}$RESET"
val tail = lines.tail.mkString(nl)
if tail.nonEmpty then head + s"$nl$prefix$tail$RESET"
val tail = lines.tail.mkString(pc.newline)
if tail.nonEmpty then head + s"${pc.newline}$prefix$tail$RESET"
else head
end if
}
Expand All @@ -96,6 +111,7 @@ trait Logger(using pc: PlatformContext) {
}
}

/** Generate a summary on the number of messages logged for each severity Lvl. */
def summary: String = {
s"""Severe Errors: $nSevere
|Normal Errors: $nError
Expand All @@ -108,15 +124,22 @@ trait Logger(using pc: PlatformContext) {
}
}

case class SysLogger()(using io: PlatformContext) extends Logger {
/** A logger that writes to "stdout" per the PlatformContext */
case class SysLogger()(using pc: PlatformContext) extends Logger:
override def write(level: Logging.Lvl, s: String): Unit = {
super.count(level)
io.stdoutln(highlight(level, s))
pc.stdoutln(highlight(level, s))
}
}

@JSExportTopLevel("StringLogger")
case class StringLogger(capacity: Int = 512 * 2)(using io: PlatformContext = pc) extends Logger {
end SysLogger

/** A logger that doesn't do I/O but collects the messages in a buffer
*
* @param capacity
* The initial capacity of the buffer, defaults to 1K
* @param pc
* The PlatformContext to use
*/
case class StringLogger(capacity: Int = 512 * 2)(using pc: PlatformContext) extends Logger:
private val stringBuilder = new mutable.StringBuilder(capacity)

override def write(level: Logging.Lvl, s: String): Unit = {
Expand All @@ -125,12 +148,24 @@ case class StringLogger(capacity: Int = 512 * 2)(using io: PlatformContext = pc)
}

override def toString: String = stringBuilder.toString()
}

@JSExportTopLevel("CallBackLogger")
case class CallBackLogger(callback: (Logging.Lvl, String) => Unit) extends Logger {
end StringLogger

/** A logger that delegates to a callback function.
*
* @param callback
* The function to call when a messages needs logging
*/
case class CallBackLogger(callback: (Logging.Lvl, String) => Unit)(using pc: PlatformContext)
extends Logger:
override def write(level: Logging.Lvl, s: String): Unit = {
super.count(level)
callback(level, s)
}
}
end CallBackLogger

/** A logger to use when logging of messages is not desired. It will still count the number of
* messages for each severity Lvl.
*/
case class NullLogger()(using pc: PlatformContext) extends Logger:
override def write(level: Logging.Lvl, s: String): Unit = super.count(level)
end NullLogger

0 comments on commit ef4ff06

Please sign in to comment.