Backport redirection updates (#770)

pull/771/head
Next Turn 2021-01-01 22:37:31 +08:00 committed by GitHub
parent c49d50381e
commit ad5f78b3c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 43 deletions

View File

@ -47,12 +47,11 @@ namespace WinSW
/// <summary> /// <summary>
/// Convenience method to copy stuff from StreamReader to StreamWriter /// Convenience method to copy stuff from StreamReader to StreamWriter
/// </summary> /// </summary>
protected void CopyStream(StreamReader reader, StreamWriter writer) protected void CopyStream(Stream reader, Stream writer)
{ {
string? line; var copy = new StreamCopyOperation(reader, writer);
while ((line = reader.ReadLine()) != null) while (copy.CopyLine() != 0)
{ {
writer.WriteLine(line);
} }
reader.Dispose(); reader.Dispose();
@ -107,8 +106,6 @@ namespace WinSW
} }
} }
protected StreamWriter CreateWriter(FileStream stream) => new(stream) { AutoFlush = true };
protected abstract void LogOutput(StreamReader outputReader); protected abstract void LogOutput(StreamReader outputReader);
protected abstract void LogError(StreamReader errorReader); protected abstract void LogError(StreamReader errorReader);
@ -132,12 +129,12 @@ namespace WinSW
protected override void LogOutput(StreamReader outputReader) protected override void LogOutput(StreamReader outputReader)
{ {
new Thread(() => this.CopyStream(outputReader, this.CreateWriter(new FileStream(this.OutputLogFileName, this.FileMode)))).Start(); new Thread(() => this.CopyStream(outputReader.BaseStream, new FileStream(this.OutputLogFileName, this.FileMode))).Start();
} }
protected override void LogError(StreamReader errorReader) protected override void LogError(StreamReader errorReader)
{ {
new Thread(() => this.CopyStream(errorReader, this.CreateWriter(new FileStream(this.ErrorLogFileName, this.FileMode)))).Start(); new Thread(() => this.CopyStream(errorReader.BaseStream, new FileStream(this.ErrorLogFileName, this.FileMode))).Start();
} }
} }
@ -203,17 +200,15 @@ namespace WinSW
var periodicRollingCalendar = new PeriodicRollingCalendar(this.Pattern, this.Period); var periodicRollingCalendar = new PeriodicRollingCalendar(this.Pattern, this.Period);
periodicRollingCalendar.Init(); periodicRollingCalendar.Init();
var writer = this.CreateWriter(new FileStream(this.BaseLogFileName + "_" + periodicRollingCalendar.Format + ext, FileMode.Append)); var writer = new FileStream(this.BaseLogFileName + "_" + periodicRollingCalendar.Format + ext, FileMode.Append);
string? line; var copy = new StreamCopyOperation(reader.BaseStream, writer);
while ((line = reader.ReadLine()) != null) while (copy.CopyLine() != 0)
{ {
if (periodicRollingCalendar.ShouldRoll) if (periodicRollingCalendar.ShouldRoll)
{ {
writer.Dispose(); writer.Dispose();
writer = this.CreateWriter(new FileStream(this.BaseLogFileName + "_" + periodicRollingCalendar.Format + ext, FileMode.Create)); copy.Writer = writer = new FileStream(this.BaseLogFileName + "_" + periodicRollingCalendar.Format + ext, FileMode.Create);
} }
writer.WriteLine(line);
} }
reader.Dispose(); reader.Dispose();
@ -228,14 +223,14 @@ namespace WinSW
public static int DefaultSizeThreshold = 10 * BytesPerMB; // roll every 10MB. public static int DefaultSizeThreshold = 10 * BytesPerMB; // roll every 10MB.
public static int DefaultFilesToKeep = 8; public static int DefaultFilesToKeep = 8;
public int SizeTheshold { get; private set; } public int SizeThreshold { get; private set; }
public int FilesToKeep { get; private set; } public int FilesToKeep { get; private set; }
public SizeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, int sizeThreshold, int filesToKeep) public SizeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, int sizeThreshold, int filesToKeep)
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern) : base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
{ {
this.SizeTheshold = sizeThreshold; this.SizeThreshold = sizeThreshold;
this.FilesToKeep = filesToKeep; this.FilesToKeep = filesToKeep;
} }
@ -259,20 +254,21 @@ namespace WinSW
/// </summary> /// </summary>
private void CopyStreamWithRotation(StreamReader reader, string ext) private void CopyStreamWithRotation(StreamReader reader, string ext)
{ {
var writer = this.CreateWriter(new FileStream(this.BaseLogFileName + ext, FileMode.Append)); var writer = new FileStream(this.BaseLogFileName + ext, FileMode.Append);
var copy = new StreamCopyOperation(reader.BaseStream, writer);
long fileLength = new FileInfo(this.BaseLogFileName + ext).Length; long fileLength = new FileInfo(this.BaseLogFileName + ext).Length;
string? line; int written;
while ((line = reader.ReadLine()) != null) while ((written = copy.CopyLine()) != 0)
{ {
int lengthToWrite = Encoding.UTF8.GetByteCount(line) + 2; // CRLF fileLength += written;
if (fileLength + lengthToWrite > this.SizeTheshold) if (fileLength > this.SizeThreshold)
{ {
writer.Dispose(); writer.Dispose();
try try
{ {
for (int j = this.FilesToKeep; j >= 1; j--) for (int j = this.FilesToKeep; j >= 2; j--)
{ {
string dst = this.BaseLogFileName + "." + (j - 1) + ext; string dst = this.BaseLogFileName + "." + (j - 1) + ext;
string src = this.BaseLogFileName + "." + (j - 2) + ext; string src = this.BaseLogFileName + "." + (j - 2) + ext;
@ -296,12 +292,9 @@ namespace WinSW
// even if the log rotation fails, create a new one, or else // even if the log rotation fails, create a new one, or else
// we'll infinitely try to roll. // we'll infinitely try to roll.
writer = this.CreateWriter(new FileStream(this.BaseLogFileName + ext, FileMode.Create)); copy.Writer = writer = new FileStream(this.BaseLogFileName + ext, FileMode.Create);
fileLength = new FileInfo(this.BaseLogFileName + ext).Length; fileLength = new FileInfo(this.BaseLogFileName + ext).Length;
} }
writer.WriteLine(line);
fileLength += lengthToWrite;
} }
reader.Dispose(); reader.Dispose();
@ -339,7 +332,7 @@ namespace WinSW
{ {
public static int BytesPerKB = 1024; public static int BytesPerKB = 1024;
public int SizeTheshold { get; private set; } public int SizeThreshold { get; private set; }
public string FilePattern { get; private set; } public string FilePattern { get; private set; }
@ -363,7 +356,7 @@ namespace WinSW
string zipdateformat) string zipdateformat)
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern) : base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
{ {
this.SizeTheshold = sizeThreshold; this.SizeThreshold = sizeThreshold;
this.FilePattern = filePattern; this.FilePattern = filePattern;
this.AutoRollAtTime = autoRollAtTime; this.AutoRollAtTime = autoRollAtTime;
this.ZipOlderThanNumDays = zipolderthannumdays; this.ZipOlderThanNumDays = zipolderthannumdays;
@ -383,13 +376,14 @@ namespace WinSW
private void CopyStreamWithRotation(StreamReader reader, string extension) private void CopyStreamWithRotation(StreamReader reader, string extension)
{ {
// lock required as the timer thread and the thread that will write to the stream could try and access the file stream at the same time // lock required as the timer thread and the thread that will write to the stream could try and access the file stream at the same time
object fileLock = new object(); object fileLock = new();
string baseDirectory = Path.GetDirectoryName(this.BaseLogFileName)!; string baseDirectory = Path.GetDirectoryName(this.BaseLogFileName)!;
string baseFileName = Path.GetFileName(this.BaseLogFileName); string baseFileName = Path.GetFileName(this.BaseLogFileName);
string logFile = this.BaseLogFileName + extension; string logFile = this.BaseLogFileName + extension;
var writer = this.CreateWriter(new FileStream(logFile, FileMode.Append)); var writer = new FileStream(logFile, FileMode.Append);
var copy = new StreamCopyOperation(reader.BaseStream, writer);
long fileLength = new FileInfo(logFile).Length; long fileLength = new FileInfo(logFile).Length;
// We auto roll at time is configured then we need to create a timer and wait until time is elasped and roll the file over // We auto roll at time is configured then we need to create a timer and wait until time is elasped and roll the file over
@ -412,7 +406,7 @@ namespace WinSW
string nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(this.FilePattern), nextFileNumber, extension)); string nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(this.FilePattern), nextFileNumber, extension));
File.Move(logFile, nextFileName); File.Move(logFile, nextFileName);
writer = this.CreateWriter(new FileStream(logFile, FileMode.Create)); copy.Writer = writer = new FileStream(logFile, FileMode.Create);
fileLength = new FileInfo(logFile).Length; fileLength = new FileInfo(logFile).Length;
} }
@ -433,13 +427,13 @@ namespace WinSW
timer.Start(); timer.Start();
} }
string? line; int written;
while ((line = reader.ReadLine()) != null) while ((written = copy.CopyLine()) != 0)
{ {
lock (fileLock) lock (fileLock)
{ {
int lengthToWrite = Encoding.UTF8.GetByteCount(line) + 2; // CRLF fileLength += written;
if (fileLength + lengthToWrite > this.SizeTheshold) if (fileLength > this.SizeThreshold)
{ {
try try
{ {
@ -453,7 +447,7 @@ namespace WinSW
// even if the log rotation fails, create a new one, or else // even if the log rotation fails, create a new one, or else
// we'll infinitely try to roll. // we'll infinitely try to roll.
writer = this.CreateWriter(new FileStream(logFile, FileMode.Create)); copy.Writer = writer = new FileStream(logFile, FileMode.Create);
fileLength = new FileInfo(logFile).Length; fileLength = new FileInfo(logFile).Length;
} }
catch (Exception e) catch (Exception e)
@ -461,9 +455,6 @@ namespace WinSW
this.EventLogger.LogEvent($"Failed to roll size time log: {e.Message}"); this.EventLogger.LogEvent($"Failed to roll size time log: {e.Message}");
} }
} }
writer.WriteLine(line);
fileLength += lengthToWrite;
} }
} }
@ -618,4 +609,70 @@ namespace WinSW
return nextFileNumber; return nextFileNumber;
} }
} }
internal sealed class StreamCopyOperation
{
private const int BufferSize = 1024;
private readonly byte[] buffer;
private readonly Stream reader;
private int startIndex;
private int endIndex;
internal Stream Writer;
internal StreamCopyOperation(Stream reader, Stream writer)
{
this.buffer = new byte[BufferSize];
this.reader = reader;
this.startIndex = 0;
this.endIndex = 0;
this.Writer = writer;
}
internal int CopyLine()
{
byte[] buffer = this.buffer;
var source = this.reader;
int startIndex = this.startIndex;
int endIndex = this.endIndex;
var destination = this.Writer;
int total = 0;
while (true)
{
if (startIndex == 0)
{
if ((endIndex = source.Read(buffer, 0, BufferSize)) == 0)
{
break;
}
}
int buffered = endIndex - startIndex;
int newLineIndex = Array.IndexOf(buffer, (byte)'\n', startIndex, buffered);
if (newLineIndex >= 0)
{
int count = newLineIndex - startIndex + 1;
total += count;
destination.Write(buffer, startIndex, count);
destination.Flush();
startIndex = (newLineIndex + 1) % BufferSize;
break;
}
total += buffered;
destination.Write(buffer, startIndex, buffered);
destination.Flush();
startIndex = 0;
}
this.startIndex = startIndex;
this.endIndex = endIndex;
return total;
}
}
} }

