Skip to content

Commit 79f00b8

Browse files
authored
Log exception synchronously on unhandled exception (#5369)
1 parent 59a66de commit 79f00b8

File tree

7 files changed

+106
-37
lines changed

7 files changed

+106
-37
lines changed

Common/ILogWriter.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
4-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
52

63
namespace Files.Common
74
{
85
public interface ILogWriter
96
{
10-
Task WriteLineToLog(string text);
117
Task InitializeAsync(string name);
8+
Task WriteLineToLogAsync(string text);
9+
void WriteLineToLog(string text);
1210
}
1311
}

Common/Logger.cs

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
using Files.Common;
2-
using System;
3-
using System.Collections.Generic;
1+
using System;
42
using System.Diagnostics;
53
using System.IO;
6-
using System.Linq;
7-
using System.Text;
84
using System.Threading;
95
using System.Threading.Tasks;
106

@@ -15,70 +11,65 @@ public class Logger
1511
ILogWriter LogWriter { get; }
1612
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
1713

18-
public Logger(ILogWriter logWriter, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
14+
public Logger(ILogWriter logWriter)
1915
{
2016
LogWriter = logWriter;
2117
}
2218

23-
public void Error(Exception ex, string formattedException, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
19+
public void Error(Exception ex, string error = "", [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
2420
{
25-
Log(type: "ERROR", caller: caller, message: $"{ex.Message}\n\t{formattedException}");
21+
LogAsync(type: "ERROR", caller: caller, message: $"{error}\n\t{ex}");
2622
}
2723

28-
public void Error(Exception ex, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
24+
public void UnhandledError(Exception ex, string error = "", [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
2925
{
30-
Log(type: "ERROR", caller: caller, message: $"{ex.Message}\n\t{ex}");
26+
LogSync(type: "ERROR", caller: caller, message: $"{error}\n\t{ex}");
3127
}
3228

3329
public void Error(string error, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
3430
{
35-
Log(type: "ERROR", caller: caller, message: error);
31+
LogAsync(type: "ERROR", caller: caller, message: error);
3632
}
3733

38-
public void Info(string info, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
39-
{
40-
Log(type: "INFO", caller: caller, message: info);
41-
}
42-
43-
public void Warn(Exception ex, string warning, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
34+
public void Warn(Exception ex, string warning = "", [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
4435
{
45-
Log(type: "WARN", caller: caller, message: $"{warning}\n\t{ex}");
36+
LogAsync(type: "WARN", caller: caller, message: $"{warning}\n\t{ex}");
4637
}
4738

4839
public void Warn(string warning, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
4940
{
50-
Log(type: "WARN", caller: caller, message: warning);
41+
LogAsync(type: "WARN", caller: caller, message: warning);
5142
}
5243

53-
public void Info(Exception ex, string info, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
44+
public void Info(Exception ex, string info = "", [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
5445
{
55-
Log(type: "INFO", caller: caller, message: $"{info}\n\t{ex}");
46+
LogAsync(type: "INFO", caller: caller, message: $"{info}\n\t{ex}");
5647
}
5748

58-
public void Info(string info, object obj, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
49+
public void Info(string info, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
5950
{
60-
Log(type: "INFO", caller: caller, message: string.Format(info, obj));
51+
LogAsync(type: "INFO", caller: caller, message: info);
6152
}
6253

63-
public void Warn(Exception ex, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
54+
public void Info(string info, object obj, [System.Runtime.CompilerServices.CallerMemberName] string caller = "")
6455
{
65-
Log(type: "WARN", caller: caller, message: $"{ex.Message}\n\t{ex}");
56+
LogAsync(type: "INFO", caller: caller, message: string.Format(info, obj));
6657
}
6758

68-
private async void Log(string type, string caller, string message, int attemptNumber = 0)
59+
private async void LogAsync(string type, string caller, string message, int attemptNumber = 0)
6960
{
7061
try
7162
{
7263
await semaphoreSlim.WaitAsync();
73-
await LogWriter.WriteLineToLog($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}|{type}|{caller}|{message}");
64+
await LogWriter.WriteLineToLogAsync($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}|{type}|{caller}|{message}");
7465
}
7566
catch (IOException e) when (!(e is FileNotFoundException))
7667
{
7768
if (attemptNumber < 5) // check the attempt count to prevent a stack overflow exception
7869
{
7970
// Log is likely in use by another process instance, so wait then try again
8071
await Task.Delay(50);
81-
Log(type, caller, message, attemptNumber + 1);
72+
LogAsync(type, caller, message, attemptNumber + 1);
8273
}
8374
else
8475
{
@@ -94,5 +85,22 @@ private async void Log(string type, string caller, string message, int attemptNu
9485
semaphoreSlim.Release();
9586
}
9687
}
88+
89+
private void LogSync(string type, string caller, string message)
90+
{
91+
try
92+
{
93+
semaphoreSlim.Wait();
94+
LogWriter.WriteLineToLog($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}|{type}|{caller}|{message}");
95+
}
96+
catch (Exception e)
97+
{
98+
Debug.WriteLine($"Writing to log file failed with the following exception:\n{e}");
99+
}
100+
finally
101+
{
102+
semaphoreSlim.Release();
103+
}
104+
}
97105
}
98106
}

Files.Launcher/LogWriter.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using Files.Common;
22
using System;
33
using System.Diagnostics;
4+
using System.Text;
45
using System.Threading.Tasks;
56
using Windows.Storage;
67
using Windows.Storage.Streams;
8+
using static Vanara.PInvoke.Kernel32;
79

810
namespace FilesFullTrust
911
{
@@ -21,7 +23,7 @@ public async Task InitializeAsync(string name)
2123
}
2224
}
2325

24-
public async Task WriteLineToLog(string text)
26+
public async Task WriteLineToLogAsync(string text)
2527
{
2628
if (logFile is null)
2729
{
@@ -36,5 +38,24 @@ public async Task WriteLineToLog(string text)
3638

3739
Debug.WriteLine($"Logged event: {text}");
3840
}
41+
42+
public void WriteLineToLog(string text)
43+
{
44+
if (logFile is null)
45+
{
46+
return;
47+
}
48+
using SafeHFILE hStream = CreateFile(logFile.Path,
49+
FileAccess.GENERIC_WRITE, System.IO.FileShare.Read, null, System.IO.FileMode.OpenOrCreate, Vanara.PInvoke.FileFlagsAndAttributes.FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
50+
if (hStream.IsInvalid)
51+
{
52+
return;
53+
}
54+
byte[] buff = Encoding.UTF8.GetBytes("\n" + text);
55+
SetFilePointer(hStream, 0, IntPtr.Zero, System.IO.SeekOrigin.End);
56+
WriteFile(hStream, buff, (uint)buff.Length, out var dwBytesWritten, IntPtr.Zero);
57+
58+
Debug.WriteLine($"Logged event: {text}");
59+
}
3960
}
4061
}

Files.Launcher/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private static void Main(string[] args)
120120
private static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
121121
{
122122
var exception = e.ExceptionObject as Exception;
123-
Logger.Error(exception, exception.Message);
123+
Logger.UnhandledError(exception, exception.Message);
124124
}
125125

126126
private static async void RecycleBinWatcher_Changed(object sender, FileSystemEventArgs e)

Files/App.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ private static void AppUnhandledException(Exception ex)
439439
Debugger.Break(); // Please check "Output Window" for exception details (View -> Output Window) (CTRL + ALT + O)
440440

441441
SaveSessionTabs();
442-
Logger.Error(ex, formattedException);
442+
Logger.UnhandledError(ex, ex.Message);
443443
if (ShowErrorNotification)
444444
{
445445
var toastContent = new ToastContent()

Files/Helpers/NativeFileOperationsHelper.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ public enum File_Attributes : uint
4141

4242
public const uint GENERIC_READ = 0x80000000;
4343
public const uint GENERIC_WRITE = 0x40000000;
44+
public const uint FILE_APPEND_DATA = 0x0004;
4445

4546
public const uint FILE_SHARE_READ = 0x00000001;
4647
public const uint FILE_SHARE_WRITE = 0x00000002;
4748
public const uint FILE_SHARE_DELETE = 0x00000004;
4849

50+
public const uint FILE_BEGIN = 0;
51+
public const uint FILE_END = 2;
52+
4953
public const uint CREATE_ALWAYS = 2;
5054
public const uint CREATE_NEW = 1;
5155
public const uint OPEN_ALWAYS = 4;
@@ -137,6 +141,16 @@ public static extern bool SetFileAttributesFromApp(
137141
string lpFileName,
138142
System.IO.FileAttributes dwFileAttributes);
139143

144+
[DllImport("api-ms-win-core-file-l1-2-1.dll", ExactSpelling = true,
145+
CallingConvention = CallingConvention.StdCall,
146+
SetLastError = true)]
147+
public static extern uint SetFilePointer(
148+
IntPtr hFile,
149+
long lDistanceToMove,
150+
IntPtr lpDistanceToMoveHigh,
151+
uint dwMoveMethod
152+
);
153+
140154
[DllImport("api-ms-win-core-file-l1-2-1.dll", CharSet = CharSet.Auto,
141155
CallingConvention = CallingConvention.StdCall,
142156
SetLastError = true)]

Files/Helpers/UniversalLogWriter.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Threading.Tasks;
66
using Windows.Storage;
77
using Windows.Storage.Streams;
8+
using static Files.Helpers.NativeFileOperationsHelper;
89

910
namespace Files.Helpers
1011
{
@@ -25,7 +26,7 @@ public async Task InitializeAsync(string name)
2526
}
2627
}
2728

28-
public async Task WriteLineToLog(string text)
29+
public async Task WriteLineToLogAsync(string text)
2930
{
3031
if (logFile is null)
3132
{
@@ -40,5 +41,32 @@ public async Task WriteLineToLog(string text)
4041

4142
Debug.WriteLine($"Logged event: {text}");
4243
}
44+
45+
public void WriteLineToLog(string text)
46+
{
47+
if (logFile is null)
48+
{
49+
return;
50+
}
51+
IntPtr hStream = CreateFileFromApp(logFile.Path,
52+
GENERIC_WRITE, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, (uint)File_Attributes.BackupSemantics, IntPtr.Zero);
53+
if (hStream.ToInt64() == -1)
54+
{
55+
return;
56+
}
57+
byte[] buff = Encoding.UTF8.GetBytes("\n" + text);
58+
int dwBytesWritten;
59+
unsafe
60+
{
61+
fixed (byte* pBuff = buff)
62+
{
63+
SetFilePointer(hStream, 0, IntPtr.Zero, FILE_END);
64+
WriteFile(hStream, pBuff, buff.Length, &dwBytesWritten, IntPtr.Zero);
65+
}
66+
}
67+
CloseHandle(hStream);
68+
69+
Debug.WriteLine($"Logged event: {text}");
70+
}
4371
}
4472
}

0 commit comments

Comments
 (0)