Skip to content

Commit

Permalink
Introduced the following new elements. (winsw#247)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
DHirani authored and oleg-nenashev committed Sep 18, 2017
1 parent fad8830 commit 221d30f
Show file tree
Hide file tree
Showing 8 changed files with 398 additions and 36 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 15 additions & 0 deletions doc/loggingAndErrorReporting.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,22 @@ This configuration must accompany a nested `<pattern>` 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 `<pattern>` provided.

```
<log mode="roll-by-size-time">
<sizeThreshold>10240</sizeThreshold>
<pattern>yyyyMMdd</pattern>
<autoRollAtTime>00:00:00</autoRollAtTime>
</log>
```

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

Expand Down
12 changes: 11 additions & 1 deletion src/Core/ServiceWrapper/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,15 @@ public static void Run(string[] _args, ServiceDescriptor descriptor = null)
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] == "/?")
{
Expand Down Expand Up @@ -869,7 +878,8 @@ private static void printAvailableCommandsInfo()
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,-?,/?)");
}
Expand Down
1 change: 0 additions & 1 deletion src/Core/ServiceWrapper_dotNET4/winsw_dotNET4.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="manifest.xml" />
<Content Include="winsw.xml">
<SubType>Designer</SubType>
</Content>
Expand Down
5 changes: 5 additions & 0 deletions src/Core/WinSWCore/Configuration/DefaultSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ public string ExecutablePath
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<Download> Downloads { get { return new List<Download>(); } }
public Dictionary<string, string> EnvironmentVariables { get { return new Dictionary<string, string>(); } }
Expand Down
229 changes: 206 additions & 23 deletions src/Core/WinSWCore/LogAppenders.cs

Large diffs are not rendered by default.

89 changes: 80 additions & 9 deletions src/Core/WinSWCore/ServiceDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,20 @@ private string SingleElement(string tagName)
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);
Expand Down Expand Up @@ -355,6 +362,49 @@ public string LogMode
}
}

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
{

Expand All @@ -366,37 +416,58 @@ public LogHandler LogHandler
// 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");
if (patternNode == null)
{
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);
Expand Down
81 changes: 79 additions & 2 deletions src/Test/winswTests/ServiceDescriptorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,63 @@ public void CanParseStopTimeoutFromMinutes()
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);

Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10)));
}

}

[Test]
public void CanParseLogname()
{
const string seedXml = "<service>"
+ "<logname>MyTestApp</logname>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);

Assert.That(serviceDescriptor.LogName, Is.EqualTo("MyTestApp"));
}

[Test]
public void CanParseOutfileDisabled()
{
const string seedXml = "<service>"
+ "<outfiledisabled>true</outfiledisabled>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);

Assert.That(serviceDescriptor.OutFileDisabled, Is.EqualTo(true));
}

[Test]
public void CanParseErrfileDisabled()
{
const string seedXml = "<service>"
+ "<errfiledisabled>true</errfiledisabled>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);

Assert.That(serviceDescriptor.ErrFileDisabled, Is.EqualTo(true));
}

[Test]
public void CanParseOutfilePattern()
{
const string seedXml = "<service>"
+ "<outfilepattern>.out.test.log</outfilepattern>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);

Assert.That(serviceDescriptor.OutFilePattern, Is.EqualTo(".out.test.log"));
}

[Test]
public void CanParseErrfilePattern()
{
const string seedXml = "<service>"
+ "<errfilepattern>.err.test.log</errfilepattern>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);

Assert.That(serviceDescriptor.ErrFilePattern, Is.EqualTo(".err.test.log"));
}

[Test]
public void LogModeRollBySize()
{
Expand Down Expand Up @@ -224,6 +279,28 @@ public void LogModeRollByTime()
Assert.That(logHandler.Pattern, Is.EqualTo("log pattern"));
}

[Test]
public void LogModeRollBySizeTime()
{
const string seedXml = "<service>"
+ "<logpath>c:\\</logpath>"
+ "<log mode=\"roll-by-size-time\">"
+ "<sizeThreshold>10240</sizeThreshold>"
+ "<pattern>yyyy-MM-dd</pattern>"
+ "<autoRollAtTime>00:00:00</autoRollAtTime>"
+ "</log>"
+ "</service>";

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()
{
Expand Down

0 comments on commit 221d30f

Please sign in to comment.