View File

@ -246,7 +246,7 @@ $@"<service>
var logHandler = serviceDescriptor.Log.CreateLogHandler() as SizeBasedRollingLogAppender; var logHandler = serviceDescriptor.Log.CreateLogHandler() as SizeBasedRollingLogAppender;
Assert.That(logHandler, Is.Not.Null); Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(112 * 1024)); Assert.That(logHandler.SizeThreshold, Is.EqualTo(112 * 1024));
Assert.That(logHandler.FilesToKeep, Is.EqualTo(113)); Assert.That(logHandler.FilesToKeep, Is.EqualTo(113));
} }
@ -287,7 +287,7 @@ $@"<service>
var logHandler = serviceDescriptor.Log.CreateLogHandler() as RollingSizeTimeLogAppender; var logHandler = serviceDescriptor.Log.CreateLogHandler() as RollingSizeTimeLogAppender;
Assert.That(logHandler, Is.Not.Null); Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(10240 * 1024)); Assert.That(logHandler.SizeThreshold, Is.EqualTo(10240 * 1024));
Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd")); Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd"));
Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0, 0, 0))); Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0, 0, 0)));
} }

View File

@ -276,7 +276,7 @@ log:
var logHandler = serviceDescriptor.Log.CreateLogHandler() as SizeBasedRollingLogAppender; var logHandler = serviceDescriptor.Log.CreateLogHandler() as SizeBasedRollingLogAppender;
Assert.That(logHandler, Is.Not.Null); Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(112 * 1024)); Assert.That(logHandler.SizeThreshold, Is.EqualTo(112 * 1024));
Assert.That(logHandler.FilesToKeep, Is.EqualTo(113)); Assert.That(logHandler.FilesToKeep, Is.EqualTo(113));
} }
@ -325,7 +325,7 @@ log:
var logHandler = serviceDescriptor.Log.CreateLogHandler() as RollingSizeTimeLogAppender; var logHandler = serviceDescriptor.Log.CreateLogHandler() as RollingSizeTimeLogAppender;
Assert.That(logHandler, Is.Not.Null); Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(10240 * 1024)); Assert.That(logHandler.SizeThreshold, Is.EqualTo(10240 * 1024));
Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd")); Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd"));
Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0, 0, 0))); Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0, 0, 0)));
} }