8000 use a log file for daemon · coder/coder-desktop-macos@4537dbc · GitHub
[go: up one dir, main page]

Skip to content

Commit 4537dbc

Browse files
committed
use a log file for daemon
1 parent 4689f22 commit 4537dbc

File tree

6 files changed

+39
-159
lines changed
  • Coder-Desktop
  • VPNLibTests
  • VPNLib
  • 6 files changed

    +39
    -159
    lines changed

    Coder-Desktop/Coder-Desktop/Preview Content/PreviewFileSync.swift

    Lines changed: 2 additions & 2 deletions
    Original file line numberDiff line numberDiff line change
    @@ -2,12 +2,12 @@ import VPNLib
    22

    33
    @MainActor
    44
    final class PreviewFileSync: FileSyncDaemon {
    5+
    var logFile: URL = .init(filePath: "~/log.txt")!
    6+
    57
    var sessionState: [VPNLib.FileSyncSession] = []
    68

    79
    var state: DaemonState = .running
    810

    9-
    var recentLogs: [String] = []
    10-
    1111
    init() {}
    1212

    1313
    func refreshSessions() async {}

    Coder-Desktop/Coder-Desktop/Views/FileSync/FileSyncConfig.swift

    Lines changed: 5 additions & 3 deletions
    Original file line numberDiff line numberDiff line change
    @@ -86,10 +86,12 @@ struct FileSyncConfig<VPN: VPNService, FS: FileSyncDaemon>: View {
    8686
    dontRetry = true
    8787
    }
    8888
    } message: {
    89-
    // You can't have styled text in alert messages
    9089
    Text("""
    91-
    File sync daemon failed: \(fileSync.state.description)\n\n\(fileSync.recentLogs.joined(separator: "\n"))
    92-
    """)
    90+
    File sync daemon failed. The daemon log file at\n\(fileSync.logFile.path)\nhas been opened.
    91+
    """).onAppear {
    92+
    // Open the log file in the default editor
    93+
    NSWorkspace.shared.open(fileSync.logFile)
    94+
    }
    9395
    }.task {
    9496
    // When the Window is visible, poll for session updates every
    9597
    // two seconds.

    Coder-Desktop/Coder-DesktopTests/Util.swift

    Lines changed: 2 additions & 2 deletions
    Original file line numberDiff line numberDiff line change
    @@ -29,12 +29,12 @@ class MockVPNService: VPNService, ObservableObject {
    2929

    3030
    @MainActor
    3131
    class MockFileSyncDaemon: FileSyncDaemon {
    32+
    var logFile: URL = .init(filePath: "~/log.txt")
    33+
    3234
    var sessionState: [VPNLib.FileSyncSession] = []
    3335

    3436
    func refreshSessions() async {}
    3537

    36-
    var recentLogs: [String] = []
    37-
    3838
    func deleteSessions(ids _: [String]) async throws(VPNLib.DaemonError) {}
    3939

    4040
    var state: VPNLib.DaemonState = .running

    Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift

    Lines changed: 30 additions & 25 deletions
    Original file line numberDiff line numberDiff line change
    @@ -10,7 +10,7 @@ import SwiftUI
    1010
    public protocol FileSyncDaemon: ObservableObject {
    1111
    var state: DaemonState { get }
    1212
    var sessionState: [FileSyncSession] { get }
    13-
    var recentLogs: [String] { get }
    13+
    var logFile: URL { get }
    1414
    func tryStart() async
    1515
    func stop() async
    1616
    func refreshSessions() async
    @@ -39,15 +39,13 @@ public class MutagenDaemon: FileSyncDaemon {
    3939

    4040
    @Published public var sessionState: [FileSyncSession] = []
    4141

    42-
    // We store the last N log lines to show in the UI if the daemon crashes
    43-
    private var logBuffer: RingBuffer<String>
    44-
    public var recentLogs: [String] { logBuffer.elements }
    45-
    4642
    private var mutagenProcess: Subprocess?
    4743
    private let mutagenPath: URL!
    4844
    private let mutagenDataDirectory: URL
    4945
    private let mutagenDaemonSocket: URL
    5046

    47+
    public let logFile: URL
    48+
    5149
    // Managing sync sessions could take a while, especially with prompting
    5250
    let sessionMgmtReqTimeout: TimeAmount = .seconds(15)
    5351

    @@ -64,13 +62,12 @@ public class MutagenDaemon: FileSyncDaemon {
    6462
    mutagenDataDirectory: URL = FileManager.default.urls(
    6563
    for: .applicationSupportDirectory,
    6664
    in: .userDomainMask
    67-
    ).first!.appending(path: "Coder Desktop").appending(path: "Mutagen"),
    68-
    logBufferCapacity: Int = 10)
    65+
    ).first!.appending(path: "Coder Desktop").appending(path: "Mutagen"))
    6966
    {
    70-
    logBuffer = .init(capacity: logBufferCapacity)
    7167
    self.mutagenPath = mutagenPath
    7268
    self.mutagenDataDirectory = mutagenDataDirectory
    7369
    mutagenDaemonSocket = mutagenDataDirectory.appending(path: "daemon").appending(path: "daemon.sock")
    70+
    logFile = mutagenDataDirectory.appending(path: "daemon.log")
    7471
    // It shouldn't be fatal if the app was built without Mutagen embedded,
    7572
    // but file sync will be unavailable.
    7673
    if mutagenPath == nil {
    @@ -113,34 +110,23 @@ public class MutagenDaemon: FileSyncDaemon {
    113110

    114111
    // Creating the same process twice from Swift will crash the MainActor,
    115112
    // so we need to wait for an earlier process to die
    116-
    if let waitForExit {
    117-
    await waitForExit()
    118-
    // We *need* to be sure the process is dead or the app ends up in an
    119-
    // unrecoverable state
    120-
    try? await Task.sleep(for: .seconds(1))
    121-
    }
    113+
    await waitForExit?()
    122114

    123115
    await transition.wait()
    124116
    defer { transition.signal() }
    125117
    logger.info("starting mutagen daemon")
    126118

    127119
    mutagenProcess = createMutagenProcess()
    128-
    // swiftlint:disable:next large_tuple
    129-
    let (standardOutput, standardError, waitForExit): (Pipe.AsyncBytes, Pipe.AsyncBytes, @Sendable () async -> Void)
    120+
    let (standardError, waitForExit): (Pipe.AsyncBytes, @Sendable () async -> Void)
    130121
    do {
    131-
    (standardOutput, standardError, waitForExit) = try mutagenProcess!.run()
    122+
    (_, standardError, waitForExit) = try mutagenProcess!.run()
    132123
    } catch {
    133124
    throw .daemonStartFailure(error)
    134125
    }
    135126
    self.waitForExit = waitForExit
    136127

    137128
    Task {
    138-
    await streamHandler(io: standardOutput)
    139-
    logger.info("standard output stream closed")
    140-
    }
    141-
    142-
    Task {
    143-
    await streamHandler(io: standardError)
    129+
    await handleDaemonLogs(io: standardError)
    144130
    logger.info("standard error stream closed")
    145131
    }
    146132

    @@ -283,11 +269,30 @@ public class MutagenDaemon: FileSyncDaemon {
    283269
    }
    284270
    }
    285271

    286-
    private func streamHandler(io: Pipe.AsyncBytes) async {
    272+
    private func handleDaemonLogs(io: Pipe.AsyncBytes) async {
    273+
    if !FileManager.default.fileExists(atPath: logFile.path) {
    274+
    guard FileManager.default.createFile(atPath: logFile.path, contents: nil) else {
    275+
    logger.error("Failed to create log file")
    276+
    return
    277+
    }
    278+
    }
    279+
    280+
    guard let fileHandle = try? FileHandle(forWritingTo: logFile) else {
    281+
    logger.error("Failed to open log file for writing")
    282+
    return
    283+
    }
    284+
    287285
    for await line in io.lines {
    288286
    logger.info("\(line, privacy: .public)")
    289-
    logBuffer.append(line)
    287+
    288+
    do {
    289+
    try fileHandle.write(contentsOf: Data("\(line)\n".utf8))
    290+
    } catch {
    291+
    logger.error("Failed to write to daemon log file: \(error)")
    292+
    }
    290293
    }
    294+
    295+
    try? fileHandle.close()
    291296
    }
    292297
    }
    293298

    Coder-Desktop/VPNLib/Util.swift

    Lines changed: 0 additions & 36 deletions
    Original file line numberDiff line numberDiff line change
    @@ -29,39 +29,3 @@ public func makeNSError(suffix: String, code: Int = -1, desc: String) -> NSError
    2929
    userInfo: [NSLocalizedDescriptionKey: desc]
    3030
    )
    3131
    }
    32-
    33-
    // Insertion-only RingBuffer for buffering the last `capacity` elements,
    34-
    // and retrieving them in insertion order.
    35-
    public struct RingBuffer<T> {
    36-
    private var buffer: [T?]
    37-
    private var start = 0
    38-
    private var size = 0
    39-
    40-
    public init(capacity: Int) {
    41-
    buffer = Array(repeating: nil, count: capacity)
    42-
    }
    43-
    44-
    public mutating func append(_ element: T) {
    45-
    let writeIndex = (start + size) % buffer.count
    46-
    buffer[writeIndex] = element
    47-
    48-
    if size < buffer.count {
    49-
    size += 1
    50-
    } else {
    51-
    start = (start + 1) % buffer.count
    52-
    }
    53-
    }
    54-
    55-
    public var elements: [T] {
    56-
    var result = [T]()
    57-
    result.reserveCapacity(size)
    58-
    for i in 0 ..< size {
    59-
    let index = (start + i) % buffer.count
    60-
    if let element = buffer[index] {
    61-
    result.append(element)
    62-
    }
    63-
    }
    64-
    65-
    return result
    66-
    }
    67-
    }

    Coder-Desktop/VPNLibTests/UtilTests.swift

    Lines changed: 0 additions & 91 deletions
    This file was deleted.

    0 commit comments

    Comments
     (0)
    0