2019-10-23 22:21:52 -07:00
|
|
|
package cash.z.wallet.sdk.ext
|
2019-01-23 02:45:26 -08:00
|
|
|
|
2019-06-14 16:15:44 -07:00
|
|
|
import java.util.concurrent.CopyOnWriteArraySet
|
2019-02-26 14:36:08 -08:00
|
|
|
|
2019-02-19 05:23:48 -08:00
|
|
|
internal typealias Leaf = String
|
2019-01-23 02:45:26 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A tiny log.
|
|
|
|
*/
|
|
|
|
interface Twig {
|
|
|
|
fun twig(logMessage: String = "")
|
2019-02-19 05:23:48 -08:00
|
|
|
operator fun plus(twig: Twig): Twig {
|
|
|
|
// if the other twig is a composite twig, let it handle the addition
|
|
|
|
return if(twig is CompositeTwig) twig.plus(this) else CompositeTwig(mutableListOf(this, twig))
|
|
|
|
}
|
|
|
|
companion object {
|
|
|
|
/**
|
|
|
|
* Plants the twig, making it the one and only bush. Twigs can be bundled together to create the appearance of
|
|
|
|
* multiple bushes (i.e `Twig.plant(twigA + twigB + twigC)`) even though there's only ever one bush.
|
|
|
|
*/
|
|
|
|
fun plant(rootTwig: Twig) {
|
|
|
|
Bush.trunk = rootTwig
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a leaf on the bush. Leaves show up in every log message as tags until they are clipped.
|
|
|
|
*/
|
|
|
|
fun sprout(leaf: Leaf) = Bush.leaves.add(leaf)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clip a leaf from the bush. Clipped leaves no longer appear in logs.
|
|
|
|
*/
|
|
|
|
fun clip(leaf: Leaf) = Bush.leaves.remove(leaf)
|
2019-06-14 16:15:44 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Clip all leaves from the bush.
|
|
|
|
*/
|
|
|
|
fun prune() = Bush.leaves.clear()
|
2019-02-19 05:23:48 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A collection of tiny logs (twigs) consisting of one trunk and maybe some leaves. There can only ever be one trunk.
|
|
|
|
* Trunks are created by planting a twig. Whenever a leaf sprouts, it will appear as a tag on every log message
|
|
|
|
* until clipped.
|
|
|
|
*
|
|
|
|
* @see [Twig.plant]
|
|
|
|
* @see [Twig.sprout]
|
|
|
|
* @see [Twig.clip]
|
|
|
|
*/
|
|
|
|
object Bush {
|
|
|
|
var trunk: Twig = SilentTwig()
|
2019-06-14 16:15:44 -07:00
|
|
|
val leaves: MutableSet<Leaf> = CopyOnWriteArraySet<Leaf>()
|
2019-01-23 02:45:26 -08:00
|
|
|
}
|
|
|
|
|
2019-02-19 05:23:48 -08:00
|
|
|
/**
|
|
|
|
* Makes a tiny log.
|
|
|
|
*/
|
|
|
|
inline fun twig(message: String) = Bush.trunk.twig(message)
|
|
|
|
|
2019-02-26 14:36:08 -08:00
|
|
|
/**
|
2019-06-14 16:15:44 -07:00
|
|
|
* Times a tiny log.
|
|
|
|
*/
|
|
|
|
inline fun <R> twig(logMessage: String, block: () -> R): R = Bush.trunk.twig(logMessage, block)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Meticulously times a tiny task.
|
2019-02-26 14:36:08 -08:00
|
|
|
*/
|
|
|
|
inline fun <R> twigTask(logMessage: String, block: () -> R): R = Bush.trunk.twigTask(logMessage, block)
|
|
|
|
|
2019-01-23 02:45:26 -08:00
|
|
|
/**
|
|
|
|
* A tiny log that does nothing. No one hears this twig fall in the woods.
|
|
|
|
*/
|
|
|
|
class SilentTwig : Twig {
|
|
|
|
override fun twig(logMessage: String) {
|
|
|
|
// shh
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A tiny log for detecting troubles. Aim at your troubles and pull the twigger.
|
|
|
|
*
|
|
|
|
* @param formatter a formatter for the twigs. The default one is pretty spiffy.
|
|
|
|
* @param printer a printer for the twigs. The default is System.err.println.
|
|
|
|
*/
|
|
|
|
open class TroubleshootingTwig(
|
|
|
|
val formatter: (String) -> String = spiffy(5),
|
|
|
|
val printer: (String) -> Any = System.err::println
|
|
|
|
) : Twig {
|
|
|
|
override fun twig(logMessage: String) {
|
|
|
|
printer(formatter(logMessage))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-19 05:23:48 -08:00
|
|
|
/**
|
|
|
|
* Since there can only ever be one trunk on the bush of twigs, this class lets
|
|
|
|
* you cheat and make that trunk be a bundle of twigs.
|
|
|
|
*/
|
2019-10-23 22:21:52 -07:00
|
|
|
open class CompositeTwig(private val twigBundle: MutableList<Twig>) :
|
|
|
|
Twig {
|
2019-02-19 05:23:48 -08:00
|
|
|
override operator fun plus(twig: Twig): Twig {
|
|
|
|
if (twig is CompositeTwig) twigBundle.addAll(twig.twigBundle) else twigBundle.add(twig); return this
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun twig(logMessage: String) {
|
|
|
|
for (twig in twigBundle) {
|
|
|
|
twig.twig(logMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 16:15:44 -07:00
|
|
|
/**
|
|
|
|
* Times a tiny log. Execute the block of code on the clock.
|
|
|
|
*/
|
|
|
|
inline fun <R> Twig.twig(logMessage: String, block: () -> R): R {
|
|
|
|
val start = System.currentTimeMillis()
|
|
|
|
val result = block()
|
|
|
|
val elapsed = (System.currentTimeMillis() - start)
|
|
|
|
twig("$logMessage | ${elapsed}ms")
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2019-01-23 02:45:26 -08:00
|
|
|
/**
|
2019-02-26 14:36:08 -08:00
|
|
|
* A tiny log task. Execute the block of code with some twigging around the outside. For silent twigs, this adds a small
|
|
|
|
* amount of overhead at the call site but still avoids logging.
|
|
|
|
*
|
|
|
|
* note: being an extension function (i.e. static rather than a member of the Twig interface) allows this function to be
|
|
|
|
* inlined and simplifies its use with suspend functions
|
|
|
|
* (otherwise the function and its "block" param would have to suspend)
|
2019-01-23 02:45:26 -08:00
|
|
|
*/
|
2019-02-26 14:36:08 -08:00
|
|
|
inline fun <R> Twig.twigTask(logMessage: String, block: () -> R): R {
|
2019-01-23 02:45:26 -08:00
|
|
|
twig("$logMessage - started | on thread ${Thread.currentThread().name})")
|
2019-06-14 16:15:44 -07:00
|
|
|
val start = System.nanoTime()
|
2019-02-26 14:36:08 -08:00
|
|
|
val result = block()
|
2019-06-14 16:15:44 -07:00
|
|
|
val elapsed = ((System.nanoTime() - start)/1e6)
|
|
|
|
twig("$logMessage - completed | in $elapsed ms" +
|
2019-02-26 14:36:08 -08:00
|
|
|
" on thread ${Thread.currentThread().name}")
|
|
|
|
return result
|
2019-01-23 02:45:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A tiny log formatter that makes twigs pretty spiffy.
|
|
|
|
*
|
|
|
|
* @param stackFrame the stack frame from which we try to derive the class. This can vary depending on how the code is
|
|
|
|
* called so we expose it for flexibility. Jiggle the handle on this whenever the line numbers appear incorrect.
|
|
|
|
*/
|
|
|
|
inline fun spiffy(stackFrame: Int = 4, tag: String = "@TWIG"): (String) -> String = { logMessage: String ->
|
|
|
|
val stack = Thread.currentThread().stackTrace[stackFrame]
|
|
|
|
val time = String.format("${tag} %1\$tD %1\$tI:%1\$tM:%1\$tS.%1\$tN", System.currentTimeMillis())
|
|
|
|
val className = stack.className.split(".").lastOrNull()?.split("\$")?.firstOrNull()
|
2019-02-19 05:23:48 -08:00
|
|
|
val tags = Bush.leaves.joinToString(" #", "#")
|
|
|
|
"$time[$className:${stack.lineNumber}]($tags) $logMessage"
|
2019-01-23 02:45:26 -08:00
|
|
|
}
|