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.
- clone this project locally
- go to the root folder and run
sbt +publishLocal
(ormake 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" )
- an extended version with PlayJson and Scalatest support:
- 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.
You can also find some examples in the DebuggerExamplesTest.scala.
Print a simple log message:
sdebug.log(s"lorem ipsum")
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:
Exceptions are printed with a stack-trace:
sdebug.print(exception)
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)
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)
A wrapper for Thread.sleep
, which also prints breadcrumbs, so that it can't be accidentally forgotten in code:
sdebug.sleep(200)
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)))
)
Measure execution time of the provided code-block:
sdebug.measure(myFunction())
// also supports custom name:
sdebug.measure("customName")(myFunction())
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
}
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)
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",
)