Skip to content

Commit 51dd03d

Browse files
authored
Merge pull request #713 from synonymdev/claude/issue-655-20260122-1114
feat: save app and ldk logs to same file
2 parents 05f5487 + 8cfc501 commit 51dd03d

3 files changed

Lines changed: 34 additions & 23 deletions

File tree

.github/workflows/claude-code-review.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ jobs:
5656
uses: anthropics/claude-code-action@v1
5757
with:
5858
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
59-
# use_sticky_comment: "true" # doesn't work
59+
# use_sticky_comment: "true" # doesn't work
6060
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
6161
plugins: 'code-review@claude-code-plugins'
6262
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
63+
# Allow Claude bot to trigger this workflow
64+
allowed_bots: 'claude[bot]'
65+
# Allow Claude to use GH CLI for reading external PR details and fetch web content
66+
claude_args: '--allowed-tools Bash(gh:*) WebFetch'

.github/workflows/claude.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ jobs:
3939
uses: anthropics/claude-code-action@v1
4040
with:
4141
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
42+
use_api_for_commits: true
4243

4344
# This is an optional setting that allows Claude to read CI results on PRs
4445
additional_permissions: |

app/src/main/java/to/bitkit/utils/Logger.kt

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import java.io.File
2222
import java.io.FileOutputStream
2323
import java.util.Date
2424
import java.util.Locale
25+
import java.util.concurrent.atomic.AtomicReference
2526
import kotlin.time.measureTime
2627
import org.lightningdevkit.ldknode.LogLevel as LdkLogLevel
2728

@@ -30,13 +31,21 @@ private const val LDK = "LDK"
3031
private const val COMPACT = false
3132

3233
enum 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

3536
val 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

200209
class 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

274282
class 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

Comments
 (0)