Skip to content

jumale/sdebug

Repository files navigation

Scala Debugging Utils

This library provides tools for debugging Scala code in console: pretty-printing variables, diffs, and some more.
It's my personal toolkit which I use for debugging in my daily work (mostly while writing tests), created as an alternative to breakpoint-debugging in IDE which sometimes does not meet my needs. It's all started with a single pretty-print function (inspired by this gist) and eventually expanded into a set of functions.

This library is not published yet anywhere, I build it locally from this repository and then enable it via global SBT configs so that I can use it in any project. It also provides a nice side effect - I can't forget removing my debugging code, otherwise CI would not be able to compile the project, since there is no such library.

Installation

  • clone this project locally
  • go to the root folder and run sbt +publishLocal (or make build)
  • create if not exists ~/.sbt/1.0/global.sbt (or ~/.sbt/0.13/global.sbt for older SBT)
  • install one of the versions:
    • an extended version with PlayJson and Scalatest support:
      libraryDependencies ++= Seq(
        "com.github.jumale" %% "sdebug-impl-ext"  % "0.4.0-SNAPSHOT"
      )
    • or a base version without any dependencies:
      libraryDependencies ++= Seq(
        "com.github.jumale" %% "sdebug-impl"  % "0.4.0-SNAPSHOT"
      )
  • restart your SBT console

Note: the extended version includes an extended printer for JsValue from PlayJson and provides an implementation of org.scalactic.Prettifier which can be optionally imported in Scalatest test-classes to make test-failures more readable.

Debugging helpers

You can also find some examples in the DebuggerExamplesTest.scala.


Print a simple log message:

sdebug.log(s"lorem ipsum")

log


Pretty-print variables:

sdebug.print(swagger)
// also can print multiple values: 
sdebug.print(foo, bar, baz)
// also can print multiple named values: 
sdebug.print(
  "foo" -> foo, 
  "bar" -> bar, 
  "baz" -> baz,
)

The printed result is a valid Scala code, so it can be copy-pasted back to IDE if needed:
log


Exceptions are printed with a stack-trace:

sdebug.print(exception)

log
The stack-trace length is limited to 10 by default, but it can be changed via setter sdebug.setErrorTraceLimit(20).


Print diffs between two values:

sdebug.diff(left, right)

log
The diff is calculated recursively, and it shows precisely which key or value has been changed/added/deleted. The sdebug-impl-ext also resolves diffs for any JsValue from Play JSON library.


Print a stack-trace to the current line:

sdebug.trace(limit = 10)

log


A wrapper for Thread.sleep, which also prints breadcrumbs, so that it can't be accidentally forgotten in code:

sdebug.sleep(200)

log


Print results in a table format:

sdebug.table(
  Seq("Name", "Value"),
  Seq("string", "foo"),
  Seq("tuple", (42, Left(None))),
  Seq("bool", false),
  Seq("list of case classes", List(CellVal("a", 1), CellVal("b", 2), CellVal("c", 3), CellVal("d", 4)))
)

log


Measure execution time of the provided code-block:

sdebug.measure(myFunction())

// also supports custom name:
sdebug.measure("customName")(myFunction())

log


Intercept and print values:

// Import this extension into the context
import sdebug.DebuggedValueOps
// Now we can intercept and print any values

val foo = "bar".sdebug // prints "bar" and returns it back

def doSomething() = 
  doThis()
    .sdebug // prints result of doThis() and returns it back
    .doThat()

Importing a scalatest formatter (available only in sdebug-impl-ext):

class MyTest extends AnyWordSpec {
  import sdebug.scalacticPrettifierAnalyse
  // now failed tests will automatically print errors in the sdebug format
}

Or an alternative version which does not print analysis:

class MyTest extends AnyWordSpec {
  import sdebug.scalacticPrettifier
}

Additional helpers

Format a value, but instead of printing save it into a file:

// 
sdebug.formatAndSave("filename.txt")(value)

// also supports multiple values:
sdebug.formatAndSave("filename.txt")(foo, bar, baz)

The default file location is ./target (i.e. the file from the example will be saved to ./target/filename.txt), and it's only applicable to relative paths/file-names, while absolute paths will be handled as is. The default folder also can be changed when creating a new instance of Debugger (not supported in sdebug-impl-ext).


Simply read/write file contents:

sdebug.save("filename.txt")("contents")
val contents = sdebug.read("filename.txt")

Read JSON files (only in sdebug-impl-ext):

val data = sdebug.readJson[MyData]("filename.json")

The debug-printer can be toggled off until the next toggle-on. This is useful when you have multiple calls of the same debugged code, but you want to see the output of one specific call:

sdebug.off()
// some code
sdebug.on()

Enable timestamps in prints

sdebug.setShowTime(true)

Configuration in runtime


Disable colors

sdebug.setColorize(false)

Configure how class names are printed:

sdebug.configureClassNames(
  showFull = true, // true results in 'class.getName', false results in 'class.getSimpleName'
  replace = Seq(
    // regex-match and replace substings in printed class names
    "^\\w+".r -> "customPrefix"
  )
)

Fully replace printed types with aliases

sdebug.aliases(
  "ClassA" -> "ClassB",
  "ClassC" -> "ClassD",
)

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages