tersesystems / echopraxia-scalafix   0.1.5

Website GitHub

Scalafix rules for echopraxia loggers

Scala versions: 2.13 2.12

Scalafix rules for Echopraxia

Echopraxia is a structured logging framework that can organize arguments into key/value fields using Scala's implicit type safe mapping system, without any additional import tax.

These scalafix rules are useful for adding flow loggers to methods, and rewriting logging statements that use string interpolation to use Echopraxia's FieldBuilder API.

Installation

Add scalafix to project/plugins.sbt:

addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.4")

Running

If you want to include the scalafix rules as part of the project to run automatically:

Add echopraxia-scalafix to build.sbt and enable semanticDB:

ThisBuild / scalafixDependencies += "com.tersesystems.echopraxia" %% "scalafix" % VERSION
ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbVersion := scalafixSemanticdb.revision

And then add rules to .scalafix.conf as per configuration:

rules = [
  EchopraxiaRewriteToStructured
]

Most likely you will want to use the sbt integration and do it from inside there, using the external rules:

EchopraxiaRewriteToStructured

This scalafix rule will rewrite statements that use string interpolation to structured arguments.

Running

To run immediately (without build.sbt changes):

scalafixEnable
scalafix dependency:[email protected]:scalafix:$VERSION

Usage

Given a logging statement that uses string interpolation, the EchopraxiaRewriteToStructured rule will rewrite:

private val logger = com.tersesystems.echopraxia.plusscala.LoggerFactory.getLogger

final def someMethod: Unit = {
  val world = "world"
  val count = 2
  logger.info(s"hello $world there are $count args")
}

would be rewritten as:

private val logger = com.tersesystems.echopraxia.plusscala.LoggerFactory.getLogger

final def someMethod: Unit = {
  val world = "world"
  val count = 2
  logger.info("hello {} there are {} args", fb => fb.list(fb.value("world", world), fb.value("count", count)))
}

The rule can work when there is a single exception:

final def someMethod(e: Exception): Unit = {
  val error = "some error text"
  logger.error(s"error $error", e)
}

but will not change the statement when there are other arguments or more than one exception.

You can change the class of the logger as appropriate, if you have a custom logger, and change the method used:

// .scalafix.conf
rules = [
  EchopraxiaRewriteToStructured
]

EchopraxiaRewriteToStructured.loggerClass = com.example.MyLogger
EchopraxiaRewriteToStructured.fieldBuilderMethod=keyValue

EchopraxiaWrapMethodWithLogger

This scalafix rule will wrap methods in a flow logger block, using a flow or trace logger.

Running

To run immediately (without build.sbt changes):

scalafixEnable
scalafix dependency:[email protected]:scalafix:VERSION

Usage

The given method:

object Main {
  private val flowLogger = FlowLoggerFactory.getLogger(getClass)

  def add(first: Int, second: Int): Int = {
    first + second
  }
}

would be rewritten as:

object Main {
  private val flowLogger = FlowLoggerFactory.getLogger(getClass)

  def add(first: Int, second: Int): Int = flowLogger.trace {
    first + second
  }
}

Add the configuration to .scalafix.conf

// .scalafix.conf
rules = [
  EchopraxiaWrapMethodWithLogger
]

# The name of the logger variable to use, i.e. `flowLogger`
EchopraxiaWrapMethodWithLogger.loggerName = flowLogger

# The method call on the logger, i.e. `flowLogger.trace`
EchopraxiaWrapMethodWithLogger.loggerMethod = trace

# The access modifier to use for wrapping methods, by default only public methods are wrapped.
EchopraxiaWrapMethodWithLogger.methodAccess = public