add timestamps to logs

This commit is contained in:
pm47 2024-03-20 12:54:41 +01:00
parent f0e21be318
commit 6bffd418e8
No known key found for this signature in database
GPG Key ID: E434ED292E85643A
4 changed files with 77 additions and 20 deletions

View File

@ -169,7 +169,7 @@ class Api(private val nodeParams: NodeParams, private val peer: Peer, private va
try { try {
eventsFlow.collect { sendSerialized(it) } eventsFlow.collect { sendSerialized(it) }
} catch (e: Throwable) { } catch (e: Throwable) {
println("onError ${closeReason.await()}") println("onError ${closeReason.await()?.message}")
} }
} }
} }

View File

@ -9,6 +9,8 @@ import com.github.ajalt.clikt.core.context
import com.github.ajalt.clikt.core.terminal import com.github.ajalt.clikt.core.terminal
import com.github.ajalt.clikt.output.MordantHelpFormatter import com.github.ajalt.clikt.output.MordantHelpFormatter
import com.github.ajalt.clikt.parameters.groups.OptionGroup import com.github.ajalt.clikt.parameters.groups.OptionGroup
import com.github.ajalt.clikt.parameters.groups.default
import com.github.ajalt.clikt.parameters.groups.mutuallyExclusiveOptions
import com.github.ajalt.clikt.parameters.groups.provideDelegate import com.github.ajalt.clikt.parameters.groups.provideDelegate
import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.types.choice import com.github.ajalt.clikt.parameters.types.choice
@ -32,6 +34,8 @@ import fr.acinq.lightning.bin.db.WalletPaymentId
import fr.acinq.lightning.bin.db.payments.LightningOutgoingQueries import fr.acinq.lightning.bin.db.payments.LightningOutgoingQueries
import fr.acinq.lightning.bin.json.ApiType import fr.acinq.lightning.bin.json.ApiType
import fr.acinq.lightning.bin.logs.FileLogWriter import fr.acinq.lightning.bin.logs.FileLogWriter
import fr.acinq.lightning.bin.logs.TimestampFormatter
import fr.acinq.lightning.bin.logs.stringTimestamp
import fr.acinq.lightning.blockchain.mempool.MempoolSpaceClient import fr.acinq.lightning.blockchain.mempool.MempoolSpaceClient
import fr.acinq.lightning.blockchain.mempool.MempoolSpaceWatcher import fr.acinq.lightning.blockchain.mempool.MempoolSpaceWatcher
import fr.acinq.lightning.crypto.LocalKeyManager import fr.acinq.lightning.crypto.LocalKeyManager
@ -83,6 +87,12 @@ class LiquidityOptions : OptionGroup(name = "Liquidity Options") {
.default(100_000.sat) .default(100_000.sat)
} }
sealed class Verbosity {
data object Default : Verbosity()
data object Silent : Verbosity()
data object Verbose : Verbosity()
}
class Phoenixd : CliktCommand() { class Phoenixd : CliktCommand() {
//private val datadir by option("--datadir", help = "Data directory").convert { it.toPath() }.default(homeDirectory / ".phoenix", defaultForHelp = "~/.phoenix") //private val datadir by option("--datadir", help = "Data directory").convert { it.toPath() }.default(homeDirectory / ".phoenix", defaultForHelp = "~/.phoenix")
private val datadir = homeDirectory / ".phoenix" private val datadir = homeDirectory / ".phoenix"
@ -90,7 +100,7 @@ class Phoenixd : CliktCommand() {
private val chain by option("--chain", help = "Bitcoin chain to use").choice( private val chain by option("--chain", help = "Bitcoin chain to use").choice(
"mainnet" to Chain.Mainnet, "testnet" to Chain.Testnet "mainnet" to Chain.Mainnet, "testnet" to Chain.Testnet
).default(Chain.Testnet, defaultForHelp = "testnet") ).default(Chain.Testnet, defaultForHelp = "testnet")
private val customMempoolSpaceHost by option("--mempool-space", "-e", help = "Custom mempool.space instance") private val customMempoolSpaceHost by option("--mempool-space", help = "Custom mempool.space instance")
private val httpBindIp by option("--http-bind-ip", help = "Bind ip for the http api").default("127.0.0.1") private val httpBindIp by option("--http-bind-ip", help = "Bind ip for the http api").default("127.0.0.1")
private val httpBindPort by option("--http-bind-port", help = "Bind port for the http api").int().default(9740) private val httpBindPort by option("--http-bind-port", help = "Bind port for the http api").int().default(9740)
private val httpPassword by option("--http-password", help = "Password for the http api").defaultLazy { private val httpPassword by option("--http-password", help = "Password for the http api").defaultLazy {
@ -108,8 +118,10 @@ class Phoenixd : CliktCommand() {
private val liquidityOptions by LiquidityOptions() private val liquidityOptions by LiquidityOptions()
private val verbose: Boolean by option("--verbose", help = "Verbose mode").flag(default = false) private val verbosity by option(help = "Verbosity level").switch(
private val silent: Boolean by option("--silent", "-s", help = "Silent mode").flag(default = false) "--silent" to Verbosity.Silent,
"--verbose" to Verbosity.Verbose
).default(Verbosity.Default, defaultForHelp = "prints high-level info to the console")
init { init {
context { context {
@ -118,6 +130,14 @@ class Phoenixd : CliktCommand() {
} }
} }
private fun consoleLog(msg: Any, err: Boolean = false) {
if (verbosity == Verbosity.Default) { // in verbose mode we print logs instead
terminal.print(gray(stringTimestamp()), stderr = err)
terminal.print(" ")
terminal.println(msg, stderr = err)
}
}
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
override fun run() { override fun run() {
FileSystem.SYSTEM.createDirectories(datadir) FileSystem.SYSTEM.createDirectories(datadir)
@ -178,9 +198,9 @@ class Phoenixd : CliktCommand() {
} }
} }
echo(cyan("datadir: ${FileSystem.SYSTEM.canonicalize(datadir)}")) consoleLog(cyan("datadir: ${FileSystem.SYSTEM.canonicalize(datadir)}"))
echo(cyan("chain: $chain")) consoleLog(cyan("chain: $chain"))
echo(cyan("autoLiquidity: ${liquidityOptions.autoLiquidity}")) consoleLog(cyan("autoLiquidity: ${liquidityOptions.autoLiquidity}"))
val scope = GlobalScope val scope = GlobalScope
val loggerFactory = LoggerFactory( val loggerFactory = LoggerFactory(
@ -188,7 +208,7 @@ class Phoenixd : CliktCommand() {
// always log to file // always log to file
add(FileLogWriter(datadir / "phoenix.log", scope)) add(FileLogWriter(datadir / "phoenix.log", scope))
// only log to console if verbose mode is enabled // only log to console if verbose mode is enabled
if (verbose) add(CommonWriter()) if (verbosity == Verbosity.Verbose) add(CommonWriter(TimestampFormatter))
}) })
) )
val mempoolSpaceHost = customMempoolSpaceHost ?: when (chain) { val mempoolSpaceHost = customMempoolSpaceHost ?: when (chain) {
@ -209,7 +229,7 @@ class Phoenixd : CliktCommand() {
zeroConfPeers = setOf(lsp.walletParams.trampolineNode.id), zeroConfPeers = setOf(lsp.walletParams.trampolineNode.id),
liquidityPolicy = MutableStateFlow(liquidityPolicy), liquidityPolicy = MutableStateFlow(liquidityPolicy),
) )
echo(cyan("nodeid: ${nodeParams.nodeId}")) consoleLog(cyan("nodeid: ${nodeParams.nodeId}"))
val driver = createAppDbDriver(datadir) val driver = createAppDbDriver(datadir)
val database = PhoenixDatabase( val database = PhoenixDatabase(
@ -268,9 +288,9 @@ class Phoenixd : CliktCommand() {
// drop initial CLOSED event // drop initial CLOSED event
peer.connectionState.dropWhile { it is Connection.CLOSED }.collect { peer.connectionState.dropWhile { it is Connection.CLOSED }.collect {
when (it) { when (it) {
Connection.ESTABLISHING -> echo(yellow("connecting to lightning peer...")) Connection.ESTABLISHING -> consoleLog(yellow("connecting to lightning peer..."))
Connection.ESTABLISHED -> echo(yellow("connected to lightning peer")) Connection.ESTABLISHED -> consoleLog(yellow("connected to lightning peer"))
is Connection.CLOSED -> echo(yellow("disconnected from lightning peer")) is Connection.CLOSED -> consoleLog(yellow("disconnected from lightning peer"))
} }
} }
} }
@ -279,20 +299,20 @@ class Phoenixd : CliktCommand() {
.filterIsInstance<PaymentEvents.PaymentReceived>() .filterIsInstance<PaymentEvents.PaymentReceived>()
.filter { it.amount > 0.msat } .filter { it.amount > 0.msat }
.collect { .collect {
echo("received lightning payment: ${it.amount.truncateToSatoshi()} (${it.receivedWith.joinToString { part -> part::class.simpleName.toString().lowercase() }})") consoleLog("received lightning payment: ${it.amount.truncateToSatoshi()} (${it.receivedWith.joinToString { part -> part::class.simpleName.toString().lowercase() }})")
} }
} }
launch { launch {
nodeParams.nodeEvents nodeParams.nodeEvents
.filterIsInstance<LiquidityEvents.Decision.Rejected>() .filterIsInstance<LiquidityEvents.Decision.Rejected>()
.collect { .collect {
echo(yellow("lightning payment rejected: amount=${it.amount.truncateToSatoshi()} fee=${it.fee.truncateToSatoshi()} maxFee=${liquidityPolicy.maxAbsoluteFee}")) consoleLog(yellow("lightning payment rejected: amount=${it.amount.truncateToSatoshi()} fee=${it.fee.truncateToSatoshi()} maxFee=${liquidityPolicy.maxAbsoluteFee}"))
} }
} }
launch { launch {
nodeParams.feeCredit nodeParams.feeCredit
.drop(1) // we drop the initial value which is 0 sat .drop(1) // we drop the initial value which is 0 sat
.collect { feeCredit -> echo("fee credit: $feeCredit") } .collect { feeCredit -> consoleLog("fee credit: $feeCredit") }
} }
} }
@ -323,23 +343,23 @@ class Phoenixd : CliktCommand() {
server.start(wait = true) server.start(wait = true)
} catch (t: Throwable) { } catch (t: Throwable) {
if (t.cause?.message?.contains("Address already in use") == true) { if (t.cause?.message?.contains("Address already in use") == true) {
echo(t.cause?.message, err = true) consoleLog(t.cause?.message.toString(), err = true)
} else throw t } else throw t
} }
} }
server.environment.monitor.subscribe(ServerReady) { server.environment.monitor.subscribe(ServerReady) {
echo("listening on http://$httpBindIp:$httpBindPort") consoleLog("listening on http://$httpBindIp:$httpBindPort")
} }
server.environment.monitor.subscribe(ApplicationStopPreparing) { server.environment.monitor.subscribe(ApplicationStopPreparing) {
echo(brightYellow("shutting down...")) consoleLog(brightYellow("shutting down..."))
listeners.cancel() listeners.cancel()
peerConnectionLoop.cancel() peerConnectionLoop.cancel()
peer.disconnect() peer.disconnect()
server.stop() server.stop()
exitProcess(0) exitProcess(0)
} }
server.environment.monitor.subscribe(ApplicationStopped) { echo(brightYellow("http server stopped")) } server.environment.monitor.subscribe(ApplicationStopped) { consoleLog(brightYellow("http server stopped")) }
runBlocking { serverJob.join() } runBlocking { serverJob.join() }
} }

