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
"); + + //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("
TimestampDateTimeLevelTypeMessage
"); + } + + //Delete the temporary file + File.Delete(Options.LogFilePath + ".tmp"); + } } }