@@ -22,6 +22,7 @@ import java.io.File
2222import java.io.FileOutputStream
2323import java.util.Date
2424import java.util.Locale
25+ import java.util.concurrent.atomic.AtomicReference
2526import kotlin.time.measureTime
2627import org.lightningdevkit.ldknode.LogLevel as LdkLogLevel
2728
@@ -30,13 +31,21 @@ private const val LDK = "LDK"
3031private const val COMPACT = false
3132
3233enum class LogSource { Ldk , Bitkit , Unknown }
33- enum class LogLevel { PERF , VERBOSE , GOSSIP , TRACE , DEBUG , INFO , WARN , ERROR ; }
34+ enum class LogLevel { PERF , VERBOSE , GOSSIP , TRACE , DEBUG , INFO , WARN , ERROR }
3435
3536val Logger = AppLogger ()
3637
37- class AppLogger ( private val source : LogSource = LogSource . Bitkit ) {
38+ class AppLogger {
3839 companion object {
3940 private const val TAG = " Logger"
41+ private val saverRef = AtomicReference <LogSaver ?>(null )
42+
43+ fun getOrCreateSaver (): LogSaver = saverRef.get() ? : synchronized(this ) {
44+ saverRef.get() ? : run {
45+ val sessionPath = runCatching { buildSessionLogFilePath() }.getOrElse { " " }
46+ LogSaverImpl (sessionPath).also { saverRef.set(it) }
47+ }
48+ }
4049 }
4150
4251 private var delegate: LoggerImpl ? = null
@@ -45,15 +54,15 @@ class AppLogger(private val source: LogSource = LogSource.Bitkit) {
4554 delegate = runCatching { createDelegate() }.getOrNull()
4655 }
4756
48- private fun createDelegate (): LoggerImpl {
49- val sessionPath = runCatching { buildSessionLogFilePath(source) }.getOrElse { " " }
50- return LoggerImpl (APP , LogSaverImpl (source, sessionPath))
51- }
57+ private fun createDelegate () = LoggerImpl (APP , getOrCreateSaver())
5258
5359 fun reset () {
5460 warn(" Wiping entire logs directory…" , context = TAG )
55- runCatching { Env .logDir.deleteRecursively() }
56- delegate = runCatching { createDelegate() }.getOrNull()
61+ synchronized(AppLogger ) {
62+ runCatching { Env .logDir.deleteRecursively() }
63+ saverRef.set(null )
64+ delegate = runCatching { createDelegate() }.getOrNull()
65+ }
5766 }
5867
5968 fun info (
@@ -114,7 +123,7 @@ class AppLogger(private val source: LogSource = LogSource.Bitkit) {
114123 }
115124}
116125
117- class LoggerImpl (
126+ private class LoggerImpl (
118127 private val tag : String = APP ,
119128 private val saver : LogSaver ,
120129 private val compact : Boolean = COMPACT ,
@@ -198,7 +207,6 @@ interface LogSaver {
198207}
199208
200209class LogSaverImpl (
201- source : LogSource ,
202210 private val sessionFilePath : String ,
203211) : LogSaver {
204212 private val queue: CoroutineScope by lazy {
@@ -207,7 +215,7 @@ class LogSaverImpl(
207215
208216 init {
209217 if (sessionFilePath.isNotEmpty()) {
210- log(" Log session for ' ${source.name} ' initialized with file path: '$sessionFilePath '" )
218+ log(" Log session initialized with file path: '$sessionFilePath '" )
211219
212220 // Clean all old log files in background
213221 CoroutineScope (Dispatchers .IO ).launch {
@@ -221,8 +229,9 @@ class LogSaverImpl(
221229
222230 queue.launch {
223231 runCatching {
232+ val sanitized = message.replace(" \n " , " " )
224233 FileOutputStream (File (sessionFilePath), true ).use { stream ->
225- stream.write(" $message \n " .toByteArray())
234+ stream.write(" $sanitized \n " .toByteArray())
226235 }
227236 }.onFailure {
228237 Log .e(APP , " Error writing to log file: '$sessionFilePath '" , it)
@@ -242,22 +251,21 @@ class LogSaverImpl(
242251
243252 val logFiles = logDir
244253 .listFiles { file -> file.extension == " log" }
245- ?.map { file -> Triple (file, file.length(), file.lastModified()) }
246254 ? : return
247255
248- var totalSize = logFiles.sumOf { it.second }
256+ var totalSize = logFiles.sumOf { it.length() }
249257 val maxSizeBytes = maxTotalSizeMB * 1024L * 1024L
250258
251259 // Sort by creation date (oldest first)
252260 logFiles
253- .sortedBy { it.third }
254- .forEach { ( file, size, _) ->
261+ .sortedBy { it.lastModified() }
262+ .forEach { file ->
255263 if (totalSize <= maxSizeBytes) return
256264
257265 runCatching {
258266 Log .d(APP , " Deleting old log file: '${file.name} '" )
259267 if (file.delete()) {
260- totalSize - = size
268+ totalSize - = file.length()
261269 }
262270 }.onFailure {
263271 Log .w(APP , " Failed to delete old log file: '${file.name} '" , it)
@@ -273,8 +281,7 @@ class LogSaverImpl(
273281
274282class LdkLogWriter (
275283 private val maxLogLevel : LdkLogLevel = Env .ldkLogLevel,
276- private val source : LogSource = LogSource .Ldk ,
277- saver : LogSaver = LogSaverImpl (source, buildSessionLogFilePath(source)),
284+ saver : LogSaver = AppLogger .getOrCreateSaver(),
278285) : LogWriter {
279286 private val delegate: LoggerImpl = LoggerImpl (LDK , saver)
280287
@@ -296,11 +303,10 @@ class LdkLogWriter(
296303 }
297304}
298305
299- private fun buildSessionLogFilePath (source : LogSource ): String {
306+ private fun buildSessionLogFilePath (): String {
300307 val logDir = Env .logDir
301- val sourceName = source.name.lowercase()
302308 val timestamp = utcDateFormatterOf(DatePattern .LOG_FILE ).format(Date ())
303- val path = logDir.resolve(" ${sourceName} _ $timestamp .log" ).path
309+ val path = logDir.resolve(" bitkit_ $timestamp .log" ).path
304310 return path
305311}
306312
0 commit comments