From be5e33a989679a1ecadd2d52ee8e6562bcb03055 Mon Sep 17 00:00:00 2001 From: Evan Kaloudis Date: Fri, 27 Feb 2026 01:29:08 -0500 Subject: [PATCH] feat: add real-time streaming LDK Node log viewer --- .gitignore | 1 + App.tsx | 5 + .../java/app/zeusln/zeus/LdkNodeModule.kt | 33 ++++++ .../ldknode/LogFileObserver.kt | 73 ++++++++++++ fetch-libraries.sh | 4 +- ios/LdkNodeMobile/LdkNodeModule.m | 10 ++ ios/LdkNodeMobile/LdkNodeModule.swift | 78 ++++++++++++- ios/LdkNodeMobile/LogFileObserver.swift | 71 ++++++++++++ ios/zeus.xcodeproj/project.pbxproj | 4 + ldknode/LdkNode.d.ts | 19 ++++ locales/en.json | 1 + utils/EventListenerUtils.ts | 6 + views/Settings/EmbeddedNode/LDKLogs.tsx | 104 ++++++++++++++++++ views/Settings/EmbeddedNode/LNDLogs.tsx | 3 + .../Settings/EmbeddedNode/RapidGossipSync.tsx | 4 +- views/Settings/EmbeddedNode/VssServer.tsx | 4 +- views/Settings/EmbeddedNode/index.tsx | 27 +++++ 17 files changed, 440 insertions(+), 7 deletions(-) create mode 100644 android/app/src/main/java/org/lightningdevkit/ldknode/LogFileObserver.kt create mode 100644 ios/LdkNodeMobile/LogFileObserver.swift create mode 100644 views/Settings/EmbeddedNode/LDKLogs.tsx diff --git a/.gitignore b/.gitignore index 0916f9fe9..35cb3cd20 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ android/local.properties # OSX # .DS_Store +__MACOSX/ # Xcode # diff --git a/App.tsx b/App.tsx index a0b2708e2..166a8148d 100644 --- a/App.tsx +++ b/App.tsx @@ -168,6 +168,7 @@ import RapidGossipSync from './views/Settings/EmbeddedNode/RapidGossipSync'; import EsploraServer from './views/Settings/EmbeddedNode/EsploraServer'; import VssServer from './views/Settings/EmbeddedNode/VssServer'; import LNDLogs from './views/Settings/EmbeddedNode/LNDLogs'; +import LDKLogs from './views/Settings/EmbeddedNode/LDKLogs'; import Peers from './views/Settings/EmbeddedNode/Peers'; import NeutrinoPeers from './views/Settings/EmbeddedNode/Peers/NeutrinoPeers'; import ZeroConfPeers from './views/Settings/EmbeddedNode/Peers/ZeroConfPeers'; @@ -1058,6 +1059,10 @@ export default class App extends React.PureComponent { name="LNDLogs" // @ts-ignore:next-line component={LNDLogs} /> + + reactApplicationContext + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + .emit("ldklog", line + "\n") + } + logFileObserver?.startObserving() + promise.resolve(true) + } } diff --git a/android/app/src/main/java/org/lightningdevkit/ldknode/LogFileObserver.kt b/android/app/src/main/java/org/lightningdevkit/ldknode/LogFileObserver.kt new file mode 100644 index 000000000..fec1b8c41 --- /dev/null +++ b/android/app/src/main/java/org/lightningdevkit/ldknode/LogFileObserver.kt @@ -0,0 +1,73 @@ +package org.lightningdevkit.ldknode + +import android.os.FileObserver +import android.util.Log +import java.io.* + +class LogFileObserver( + private val filePath: String, + private val onNewLine: (String) -> Unit +) { + private var fileObserver: FileObserver? = null + private var reader: BufferedReader? = null + + fun startObserving() { + // Open file, create if needed + val file = File(filePath) + file.parentFile?.mkdirs() + if (!file.exists()) file.createNewFile() + + val stream = FileInputStream(file) + reader = BufferedReader(InputStreamReader(stream)) + + // Read to EOF without emitting (skip historical) + readToEnd(emit = false) + + // Watch for MODIFY events + fileObserver = object : FileObserver(filePath) { + override fun onEvent(event: Int, path: String?) { + if (event != MODIFY) return + readToEnd(emit = true) + } + } + fileObserver?.startWatching() + } + + fun stopObserving() { + fileObserver?.stopWatching() + fileObserver = null + reader?.close() + reader = null + } + + private fun readToEnd(emit: Boolean) { + try { + var line = reader?.readLine() + while (line != null) { + if (emit) onNewLine(line) + line = reader?.readLine() + } + } catch (e: IOException) { + Log.e("LogFileObserver", "Error reading from log file", e) + } + } + + companion object { + fun tailFile(filePath: String, numLines: Int): String { + val file = File(filePath) + if (!file.exists() || file.length() == 0L) return "" + + // Read last chunk (256KB should be plenty for typical numLines) + val chunkSize = minOf(262144L, file.length()) + val raf = RandomAccessFile(file, "r") + raf.use { + it.seek(file.length() - chunkSize) + val bytes = ByteArray(chunkSize.toInt()) + it.readFully(bytes) + val content = String(bytes, Charsets.UTF_8) + val lines = content.split("\n") + return lines.takeLast(numLines).joinToString("\n") + } + } + } +} diff --git a/fetch-libraries.sh b/fetch-libraries.sh index e2c525ddf..03a64ff55 100644 --- a/fetch-libraries.sh +++ b/fetch-libraries.sh @@ -12,9 +12,9 @@ ANDROID_LINK=$FILE_PATH$ANDROID_FILE IOS_LINK=$FILE_PATH$IOS_FILE.zip # LDK Node -LDK_NODE_VERSION=v0.7.0-zeus-lsps7-rgs-troubleshoot +LDK_NODE_VERSION=v0.7.0-zeus-lsps7-rgs-troubleshoot-logobserver LDK_NODE_IOS_FILE=LDKNodeFFI.xcframework -LDK_NODE_IOS_SHA256='a99cf0cfd623737e588c48ae8f97bab4c71b18ce9593bb60a4a7cabcaf0d9026' +LDK_NODE_IOS_SHA256='75b031ebb05d186f0ffb09c336097a6f7b30205cb26596551b054bc07ea67907' LDK_NODE_IOS_LINK=https://github.com/ZeusLN/ldk-node/releases/download/$LDK_NODE_VERSION/$LDK_NODE_IOS_FILE.zip # test that curl and unzip are installed diff --git a/ios/LdkNodeMobile/LdkNodeModule.m b/ios/LdkNodeMobile/LdkNodeModule.m index a0f9a457e..d0edc85f8 100644 --- a/ios/LdkNodeMobile/LdkNodeModule.m +++ b/ios/LdkNodeMobile/LdkNodeModule.m @@ -78,6 +78,16 @@ RCT_EXTERN_METHOD(eventHandled:(RCTPromiseResolveBlock)resolve rejecter:(RCTProm RCT_EXTERN_METHOD(lsps1RequestChannel:(double)lspBalanceSat clientBalanceSat:(double)clientBalanceSat channelExpiryBlocks:(double)channelExpiryBlocks announceChannel:(BOOL)announceChannel resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(lsps1CheckOrderStatus:(NSString *)orderId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +// LSPS7 Methods +RCT_EXTERN_METHOD(setLiquiditySourceLsps7:(NSString *)nodeId address:(NSString *)address token:(NSString *)token resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(lsps7GetExtendableChannels:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(lsps7CreateOrder:(NSString *)shortChannelId channelExtensionExpiryBlocks:(double)channelExtensionExpiryBlocks token:(NSString *)token refundOnchainAddress:(NSString *)refundOnchainAddress resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(lsps7CheckOrderStatus:(NSString *)orderId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) + +// Log File Methods +RCT_EXTERN_METHOD(tailLdkNodeLog:(nonnull NSNumber *)numLines resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(observeLdkNodeLogFile:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) + // Message Signing Methods RCT_EXTERN_METHOD(signMessage:(NSString *)message resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(verifySignature:(NSString *)message signature:(NSString *)signature publicKey:(NSString *)publicKey resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) diff --git a/ios/LdkNodeMobile/LdkNodeModule.swift b/ios/LdkNodeMobile/LdkNodeModule.swift index 7b577e48c..89309d078 100644 --- a/ios/LdkNodeMobile/LdkNodeModule.swift +++ b/ios/LdkNodeMobile/LdkNodeModule.swift @@ -6,6 +6,7 @@ class LdkNodeModule: RCTEventEmitter { private var node: Node? private var builder: Builder? + private var logFileObserver: LogFileObserver? // Stored config values for building with custom Config private var storedNetwork: Network = .bitcoin @@ -37,7 +38,7 @@ class LdkNodeModule: RCTEventEmitter { } override func supportedEvents() -> [String]! { - return ["LdkNodeEvent"] + return ["LdkNodeEvent", "ldklog"] } @objc @@ -381,6 +382,11 @@ class LdkNodeModule: RCTEventEmitter { if let lsps7NodeId = self.storedLsps7NodeId, let lsps7Address = self.storedLsps7Address { builder.setLiquiditySourceLsps7(nodeId: lsps7NodeId, address: lsps7Address, token: self.storedLsps7Token) } + + if !self.storedStorageDirPath.isEmpty { + NSLog("LdkNodeModule: applyBuilderSettings: Enabling filesystem logger") + builder.setFilesystemLogger(logFilePath: "\(self.storedStorageDirPath)/ldk_node.log", maxLogLevel: .debug) + } } private func createEsploraSyncConfig() -> EsploraSyncConfig { @@ -420,6 +426,8 @@ class LdkNodeModule: RCTEventEmitter { do { try node.stop() + self.logFileObserver?.stopObserving() + self.logFileObserver = nil self.node = nil resolve(["status": "ok"]) } catch { @@ -1490,4 +1498,72 @@ class LdkNodeModule: RCTEventEmitter { return "refunded" } } + + // MARK: - LSPS7 Serialization Helpers + + private func serializeLsps7OrderResponse(_ response: Lsps7OrderResponse) -> [String: Any] { + var result: [String: Any] = [ + "orderId": response.orderId, + "orderState": serializeLsps7OrderState(response.orderState), + "channelExtensionExpiryBlocks": response.channelExtensionExpiryBlocks, + "newChannelExpiryBlock": response.newChannelExpiryBlock, + "paymentInfo": serializeLsps1PaymentInfo(response.payment), + "channel": serializeLsps7ExtendableChannel(response.channel) + ] + return result + } + + private func serializeLsps7ExtendableChannel(_ channel: Lsps7ExtendableChannel) -> [String: Any] { + var result: [String: Any] = [ + "shortChannelId": channel.shortChannelId, + "maxChannelExtensionExpiryBlocks": channel.maxChannelExtensionExpiryBlocks, + "expirationBlock": channel.expirationBlock + ] + if let originalOrder = channel.originalOrder { + result["originalOrder"] = [ + "id": originalOrder.id, + "service": originalOrder.service + ] + } + if let extensionOrderIds = channel.extensionOrderIds { + result["extensionOrderIds"] = extensionOrderIds + } + return result + } + + // MARK: - Log File Methods + + @objc(tailLdkNodeLog:resolver:rejecter:) + func tailLdkNodeLog(_ numLines: NSNumber, + resolver resolve: @escaping RCTPromiseResolveBlock, + rejecter reject: @escaping RCTPromiseRejectBlock) { + let logPath = "\(self.storedStorageDirPath)/ldk_node.log" + resolve(LogFileObserver.tailFile(path: logPath, numLines: numLines.intValue)) + } + + @objc(observeLdkNodeLogFile:rejecter:) + func observeLdkNodeLogFile(_ resolve: @escaping RCTPromiseResolveBlock, + rejecter reject: @escaping RCTPromiseRejectBlock) { + if logFileObserver != nil { + resolve(true) + return + } + let logPath = "\(self.storedStorageDirPath)/ldk_node.log" + logFileObserver = LogFileObserver(filePath: logPath) { [weak self] data in + self?.sendEvent(withName: "ldklog", body: data) + } + logFileObserver?.startObserving() + resolve(true) + } + + private func serializeLsps7OrderState(_ state: Lsps7OrderState) -> String { + switch state { + case .created: + return "CREATED" + case .completed: + return "COMPLETED" + case .failed: + return "FAILED" + } + } } diff --git a/ios/LdkNodeMobile/LogFileObserver.swift b/ios/LdkNodeMobile/LogFileObserver.swift new file mode 100644 index 000000000..7aa10b54b --- /dev/null +++ b/ios/LdkNodeMobile/LogFileObserver.swift @@ -0,0 +1,71 @@ +import Foundation + +public class LogFileObserver { + private let filePath: String + private let onNewData: (String) -> Void + private var fileHandle: FileHandle? + private var source: DispatchSourceFileSystemObject? + + public init(filePath: String, onNewData: @escaping (String) -> Void) { + self.filePath = filePath + self.onNewData = onNewData + } + + public func startObserving() { + // Create file if needed + let fm = FileManager.default + if !fm.fileExists(atPath: filePath) { + let dir = (filePath as NSString).deletingLastPathComponent + try? fm.createDirectory(atPath: dir, withIntermediateDirectories: true) + fm.createFile(atPath: filePath, contents: nil) + } + + guard let fh = FileHandle(forReadingAtPath: filePath) else { return } + self.fileHandle = fh + + // Seek to end (skip historical) + fh.seekToEndOfFile() + + // Use GCD dispatch source to watch for writes + let fd = fh.fileDescriptor + let source = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: fd, + eventMask: .write, + queue: DispatchQueue.global(qos: .utility) + ) + source.setEventHandler { [weak self] in + let data = fh.availableData + if data.count > 0, let str = String(data: data, encoding: .utf8) { + self?.onNewData(str) + } + } + source.setCancelHandler { + fh.closeFile() + } + source.resume() + self.source = source + } + + public func stopObserving() { + source?.cancel() + source = nil + fileHandle = nil + } + + public static func tailFile(path: String, numLines: Int) -> String { + guard let fh = FileHandle(forReadingAtPath: path) else { return "" } + defer { fh.closeFile() } + + let fileSize = fh.seekToEndOfFile() + if fileSize == 0 { return "" } + + // Read last chunk (256KB should be plenty for typical numLines) + var chunkSize: UInt64 = 262144 + if chunkSize > fileSize { chunkSize = fileSize } + fh.seek(toFileOffset: fileSize - chunkSize) + + guard let data = String(data: fh.readDataToEndOfFile(), encoding: .utf8) else { return "" } + let lines = data.components(separatedBy: "\n") + return lines.suffix(numLines).joined(separator: "\n") + } +} diff --git a/ios/zeus.xcodeproj/project.pbxproj b/ios/zeus.xcodeproj/project.pbxproj index 1ce55254b..db292582c 100644 --- a/ios/zeus.xcodeproj/project.pbxproj +++ b/ios/zeus.xcodeproj/project.pbxproj @@ -170,6 +170,7 @@ B1C2D3E62D36C99900000003 /* LDKNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C2D3E52D36C99900000002 /* LDKNode.swift */; }; B1C2D3E92D36C99900000006 /* LdkNodeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C2D3E72D36C99900000004 /* LdkNodeModule.swift */; }; B1C2D3EA2D36C99900000007 /* LdkNodeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = B1C2D3E82D36C99900000005 /* LdkNodeModule.m */; }; + B1C2D3EC2D36C99900000009 /* LogFileObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C2D3EB2D36C99900000008 /* LogFileObserver.swift */; }; B1DB19555C1C4546922217C2 /* eye_closed.svg in Resources */ = {isa = PBXBuildFile; fileRef = 744BE042F00E4B16BDC79870 /* eye_closed.svg */; }; B20A92EA4E82493CB6D1A2F0 /* NFC.svg in Resources */ = {isa = PBXBuildFile; fileRef = 3C37D42473A54AD981B549B4 /* NFC.svg */; }; B39739CDBF39F16F2AD52938 /* libPods-zeus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC14DF216D694B9C5C997FE /* libPods-zeus.a */; }; @@ -676,6 +677,7 @@ B1C2D3E52D36C99900000002 /* LDKNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LDKNode.swift; sourceTree = ""; }; B1C2D3E72D36C99900000004 /* LdkNodeModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LdkNodeModule.swift; sourceTree = ""; }; B1C2D3E82D36C99900000005 /* LdkNodeModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LdkNodeModule.m; sourceTree = ""; }; + B1C2D3EB2D36C99900000008 /* LogFileObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogFileObserver.swift; sourceTree = ""; }; B36D434AE9B14EE7AD5E6BCE /* Routing.svg */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Routing.svg; path = ../assets/images/SVG/Routing.svg; sourceTree = ""; }; B4C39762C9B748E39BFDA90F /* ExchangeBitcoin.svg */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = ExchangeBitcoin.svg; path = ../assets/images/SVG/ExchangeBitcoin.svg; sourceTree = ""; }; B4D00602022C4AE1BFA1A2C1 /* Hourglass.svg */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Hourglass.svg; path = ../assets/images/SVG/Hourglass.svg; sourceTree = ""; }; @@ -1097,6 +1099,7 @@ isa = PBXGroup; children = ( B1C2D3E52D36C99900000002 /* LDKNode.swift */, + B1C2D3EB2D36C99900000008 /* LogFileObserver.swift */, B1C2D3E72D36C99900000004 /* LdkNodeModule.swift */, B1C2D3E82D36C99900000005 /* LdkNodeModule.m */, ); @@ -2038,6 +2041,7 @@ B1C2D3E62D36C99900000003 /* LDKNode.swift in Sources */, B1C2D3E92D36C99900000006 /* LdkNodeModule.swift in Sources */, B1C2D3EA2D36C99900000007 /* LdkNodeModule.m in Sources */, + B1C2D3EC2D36C99900000009 /* LogFileObserver.swift in Sources */, 745388452A54C2B6000927CE /* LndMobileScheduledSync.swift in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, 13B07FBD1A6810BG00A75B9A /* BackgroundTaskManager.m in Sources */, diff --git a/ldknode/LdkNode.d.ts b/ldknode/LdkNode.d.ts index 82bce7483..9404e30b9 100644 --- a/ldknode/LdkNode.d.ts +++ b/ldknode/LdkNode.d.ts @@ -518,6 +518,25 @@ export interface ILdkNodeModule { ): Promise; lsps1CheckOrderStatus(orderId: string): Promise; + // LSPS7 Methods + setLiquiditySourceLsps7( + nodeId: string, + address: string, + token?: string | null + ): Promise; + lsps7GetExtendableChannels(): Promise; + lsps7CreateOrder( + shortChannelId: string, + channelExtensionExpiryBlocks: number, + token?: string | null, + refundOnchainAddress?: string | null + ): Promise; + lsps7CheckOrderStatus(orderId: string): Promise; + + // Log File Methods + tailLdkNodeLog(numLines: number): Promise; + observeLdkNodeLogFile(): Promise; + // Message Signing Methods signMessage(message: string): Promise<{ signature: string }>; verifySignature( diff --git a/locales/en.json b/locales/en.json index b32993cb3..f8b881cd2 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1291,6 +1291,7 @@ "views.Settings.EmbeddedNode.AdvancedRescan.start": "Start rescan", "views.Settings.EmbeddedNode.LNDLogs.title": "LND Logs", "views.Settings.EmbeddedNode.LNDLogs.copyLogs": "Copy logs to clipboard", + "views.Settings.EmbeddedNode.LDKLogs.title": "LDK Node Logs", "views.Settings.EmbeddedNode.feeEstimator": "Fee estimator", "views.Settings.EmbeddedNode.customFeeEstimator": "Custom fee estimator", "views.Settings.EmbeddedNode.speedloader": "Express Graph Sync server", diff --git a/utils/EventListenerUtils.ts b/utils/EventListenerUtils.ts index 8ebff9ebc..4f4fc39a4 100644 --- a/utils/EventListenerUtils.ts +++ b/utils/EventListenerUtils.ts @@ -16,3 +16,9 @@ export const LndMobileToolsEventEmitter = ? DeviceEventEmitter : // @ts-ignore:next-line new NativeEventEmitter(NativeModules.LndMobileTools); + +export const LdkNodeEventEmitter = + Platform.OS == 'android' + ? DeviceEventEmitter + : // @ts-ignore:next-line + new NativeEventEmitter(NativeModules.LdkNodeModule); diff --git a/views/Settings/EmbeddedNode/LDKLogs.tsx b/views/Settings/EmbeddedNode/LDKLogs.tsx new file mode 100644 index 000000000..453c5a864 --- /dev/null +++ b/views/Settings/EmbeddedNode/LDKLogs.tsx @@ -0,0 +1,104 @@ +import * as React from 'react'; +import { NativeModules, Platform, View } from 'react-native'; +import { NativeStackNavigationProp } from '@react-navigation/native-stack'; + +import CopyButton from '../../../components/CopyButton'; +import Screen from '../../../components/Screen'; +import Header from '../../../components/Header'; +import LogBox from '../../../components/LogBox'; + +import { localeString } from '../../../utils/LocaleUtils'; +import { themeColor } from '../../../utils/ThemeUtils'; +import { LdkNodeEventEmitter } from '../../../utils/EventListenerUtils'; + +const MAX_LOG_LENGTH = 100000; + +interface LDKLogsProps { + navigation: NativeStackNavigationProp; +} + +interface LDKLogsState { + log: string; +} + +export default class LDKLogs extends React.Component< + LDKLogsProps, + LDKLogsState +> { + state = { + log: '' + }; + logListener: any = null; + + async componentDidMount(): Promise { + (async () => { + const tailLog = await NativeModules.LdkNodeModule.tailLdkNodeLog( + 100 + ); + let log = tailLog; + + this.logListener = LdkNodeEventEmitter.addListener( + 'ldklog', + (data: string) => { + log = log + data; + if (log.length > MAX_LOG_LENGTH) { + log = log.slice(-MAX_LOG_LENGTH); + } + this.setState({ log }); + } + ); + + NativeModules.LdkNodeModule.observeLdkNodeLogFile().catch( + (e: any) => { + console.log('Could not observe LDK log file:', e); + } + ); + + this.setState({ log }); + })(); + } + + componentWillUnmount(): void { + if (this.logListener) { + this.logListener.remove(); + } + } + + render() { + const { navigation } = this.props; + const { log } = this.state; + + return ( + + +
+ + + + + + + ); + } +} diff --git a/views/Settings/EmbeddedNode/LNDLogs.tsx b/views/Settings/EmbeddedNode/LNDLogs.tsx index c3643a55c..f93bd311e 100644 --- a/views/Settings/EmbeddedNode/LNDLogs.tsx +++ b/views/Settings/EmbeddedNode/LNDLogs.tsx @@ -54,6 +54,9 @@ export default class LNDLogs extends React.Component< 'lndlog', (data: string) => { log = log + data.slice(11); + if (log.length > 100000) { + log = log.slice(-100000); + } this.setState({ log }); diff --git a/views/Settings/EmbeddedNode/RapidGossipSync.tsx b/views/Settings/EmbeddedNode/RapidGossipSync.tsx index f4ad53a53..cf78a425f 100644 --- a/views/Settings/EmbeddedNode/RapidGossipSync.tsx +++ b/views/Settings/EmbeddedNode/RapidGossipSync.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { ScrollView, Text, View } from 'react-native'; import { inject, observer } from 'mobx-react'; -import { StackNavigationProp } from '@react-navigation/stack'; +import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import Button from '../../../components/Button'; import Header from '../../../components/Header'; @@ -16,7 +16,7 @@ import { themeColor } from '../../../utils/ThemeUtils'; import { getDefaultRgsServer } from '../../../utils/EmbeddedLdkNodeUtils'; interface RapidGossipSyncProps { - navigation: StackNavigationProp; + navigation: NativeStackNavigationProp; SettingsStore: SettingsStore; } diff --git a/views/Settings/EmbeddedNode/VssServer.tsx b/views/Settings/EmbeddedNode/VssServer.tsx index 979e6da5a..4f483e34c 100644 --- a/views/Settings/EmbeddedNode/VssServer.tsx +++ b/views/Settings/EmbeddedNode/VssServer.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { ScrollView, Text, View } from 'react-native'; import { inject, observer } from 'mobx-react'; -import { StackNavigationProp } from '@react-navigation/stack'; +import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import Button from '../../../components/Button'; import Header from '../../../components/Header'; @@ -16,7 +16,7 @@ import { themeColor } from '../../../utils/ThemeUtils'; import { DEFAULT_VSS_SERVER } from '../../../utils/EmbeddedLdkNodeUtils'; interface VssServerProps { - navigation: StackNavigationProp; + navigation: NativeStackNavigationProp; SettingsStore: SettingsStore; } diff --git a/views/Settings/EmbeddedNode/index.tsx b/views/Settings/EmbeddedNode/index.tsx index 114532a80..c95be5fe2 100644 --- a/views/Settings/EmbeddedNode/index.tsx +++ b/views/Settings/EmbeddedNode/index.tsx @@ -273,6 +273,33 @@ export default class EmbeddedNode extends React.Component< )} + {/* Embedded LDK: LDK Logs */} + {isEmbeddedLdk && ( + navigation.navigate('LDKLogs')} + > + + + {localeString( + 'views.Settings.EmbeddedNode.LDKLogs.title' + )} + + + + + )} + {/* Embedded LND: Advanced */} {isEmbeddedLnd && (