diff --git a/README.md b/README.md
index 490b4c0e..742659dd 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@ The key incantations are:
`-z` Path to a config file that defines all of the above, and much much more! See below for more details. Give it `-z generate` to generate a sample config file called `.\default.toml`.
-`-t` Type of log you would like to output. Currently supported options are plain and JSON. Defaults to plain.
+`-t` Type of log you would like to output. Currently supported options are plain, JSON, and HTML. Defaults to plain.
`-x` Max number of threads to use. Don't set it below 4 or shit will break.
diff --git a/SnaffCore/Concurrency/SnafflerMessageType.cs b/SnaffCore/Concurrency/SnafflerMessageType.cs
index ac3c0a78..c70432b0 100644
--- a/SnaffCore/Concurrency/SnafflerMessageType.cs
+++ b/SnaffCore/Concurrency/SnafflerMessageType.cs
@@ -1,10 +1,15 @@
-namespace SnaffCore.Concurrency
+using System.ComponentModel;
+
+namespace SnaffCore.Concurrency
{
public enum SnafflerMessageType
{
Error,
+ [Description("Share")]
ShareResult,
+ [Description("Dir")]
DirResult,
+ [Description("File")]
FileResult,
Finish,
Info,
diff --git a/SnaffCore/Config/Options.cs b/SnaffCore/Config/Options.cs
index 78c66282..aed95e6c 100644
--- a/SnaffCore/Config/Options.cs
+++ b/SnaffCore/Config/Options.cs
@@ -7,7 +7,8 @@ namespace SnaffCore.Config
public enum LogType
{
Plain = 0,
- JSON = 1
+ JSON = 1,
+ HTML = 2
}
public partial class Options
diff --git a/Snaffler/Config.cs b/Snaffler/Config.cs
index 0a7e9a57..5f66161a 100644
--- a/Snaffler/Config.cs
+++ b/Snaffler/Config.cs
@@ -173,6 +173,10 @@ private static Options ParseImpl(string[] args)
{
parsedConfig.LogType = LogType.JSON;
}
+ else if (logType.Value.ToLower() == "html")
+ {
+ parsedConfig.LogType = LogType.HTML;
+ }
else
{
Mq.Info("Invalid type argument passed (" + logType.Value + ") defaulting to plaintext");
diff --git a/Snaffler/SnaffleRunner.cs b/Snaffler/SnaffleRunner.cs
index 4d01882d..6967a75d 100644
--- a/Snaffler/SnaffleRunner.cs
+++ b/Snaffler/SnaffleRunner.cs
@@ -11,6 +11,7 @@
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Threading;
+using System.ComponentModel;
namespace Snaffler
{
@@ -185,6 +186,10 @@ public void Run(string[] args)
};
logfile.Layout = jsonLayout;
}
+ else if (Options.LogType == LogType.HTML)
+ {
+ logfile.Layout = "
| ${longdate} | ${event-properties:htmlFields:objectPath=DateTime} | ${level} | ${event-properties:htmlFields:objectPath=Type} | ${message} |
";
+ }
}
// Apply config
@@ -247,6 +252,10 @@ private bool HandleOutput()
{
ProcessMessageJSON(message);
}
+ else if (Options.LogType == LogType.HTML)
+ {
+ ProcessMessageHTML(message);
+ }
// catch terminating messages and bail out of the master 'while' loop
if ((message.Type == SnafflerMessageType.Fatal) || (message.Type == SnafflerMessageType.Finish))
@@ -364,6 +373,73 @@ private void ProcessMessageJSON(SnafflerMessage message)
}
}
+ private void ProcessMessageHTML(SnafflerMessage message)
+ {
+ // standardized time formatting, UTC
+ string datetime = String.Format("{1}{0}{2:u}{0}", Options.Separator, hostString(), message.DateTime.ToUniversalTime());
+
+ // get a more user friendly name for a type variant if provided
+ var typeVariantField = message.Type.GetType().GetField(message.Type.ToString());
+ var typeVariantAttribute = (DescriptionAttribute)Attribute.GetCustomAttribute(typeVariantField, typeof(DescriptionAttribute));
+ string userReadableType = typeVariantAttribute == null ? message.Type.ToString() : typeVariantAttribute.Description;
+
+ var htmlFields = new { DateTime = datetime, Type = userReadableType };
+
+ switch (message.Type)
+ {
+ case SnafflerMessageType.Trace:
+ //Logger.Trace(message);
+ Logger.WithProperty("htmlFields", htmlFields).Trace(message.Message);
+ break;
+ case SnafflerMessageType.Degub:
+ //Logger.Debug(message);
+ Logger.WithProperty("htmlFields", htmlFields).Debug(message.Message);
+ break;
+ case SnafflerMessageType.Info:
+ //Logger.Info(message);
+ Logger.WithProperty("htmlFields", htmlFields).Info(message.Message);
+ break;
+ case SnafflerMessageType.FileResult:
+ //Logger.Warn(message);
+ Logger.WithProperty("htmlFields", htmlFields).Warn(FileResultLogFromMessage(message));
+ break;
+ case SnafflerMessageType.DirResult:
+ //Logger.Warn(message);
+ Logger.WithProperty("htmlFields", htmlFields).Warn(DirResultLogFromMessage(message));
+ break;
+ case SnafflerMessageType.ShareResult:
+ //Logger.Warn(message);
+ Logger.WithProperty("htmlFields", htmlFields).Warn(ShareResultLogFromMessage(message));
+ break;
+ case SnafflerMessageType.Error:
+ //Logger.Error(message);
+ Logger.WithProperty("htmlFields", htmlFields).Error(message.Message);
+ break;
+ case SnafflerMessageType.Fatal:
+ //Logger.Fatal(message);
+ Logger.WithProperty("htmlFields", htmlFields).Fatal(message.Message);
+ if (Debugger.IsAttached)
+ {
+ Console.ReadKey();
+ }
+ break;
+ case SnafflerMessageType.Finish:
+ Logger.Info("Snaffler out.");
+
+ if (Debugger.IsAttached)
+ {
+ Console.WriteLine("Press any key to exit.");
+ Console.ReadKey();
+ }
+ if (Options.LogType == LogType.HTML)
+ {
+ Logger.Info("Normalising output, please wait...");
+ FixHTMLOutput();
+ }
+ break;
+ }
+ }
+
public string ShareResultLogFromMessage(SnafflerMessage message)
{
string sharePath = message.ShareResult.SharePath;
@@ -579,5 +655,32 @@ private void FixJSONOutput()
//Delete the temporary file.
File.Delete(Options.LogFilePath + ".tmp");
}
+
+ private void FixHTMLOutput()
+ {
+ //Rename the log file temporarily
+ File.Move(Options.LogFilePath, Options.LogFilePath + ".tmp");
+
+ //Prepare the normalised file
+ using (StreamWriter file = new StreamWriter(Options.LogFilePath))
+ {
+ //Write the start of the surrounding template that we need
+ file.Write("Snaffler Logs| Timestamp | DateTime | Level | Type | Message |
");
+
+ //Open the original file
+ using (FileStream sourceStream = new FileStream(Options.LogFilePath + ".tmp", FileMode.Open, FileAccess.Read))
+ using (StreamReader sourceReader = new StreamReader(sourceStream))
+ {
+ //Write the original content
+ file.Write(sourceReader.ReadToEnd());
+ }
+
+ //Write the end of the surrounding template that we need
+ file.Write("
");
+ }
+
+ //Delete the temporary file
+ File.Delete(Options.LogFilePath + ".tmp");
+ }
}
}