From 221d30f2719163eba39128dc5cbebc38fb01e3df Mon Sep 17 00:00:00 2001 From: Dinz Date: Mon, 18 Sep 2017 17:22:01 +0100 Subject: [PATCH] Introduced the following new elements. (#247) * Introduced the following new elements. 1. logname - you can override the name of the log file rather than using the EXE name, this means you don't have to call your EXE a different name, just name the winsw exe different. Default's the name to the EXE as before. 2. outfiledisabled - you can disable writing to the out file. Defaults to false. 3. errfiledisabled - you can disable writing to the error file. Defaults to false. 4. outfilepattern - you can choose the pattern of the out file. Defaults to .out.log. 5. errfilepattern - you can choos the pattern of the error file. Defaults to .err.log. * Downgraded from C#7.0 syntax. * Applied reviewers comment * not required * removed the key * Added unit test for new fields logname, outfiledisabled, errfiledisabled and errfilepattern. Created a new appender called roll-by-size-time see class RollingSizeTimeLogAppender, this appender supports rolling by time and size and rolling at a specific time each day. Added unit test for the new appender. Added a new option testwait which is similar to test but waits for the user to press any key before calling the stop method. * Update loggingAndErrorReporting.md * Cannot use $ string.format syntax, downgraded code to string.format. * Another syntax found of $ * Fixed a unit tests --- .gitignore | 2 + doc/loggingAndErrorReporting.md | 15 ++ src/Core/ServiceWrapper/Main.cs | 12 +- .../winsw_dotNET4.csproj | 1 - .../Configuration/DefaultSettings.cs | 5 + src/Core/WinSWCore/LogAppenders.cs | 229 ++++++++++++++++-- src/Core/WinSWCore/ServiceDescriptor.cs | 89 ++++++- src/Test/winswTests/ServiceDescriptorTests.cs | 81 ++++++- 8 files changed, 398 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index ea56750..beb12a5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ obj /src/packages/ /winsw_key.snk /winsw_key.pfx +/src/.vs/winsw/v15/sqlite3/storage.ide +/src/Core/WinSWCore/WinSWCore.csproj.DotSettings diff --git a/doc/loggingAndErrorReporting.md b/doc/loggingAndErrorReporting.md index d6b7b92..477faa4 100644 --- a/doc/loggingAndErrorReporting.md +++ b/doc/loggingAndErrorReporting.md @@ -48,7 +48,22 @@ This configuration must accompany a nested `` element, which specifies The syntax of the pattern string is specified by [DateTime.ToString()](http://msdn.microsoft.com/en-us/library/zdtaw1bw.aspx). For example, in the above example, the log of Jan 1, 2013 gets written to `myapp.20130101.out.log` and `myapp.20130101.err.log`. +### Rotate by size and time mode +Works in a combination of rotate size mode and rotate time mode, if the log file gets bigger than a set size, it gets rotated using `` provided. +``` + + 10240 + yyyyMMdd + 00:00:00 + +``` + +The syntax of the pattern string is specified by [DateTime.ToString()](http://msdn.microsoft.com/en-us/library/zdtaw1bw.aspx). +For example, in the above example, the log of Jan 1, 2013 gets written to `myapp.20130101.out.log` and `myapp.20130101.err.log`. + +The syntax of the autoRollAtTime is specified by [TimeSpan.ToString()](https://msdn.microsoft.com/en-us/library/1ecy8h51(v=vs.110).aspx). +For example, in the above example, at the start of the day it will roll the file over. ### Error reporting diff --git a/src/Core/ServiceWrapper/Main.cs b/src/Core/ServiceWrapper/Main.cs index 8dc99d4..1dbe608 100644 --- a/src/Core/ServiceWrapper/Main.cs +++ b/src/Core/ServiceWrapper/Main.cs @@ -737,6 +737,15 @@ namespace winsw wsvc.OnStop(); return; } + if (args[0] == "testwait") + { + WrapperService wsvc = new WrapperService(); + wsvc.OnStart(args.ToArray()); + Console.WriteLine("Press any key to stop the service..."); + Console.Read(); + wsvc.OnStop(); + return; + } if (args[0] == "help" || args[0] == "--help" || args[0] == "-h" || args[0] == "-?" || args[0] == "/?") { @@ -869,7 +878,8 @@ namespace winsw Console.WriteLine("- 'restart' - restart the service"); Console.WriteLine("- 'restart!' - self-restart (can be called from child processes)"); Console.WriteLine("- 'status' - check the current status of the service"); - Console.WriteLine("- 'test' - check if the service can be started and then stopped"); + Console.WriteLine("- 'test' - check if the service can be started and then stopped"); + Console.WriteLine("- 'testwait' - starts the service and waits until a key is pressed then stops the service"); Console.WriteLine("- 'version' - print the version info"); Console.WriteLine("- 'help' - print the help info (aliases: -h,--help,-?,/?)"); } diff --git a/src/Core/ServiceWrapper_dotNET4/winsw_dotNET4.csproj b/src/Core/ServiceWrapper_dotNET4/winsw_dotNET4.csproj index 88303bd..3d5f033 100644 --- a/src/Core/ServiceWrapper_dotNET4/winsw_dotNET4.csproj +++ b/src/Core/ServiceWrapper_dotNET4/winsw_dotNET4.csproj @@ -79,7 +79,6 @@ - Designer diff --git a/src/Core/WinSWCore/Configuration/DefaultSettings.cs b/src/Core/WinSWCore/Configuration/DefaultSettings.cs index 596f153..bd74a38 100644 --- a/src/Core/WinSWCore/Configuration/DefaultSettings.cs +++ b/src/Core/WinSWCore/Configuration/DefaultSettings.cs @@ -59,6 +59,11 @@ namespace winsw.Configuration public string LogDirectory { get { return Path.GetDirectoryName(ExecutablePath); } } public string LogMode { get { return "append"; } } + public bool OutFileDisabled { get { return false; } } + public bool ErrFileDisabled { get { return false; } } + public string OutFilePattern { get { return ".out.log"; } } + public string ErrFilePattern { get { return ".err.log"; } } + // Environment public List Downloads { get { return new List(); } } public Dictionary EnvironmentVariables { get { return new Dictionary(); } } diff --git a/src/Core/WinSWCore/LogAppenders.cs b/src/Core/WinSWCore/LogAppenders.cs index 21304e3..fd88073 100644 --- a/src/Core/WinSWCore/LogAppenders.cs +++ b/src/Core/WinSWCore/LogAppenders.cs @@ -1,6 +1,8 @@ +using System; using System.Diagnostics; using System.IO; using System.Threading; +using System.Timers; namespace winsw { @@ -64,10 +66,18 @@ namespace winsw public abstract class AbstractFileLogAppender : LogHandler { protected string BaseLogFileName { private set; get; } + protected bool OutFileDisabled { private set; get; } + protected bool ErrFileDisabled { private set; get; } + protected string OutFilePattern { private set; get; } + protected string ErrFilePattern { private set; get; } - public AbstractFileLogAppender(string logDirectory, string baseName) + protected AbstractFileLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern) { BaseLogFileName = Path.Combine(logDirectory, baseName); + OutFileDisabled = outFileDisabled; + OutFilePattern = outFilePattern; + ErrFileDisabled = errFileDisabled; + ErrFilePattern = errFilePattern; } } @@ -77,8 +87,8 @@ namespace winsw public string OutputLogFileName { private set; get; } public string ErrorLogFileName { private set; get; } - public SimpleLogAppender(string logDirectory, string baseName, FileMode fileMode) - : base(logDirectory, baseName) + protected SimpleLogAppender(string logDirectory, string baseName, FileMode fileMode, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern) + : base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern) { FileMode = fileMode; OutputLogFileName = BaseLogFileName + ".out.log"; @@ -87,23 +97,23 @@ namespace winsw public override void log(Stream outputStream, Stream errorStream) { - new Thread(delegate() { CopyStream(outputStream, new FileStream(OutputLogFileName, FileMode)); }).Start(); - new Thread(delegate() { CopyStream(errorStream, new FileStream(ErrorLogFileName, FileMode)); }).Start(); + if (!OutFileDisabled) new Thread(delegate() { CopyStream(outputStream, new FileStream(OutputLogFileName, FileMode)); }).Start(); + if (!ErrFileDisabled) new Thread(delegate() { CopyStream(errorStream, new FileStream(ErrorLogFileName, FileMode)); }).Start(); } } public class DefaultLogAppender : SimpleLogAppender { - public DefaultLogAppender(string logDirectory, string baseName) - : base(logDirectory, baseName, FileMode.Append) + public DefaultLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern) + : base(logDirectory, baseName, FileMode.Append, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern) { } } public class ResetLogAppender : SimpleLogAppender { - public ResetLogAppender(string logDirectory, string baseName) - : base(logDirectory, baseName, FileMode.Create) + public ResetLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern) + : base(logDirectory, baseName, FileMode.Create, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern) { } } @@ -125,8 +135,8 @@ namespace winsw public string Pattern { get; private set; } public int Period { get; private set; } - public TimeBasedRollingLogAppender(string logDirectory, string baseName, string pattern, int period) - : base(logDirectory, baseName) + public TimeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, string pattern, int period) + : base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern) { Pattern = pattern; Period = period; @@ -134,8 +144,8 @@ namespace winsw public override void log(Stream outputStream, Stream errorStream) { - new Thread(delegate() { CopyStreamWithDateRotation(outputStream, ".out.log"); }).Start(); - new Thread(delegate() { CopyStreamWithDateRotation(errorStream, ".err.log"); }).Start(); + if (!OutFileDisabled) new Thread(delegate() { CopyStreamWithDateRotation(outputStream, OutFilePattern); }).Start(); + if (!ErrFileDisabled) new Thread(delegate() { CopyStreamWithDateRotation(errorStream, ErrFilePattern); }).Start(); } /// @@ -213,20 +223,20 @@ namespace winsw public int FilesToKeep { private set; get; } - public SizeBasedRollingLogAppender(string logDirectory, string baseName, int sizeThreshold, int filesToKeep) - : base(logDirectory, baseName) + 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) { SizeTheshold = sizeThreshold; FilesToKeep = filesToKeep; } - public SizeBasedRollingLogAppender(string logDirectory, string baseName) - : this(logDirectory, baseName, DEFAULT_SIZE_THRESHOLD, DEFAULT_FILES_TO_KEEP) { } + public SizeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern) + : this(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern, DEFAULT_SIZE_THRESHOLD, DEFAULT_FILES_TO_KEEP) { } public override void log(Stream outputStream, Stream errorStream) { - new Thread(delegate() { CopyStreamWithRotation(outputStream, ".out.log"); }).Start(); - new Thread(delegate() { CopyStreamWithRotation(errorStream, ".err.log"); }).Start(); + if (!OutFileDisabled) new Thread(delegate() { CopyStreamWithRotation(outputStream, OutFilePattern); }).Start(); + if (!ErrFileDisabled) new Thread(delegate() { CopyStreamWithRotation(errorStream, ErrFilePattern); }).Start(); } /// @@ -299,16 +309,189 @@ namespace winsw /// public class RollingLogAppender : SimpleLogAppender { - public RollingLogAppender(string logDirectory, string baseName) - : base(logDirectory, baseName, FileMode.Append) + public RollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern) + : base(logDirectory, baseName, FileMode.Append, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern) { } public override void log(Stream outputStream, Stream errorStream) { - CopyFile(OutputLogFileName, OutputLogFileName + ".old"); - CopyFile(ErrorLogFileName, ErrorLogFileName + ".old"); + if (!OutFileDisabled) CopyFile(OutputLogFileName, OutputLogFileName + ".old"); + if (!ErrFileDisabled) CopyFile(ErrorLogFileName, ErrorLogFileName + ".old"); base.log(outputStream, errorStream); } } + + public class RollingSizeTimeLogAppender : AbstractFileLogAppender + { + public static int BYTES_PER_KB = 1024; + public int SizeTheshold { private set; get; } + public string FilePattern { private set; get; } + public TimeSpan? AutoRollAtTime { private set; get; } + + public RollingSizeTimeLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, int sizeThreshold, string filePattern, TimeSpan? autoRollAtTime) + : base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern) + { + SizeTheshold = sizeThreshold; + FilePattern = filePattern; + AutoRollAtTime = autoRollAtTime; + } + + public override void log(Stream outputStream, Stream errorStream) + { + if (!OutFileDisabled) new Thread(delegate () { CopyStreamWithRotation(outputStream, OutFilePattern); }).Start(); + if (!ErrFileDisabled) new Thread(delegate () { CopyStreamWithRotation(errorStream, ErrFilePattern); }).Start(); + } + + private void CopyStreamWithRotation(Stream data, string ext) + { + // 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 + var fileLock = new object(); + + var buf = new byte[1024]; + + var baseDirectory = Path.GetDirectoryName(BaseLogFileName); + var baseFileName = Path.GetFileName(BaseLogFileName); + var logFile = string.Format("{0}{1}", BaseLogFileName, ext); + + var w = new FileStream(logFile, FileMode.Append); + var sz = 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 + if (AutoRollAtTime != null) + { + var tickTime = SetupRollTimer(); + var timer = new System.Timers.Timer(tickTime); + timer.Elapsed += (s, e) => + { + try + { + timer.Stop(); + lock (fileLock) + { + w.Close(); + + var nextFileNumber = GetNextFileNumber(ext, baseDirectory, baseFileName); + var nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, DateTime.UtcNow.ToString(FilePattern), nextFileNumber, ext)); + File.Move(logFile, nextFileName); + + w = new FileStream(logFile, FileMode.Create); + sz = new FileInfo(logFile).Length; + } + } + catch (Exception et) + { + EventLogger.LogEvent(string.Format("Failed to to trigger auto roll at time event due to: {0}", et.Message)); + } + finally + { + // Recalculate the next interval + timer.Interval = SetupRollTimer(); + timer.Start(); + } + }; + timer.Start(); + } + while (true) + { + var len = data.Read(buf, 0, buf.Length); + if (len == 0) break; // EOF + lock (fileLock) + { + if (sz + len < SizeTheshold) + { + // typical case. write the whole thing into the current file + w.Write(buf, 0, len); + sz += len; + } + else + { + try + { + // rotate at the line boundary + int s = 0; + for (int i = 0; i < len; i++) + { + if (buf[i] != 0x0A) continue; + if (sz + i < SizeTheshold) continue; + + // at the line boundary and exceeded the rotation unit. + // time to rotate. + w.Write(buf, s, i + 1); + w.Close(); + s = i + 1; + + // rotate file + var nextFileNumber = GetNextFileNumber(ext, baseDirectory, baseFileName); + var nextFileName = + Path.Combine(baseDirectory, + string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, DateTime.UtcNow.ToString(FilePattern), nextFileNumber, ext)); + File.Move(logFile, nextFileName); + + // even if the log rotation fails, create a new one, or else + // we'll infinitely try to rotate. + w = new FileStream(logFile, FileMode.Create); + sz = new FileInfo(logFile).Length; + } + } + catch (Exception e) + { + EventLogger.LogEvent(string.Format("Failed to roll size time log: {0}", e.Message)); + } + } + w.Flush(); + } + } + data.Close(); + w.Close(); + } + + private double SetupRollTimer() + { + var nowTime = DateTime.Now.ToUniversalTime(); + var scheduledTime = new DateTime(nowTime.Year, nowTime.Month, nowTime.Day, AutoRollAtTime.Value.Hours, + AutoRollAtTime.Value.Minutes, AutoRollAtTime.Value.Seconds, 0).ToUniversalTime(); //Specify your time HH,MM,SS + if (nowTime > scheduledTime) + scheduledTime = scheduledTime.AddDays(1); + + double tickTime = (double) (scheduledTime - DateTime.Now.ToUniversalTime()).TotalMilliseconds; + return tickTime; + } + + private int GetNextFileNumber(string ext, string baseDirectory, string baseFileName) + { + var nextFileNumber = 0; + var files = Directory.GetFiles(baseDirectory, String.Format("{0}.{1}.#*{2}", baseFileName, DateTime.UtcNow.ToString(FilePattern), ext)); + if (files.Length == 0) + { + nextFileNumber = 1; + } + else + { + foreach (var f in files) + { + try + { + var filenameOnly = Path.GetFileNameWithoutExtension(f); + var lastNumberAsString = filenameOnly.Substring(filenameOnly.Length - 4, 4); + int lastNumber = 0; + if (int.TryParse(lastNumberAsString, out lastNumber)) + { + if (lastNumber > nextFileNumber) + nextFileNumber = lastNumber; + } + else + throw new IOException(string.Format("File {0} does not follow the pattern provided",f)); + } + catch (Exception e) + { + throw new IOException(string.Format("Failed to process file {0} due to error {1}",f, e.Message), e); + } + } + if (nextFileNumber == 0) throw new IOException("Cannot roll the file because matching pattern not found"); + nextFileNumber++; + } + return nextFileNumber; + } + } } diff --git a/src/Core/WinSWCore/ServiceDescriptor.cs b/src/Core/WinSWCore/ServiceDescriptor.cs index 53393c7..1201607 100755 --- a/src/Core/WinSWCore/ServiceDescriptor.cs +++ b/src/Core/WinSWCore/ServiceDescriptor.cs @@ -103,13 +103,20 @@ namespace winsw return SingleElement(tagName, false); } - private string SingleElement(string tagName, Boolean optional) + private string SingleElement(string tagName, bool optional) { var n = dom.SelectSingleNode("//" + tagName); if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML"); return n == null ? null : Environment.ExpandEnvironmentVariables(n.InnerText); } + private bool SingleBoolElement(string tagName, bool defaultValue) + { + var e = dom.SelectSingleNode("//" + tagName); + + return e == null ? defaultValue : bool.Parse(e.InnerText); + } + private int SingleIntElement(XmlNode parent, string tagName, int defaultValue) { var e = parent.SelectSingleNode(tagName); @@ -355,6 +362,49 @@ namespace winsw } } + public string LogName + { + get + { + XmlNode loggingName = dom.SelectSingleNode("//logname"); + + return loggingName != null ? Environment.ExpandEnvironmentVariables(loggingName.InnerText) : BaseName; + } + } + + public bool OutFileDisabled + { + get { return SingleBoolElement("outfiledisabled", Defaults.OutFileDisabled); } + } + + public bool ErrFileDisabled + { + get + { + return SingleBoolElement("errfiledisabled", Defaults.ErrFileDisabled); + } + } + + public string OutFilePattern + { + get + { + XmlNode loggingName = dom.SelectSingleNode("//outfilepattern"); + + return loggingName != null ? Environment.ExpandEnvironmentVariables(loggingName.InnerText) : Defaults.OutFilePattern; + } + } + + public string ErrFilePattern + { + get + { + XmlNode loggingName = dom.SelectSingleNode("//errfilepattern"); + + return loggingName != null ? Environment.ExpandEnvironmentVariables(loggingName.InnerText) : Defaults.ErrFilePattern; + } + } + public LogHandler LogHandler { @@ -366,19 +416,20 @@ namespace winsw // this is more modern way, to support nested elements as configuration e = (XmlElement)dom.SelectSingleNode("//log"); } + int sizeThreshold; switch (LogMode) { case "rotate": - return new SizeBasedRollingLogAppender(LogDirectory, BaseName); + return new SizeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern); case "none": return new IgnoreLogAppender(); case "reset": - return new ResetLogAppender(LogDirectory, BaseName); + return new ResetLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern); case "roll": - return new RollingLogAppender(LogDirectory, BaseName); + return new RollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern); case "roll-by-time": XmlNode patternNode = e.SelectSingleNode("pattern"); @@ -386,17 +437,37 @@ namespace winsw { throw new InvalidDataException("Time Based rolling policy is specified but no pattern can be found in configuration XML."); } - string pattern = patternNode.InnerText; + var pattern = patternNode.InnerText; int period = SingleIntElement(e,"period",1); - return new TimeBasedRollingLogAppender(LogDirectory, BaseName, pattern, period); + return new TimeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern, pattern, period); case "roll-by-size": - int sizeThreshold = SingleIntElement(e,"sizeThreshold",10*1024) * SizeBasedRollingLogAppender.BYTES_PER_KB; + sizeThreshold = SingleIntElement(e,"sizeThreshold",10*1024) * SizeBasedRollingLogAppender.BYTES_PER_KB; int keepFiles = SingleIntElement(e,"keepFiles",SizeBasedRollingLogAppender.DEFAULT_FILES_TO_KEEP); - return new SizeBasedRollingLogAppender(LogDirectory, BaseName, sizeThreshold, keepFiles); + return new SizeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern, sizeThreshold, keepFiles); case "append": - return new DefaultLogAppender(LogDirectory, BaseName); + return new DefaultLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern); + + case "roll-by-size-time": + sizeThreshold = SingleIntElement(e, "sizeThreshold", 10 * 1024) * RollingSizeTimeLogAppender.BYTES_PER_KB; + XmlNode filePatternNode = e.SelectSingleNode("pattern"); + if (filePatternNode == null) + { + throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but no pattern can be found in configuration XML."); + } + XmlNode autoRollAtTimeNode = e.SelectSingleNode("autoRollAtTime"); + TimeSpan? autoRollAtTime = null; + if (autoRollAtTimeNode != null) + { + TimeSpan autoRollAtTimeValue; + // validate it + if (!TimeSpan.TryParse(autoRollAtTimeNode.InnerText, out autoRollAtTimeValue)) + throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but autoRollAtTime does not match the TimeSpan format HH:mm:ss found in configuration XML."); + autoRollAtTime = autoRollAtTimeValue; + } + + return new RollingSizeTimeLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern, sizeThreshold, filePatternNode.InnerText, autoRollAtTime); default: throw new InvalidDataException("Undefined logging mode: " + LogMode); diff --git a/src/Test/winswTests/ServiceDescriptorTests.cs b/src/Test/winswTests/ServiceDescriptorTests.cs index 9e741a8..6dc33ce 100644 --- a/src/Test/winswTests/ServiceDescriptorTests.cs +++ b/src/Test/winswTests/ServiceDescriptorTests.cs @@ -182,8 +182,63 @@ namespace winswTests var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10))); - } - + } + + [Test] + public void CanParseLogname() + { + const string seedXml = "" + + "MyTestApp" + + ""; + var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); + + Assert.That(serviceDescriptor.LogName, Is.EqualTo("MyTestApp")); + } + + [Test] + public void CanParseOutfileDisabled() + { + const string seedXml = "" + + "true" + + ""; + var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); + + Assert.That(serviceDescriptor.OutFileDisabled, Is.EqualTo(true)); + } + + [Test] + public void CanParseErrfileDisabled() + { + const string seedXml = "" + + "true" + + ""; + var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); + + Assert.That(serviceDescriptor.ErrFileDisabled, Is.EqualTo(true)); + } + + [Test] + public void CanParseOutfilePattern() + { + const string seedXml = "" + + ".out.test.log" + + ""; + var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); + + Assert.That(serviceDescriptor.OutFilePattern, Is.EqualTo(".out.test.log")); + } + + [Test] + public void CanParseErrfilePattern() + { + const string seedXml = "" + + ".err.test.log" + + ""; + var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); + + Assert.That(serviceDescriptor.ErrFilePattern, Is.EqualTo(".err.test.log")); + } + [Test] public void LogModeRollBySize() { @@ -224,6 +279,28 @@ namespace winswTests Assert.That(logHandler.Pattern, Is.EqualTo("log pattern")); } + [Test] + public void LogModeRollBySizeTime() + { + const string seedXml = "" + + "c:\\" + + "" + + "10240" + + "yyyy-MM-dd" + + "00:00:00" + + "" + + ""; + + var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); + serviceDescriptor.BaseName = "service"; + + var logHandler = serviceDescriptor.LogHandler as RollingSizeTimeLogAppender; + Assert.NotNull(logHandler); + Assert.That(logHandler.SizeTheshold, Is.EqualTo(10240 * 1024)); + Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd")); + Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0,0,0))); + } + [Test] public void VerifyServiceLogonRightGraceful() {