View File

@ -9,7 +9,7 @@ import okio.FileSystem
import okio.Path import okio.Path
import okio.buffer import okio.buffer
class FileLogWriter(private val logFile: Path, scope: CoroutineScope, private val messageStringFormatter: MessageStringFormatter = DefaultFormatter) : LogWriter() { class FileLogWriter(private val logFile: Path, scope: CoroutineScope, private val messageStringFormatter: MessageStringFormatter = TimestampFormatter) : LogWriter() {
private val mailbox: Channel<String> = Channel(Channel.BUFFERED) private val mailbox: Channel<String> = Channel(Channel.BUFFERED)
override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) { override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) {

View File

@ -0,0 +1,37 @@
package fr.acinq.lightning.bin.logs
import co.touchlab.kermit.Message
import co.touchlab.kermit.MessageStringFormatter
import co.touchlab.kermit.Severity
import co.touchlab.kermit.Tag
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.format.char
import kotlinx.datetime.toLocalDateTime
object TimestampFormatter : MessageStringFormatter {
override fun formatMessage(severity: Severity?, tag: Tag?, message: Message): String {
val sb = StringBuilder()
sb.append(stringTimestamp())
sb.append(" ")
sb.append(super.formatMessage(severity, tag, message))
return sb.toString()
}
}
private val dateFormat = LocalDateTime.Format {
year()
char('-')
monthNumber()
char('-')
dayOfMonth()
char(' ')
hour()
char(':')
minute()
char(':')
second()
}
fun stringTimestamp() = dateFormat.format(Clock.System.now().toLocalDateTime(TimeZone.UTC))