-
Notifications
You must be signed in to change notification settings - Fork 591
/
DiskLogListener.cs
113 lines (89 loc) · 3.31 KB
/
DiskLogListener.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace BepInEx.Logging;
/// <summary>
/// Logs entries using Unity specific outputs.
/// </summary>
public class DiskLogListener : ILogListener
{
public static HashSet<string> BlacklistedSources = new();
/// <summary>
/// Creates a new disk log listener.
/// </summary>
/// <param name="localPath">Path to the log.</param>
/// <param name="displayedLogLevel">Log levels to display.</param>
/// <param name="appendLog">Whether to append logs to an already existing log file.</param>
/// <param name="delayedFlushing">
/// Whether to delay flushing to disk to improve performance. Useful to set this to false
/// when debugging crashes.
/// </param>
/// <param name="fileLimit">Maximum amount of concurrently opened log files. Can help with infinite game boot loops.</param>
public DiskLogListener(string localPath,
LogLevel displayedLogLevel = LogLevel.Info,
bool appendLog = false,
bool delayedFlushing = true,
int fileLimit = 5)
{
DisplayedLogLevel = displayedLogLevel;
var counter = 1;
FileStream fileStream;
while (!Utility.TryOpenFileStream(Path.Combine(Paths.BepInExRootPath, localPath),
appendLog ? FileMode.Append : FileMode.Create, out fileStream,
share: FileShare.Read, access: FileAccess.Write))
{
if (counter == fileLimit)
{
Logger.Log(LogLevel.Error, "Couldn't open a log file for writing. Skipping log file creation");
return;
}
Logger.Log(LogLevel.Warning, $"Couldn't open log file '{localPath}' for writing, trying another...");
localPath = $"LogOutput.{counter++}.log";
}
LogWriter = TextWriter.Synchronized(new StreamWriter(fileStream, Utility.UTF8NoBom));
if (delayedFlushing) FlushTimer = new Timer(o => { LogWriter?.Flush(); }, null, 2000, 2000);
InstantFlushing = !delayedFlushing;
}
/// <summary>
/// Log levels to display.
/// </summary>
public LogLevel DisplayedLogLevel { get; }
/// <summary>
/// Writer for the disk log.
/// </summary>
public TextWriter LogWriter { get; protected set; }
/// <summary>
/// Timer for flushing the logs to a file.
/// </summary>
private Timer FlushTimer { get; }
private bool InstantFlushing { get; }
/// <inheritdoc />
public LogLevel LogLevelFilter => DisplayedLogLevel;
/// <inheritdoc />
public void LogEvent(object sender, LogEventArgs eventArgs)
{
if (LogWriter == null)
return;
if (BlacklistedSources.Contains(eventArgs.Source.SourceName))
return;
LogWriter.WriteLine(eventArgs.ToString());
if (InstantFlushing)
LogWriter.Flush();
}
/// <inheritdoc />
public void Dispose()
{
FlushTimer?.Dispose();
try
{
LogWriter?.Flush();
LogWriter?.Dispose();
}
catch (ObjectDisposedException) { }
}
~DiskLogListener()
{
Dispose();
}
}