如何在C#应用运行时实时查看自定义日志文件的更新?
Hey Andrew, let's tackle this problem you're having with real-time log viewing while your C# app is running.
First, let's refine your existing logging code to ensure it plays nicely with both external tools and in-app viewers:
Optimized Logging Method
Your current LogSync method is solid, but we can make a couple of tweaks to ensure the file handle is properly released (so external tools can read it without lock issues) and simplify the code a bit:
protected void LogSync(string message) { try { lock (SynLock) { // The using block automatically disposes the StreamWriter, closing the file handle using (var sw = new StreamWriter(SyncFilename, true)) { sw.WriteLine(message); // Replaces Write + Environment.NewLine for cleaner code } } } catch (Exception e) { Console.WriteLine($"Log write failed: {e.Message}"); throw; } }
Why This Works
The using statement guarantees the StreamWriter is disposed immediately after writing, which releases the file lock. This means tools like:
- Notepad++ (enable "Reload if changed" in the View menu)
- PowerShell's
Get-Content -Path "your-log-file.txt" -Wait - Unix-like
tail -f your-log-file.txt(on Linux/macOS/WSL)
can all monitor the log file in real time as your app writes to it.
Build an In-App Real-Time Log Viewer
If you want to add a dedicated log viewer directly into your C# app (e.g., a text box that updates as logs are written), you can use FileSystemWatcher to monitor the log file and display new content automatically. Here's a step-by-step implementation:
Step 1: Declare Required Variables
Add these fields to your form/class:
private FileSystemWatcher _logFileWatcher; private long _lastReadPosition = 0; private readonly object _uiUpdateLock = new object();
Step 2: Initialize the File Watcher
Call this method when your app starts (e.g., in Form_Load for WinForms):
private void InitializeLogViewer() { var logDirectory = Path.GetDirectoryName(SyncFilename); var logFileName = Path.GetFileName(SyncFilename); _logFileWatcher = new FileSystemWatcher(logDirectory, logFileName) { NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size, EnableRaisingEvents = true }; // Hook up the event handler for file changes _logFileWatcher.Changed += OnLogFileUpdated; // Load existing log content on startup if (File.Exists(SyncFilename)) { var fileInfo = new FileInfo(SyncFilename); _lastReadPosition = fileInfo.Length; var initialLogs = File.ReadAllText(SyncFilename); UpdateLogDisplay(initialLogs); } }
Step 3: Handle File Change Events
This method reads only the new content added since the last check:
private void OnLogFileUpdated(object sender, FileSystemEventArgs e) { // FileSystemWatcher can fire multiple events for one write—add a small delay to avoid duplicates Thread.Sleep(100); var fileInfo = new FileInfo(e.FullPath); if (fileInfo.Length <= _lastReadPosition) return; try { // Open the file with shared read/write access to avoid locking conflicts using (var stream = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) using (var reader = new StreamReader(stream)) { // Jump to the last position we read reader.BaseStream.Seek(_lastReadPosition, SeekOrigin.Begin); var newLogContent = reader.ReadToEnd(); // Update our last read position _lastReadPosition = reader.BaseStream.Position; // Update the UI with new content UpdateLogDisplay(newLogContent); } } catch (IOException) { // Retry if the file was temporarily locked by the logging method Thread.Sleep(500); OnLogFileUpdated(sender, e); } }
Step 4: Update the UI
This method ensures we update the UI safely from the main thread (critical for WinForms/WPF):
private void UpdateLogDisplay(string newContent) { // For WinForms: Check if we need to marshal to the UI thread if (yourLogTextBox.InvokeRequired) { yourLogTextBox.Invoke(new Action<string>(UpdateLogDisplay), newContent); return; } lock (_uiUpdateLock) { yourLogTextBox.AppendText(newContent); // Auto-scroll to the bottom of the text box yourLogTextBox.SelectionStart = yourLogTextBox.Text.Length; yourLogTextBox.ScrollToCaret(); } }
Step 5: Clean Up the Watcher
Don't forget to dispose the watcher when your app closes:
private void OnApplicationClosing(object sender, EventArgs e) { _logFileWatcher?.Dispose(); }
Key Notes
- Avoid Duplicate Events: The small
Thread.SleepinOnLogFileUpdatedhelps prevent multiple UI updates for a single log write. - Shared File Access: Using
FileShare.ReadWriteensures your log viewer can read the file even while the logging method is writing to it. - Thread Safety: Always marshal UI updates to the main thread to avoid cross-thread exceptions.
内容的提问来源于stack exchange,提问作者Andrew Simpson

