diff --git a/Cachet-Monitor/Cachet-Monitor.csproj b/Cachet-Monitor/Cachet-Monitor.csproj index 73d1f50..14324a1 100644 --- a/Cachet-Monitor/Cachet-Monitor.csproj +++ b/Cachet-Monitor/Cachet-Monitor.csproj @@ -4,8 +4,8 @@ Exe netcoreapp3.1 Cachet_Monitor - 2020.2.24.0 - 2020.2.24.0 + 2020.3.4.0 + 2020.3.4.0 diff --git a/Cachet-Monitor/Extensions/Extensions.cs b/Cachet-Monitor/Extensions/Extensions.cs new file mode 100644 index 0000000..f960293 --- /dev/null +++ b/Cachet-Monitor/Extensions/Extensions.cs @@ -0,0 +1,58 @@ +using Cachet_Monitor.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Cachet_Monitor.Extensions +{ + public static class Extensions + { + public static string PrettyLines(this List lines, int padding = 1) + { + int elementCount = lines[0].Length; + int[] maxValues = new int[elementCount]; + + for (int i = 0; i < elementCount; i++) + maxValues[i] = lines.Max(x => x[i].Length) + padding; + + var sb = new StringBuilder(); + bool isFirst = true; + + foreach (var line in lines) + { + if (!isFirst) + sb.AppendLine(); + + isFirst = false; + + for (int i = 0; i < line.Length; i++) + { + var value = line[i]; + sb.Append(value.PadRight(maxValues[i])); + } + } + return Convert.ToString(sb); + } + + public static ConsoleColor SeverityToColor(this LogSeverity sev) + { + switch (sev) + { + case LogSeverity.Critical: + return ConsoleColor.Red; + case LogSeverity.Error: + return ConsoleColor.Red; + case LogSeverity.Info: + return ConsoleColor.Green; + case LogSeverity.Warning: + return ConsoleColor.Yellow; + case LogSeverity.Verbose: + return ConsoleColor.Cyan; + + default: + return ConsoleColor.White; + } + } + } +} diff --git a/Cachet-Monitor/Log.cs b/Cachet-Monitor/Log.cs new file mode 100644 index 0000000..7a76e6a --- /dev/null +++ b/Cachet-Monitor/Log.cs @@ -0,0 +1,262 @@ +using Cachet_Monitor.Extensions; +using Cachet_Monitor.Models; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace Cachet_Monitor +{ + public class Log + { + public readonly static string CurrentLogFileName = DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log"; + + private static StreamWriter LogFile; + private static Configuration Config; + + public static void Configure(Configuration config) + { + Config = config; + + var path = Path.Combine(Environment.CurrentDirectory, "logs"); + + if (Config.LogToFile && !Directory.Exists(path)) + Directory.CreateDirectory(path); + + if (LogFile == null && Directory.Exists(path)) + { + LogFile = new StreamWriter( + File.Open( + Path.Combine(path, CurrentLogFileName), + FileMode.Append, + FileAccess.Write, + FileShare.Read) + ) + { + AutoFlush = true, + NewLine = "\n" + }; + } + } + + private static string Message(string source, string message, LogSeverity severity) + { + var lines = new List + { + new[] + { + string.Format("{0:dd/MM/yyyy HH:mm:ss}", DateTime.UtcNow), + "[" + source + "]", + "[" + severity.ToString()[0] + "]", + message??"" + } + }; + + var prettied = lines.PrettyLines(2); + + Console.ForegroundColor = severity.SeverityToColor(); + + return prettied; + } + + public static void Critical(string source, string message, Exception exception = null) + { + var msg = Message(source, message, LogSeverity.Critical); + + if (exception != null) + { + var m = msg + "EXTRA INFORMATION:\n" + exception.ToString(); + + if (LogFile != null) + { + LogFile.WriteLine(m); + } + + if (Config.LogLevel >= LogSeverity.Critical) + Console.Out.WriteLine(m); + } + else + { + if (LogFile != null) + { + LogFile.WriteLine(msg); + } + + if (Config.LogLevel >= LogSeverity.Critical) + Console.Out.WriteLine(msg); + } + + Console.ForegroundColor = ConsoleColor.White; + + if (LogFile != null) + { + LogFile.Flush(); + } + } + + public static void Debug(string source, string message, Exception exception = null) + { + var msg = Message(source, message, LogSeverity.Debug); + + if (exception != null) + { + var m = msg + "EXTRA INFORMATION:\n" + exception.ToString(); + + if (Config.LogLevel >= LogSeverity.Debug) + { + Console.Out.WriteLine(m); + + if (LogFile != null) + { + LogFile.WriteLine(m); + } + } + } + else + { + if (Config.LogLevel >= LogSeverity.Debug) + { + Console.Out.WriteLine(msg); + + if (LogFile != null) + { + LogFile.WriteLine(msg); + } + } + } + + Console.ForegroundColor = ConsoleColor.White; + + if (LogFile != null) + { + LogFile.Flush(); + } + } + + public static void Error(string source, string message, Exception exception = null) + { + var msg = Message(source, message, LogSeverity.Error); + + if (exception != null) + { + var m = msg + "EXTRA INFORMATION:\n" + exception.ToString(); + + if (LogFile != null) + { + LogFile.WriteLine(m); + } + } + else + { + if (LogFile != null) + { + LogFile.WriteLine(msg); + } + } + + if (Config.LogLevel >= LogSeverity.Error) + Console.Out.WriteLine(msg); + + Console.ForegroundColor = ConsoleColor.White; + + if (LogFile != null) + { + LogFile.Flush(); + } + } + + public static void Verbose(string source, string message, Exception exception = null) + { + var msg = Message(source, message, LogSeverity.Verbose); + + if (Config.LogLevel >= LogSeverity.Verbose) + { + Console.Out.WriteLine(msg); + + if (exception != null) + { + var m = msg + "EXTRA INFORMATION:\n" + exception.ToString(); + + if (LogFile != null) + { + LogFile.WriteLine(m); + } + } + else + { + if (LogFile != null) + { + LogFile.WriteLine(msg); + } + } + } + + Console.ForegroundColor = ConsoleColor.White; + + if (LogFile != null) + { + LogFile.Flush(); + } + } + + public static void Warning(string source, string message, Exception exception = null) + { + var msg = Message(source, message, LogSeverity.Warning); + + if (exception != null) + { + var m = msg + "EXTRA INFORMATION:\n" + exception.ToString(); + + if (LogFile != null) + { + LogFile.WriteLine(m); + } + } + else + { + if (LogFile != null) + { + LogFile.WriteLine(msg); + } + } + + if (Config.LogLevel >= LogSeverity.Warning) + Console.Out.WriteLine(msg); + + Console.ForegroundColor = ConsoleColor.White; + + if (LogFile != null) + { + LogFile.Flush(); + } + } + + public static void Info(string source, string message) + { + var msg = Message(source, message, LogSeverity.Info); + + if (LogFile != null) + { + LogFile.WriteLine(msg); + } + + if (Config.LogLevel >= LogSeverity.Info) + Console.Out.WriteLine(msg); + + Console.ForegroundColor = ConsoleColor.White; + + if (LogFile != null) + { + LogFile.Flush(); + } + } + + public static void FlushNewLine() + { + LogFile.WriteLine("-------------------------------------------"); + + LogFile.Close(); + } + } +} diff --git a/Cachet-Monitor/Models/Configuration/Configuration.cs b/Cachet-Monitor/Models/Configuration/Configuration.cs index ce41d5a..d20c116 100644 --- a/Cachet-Monitor/Models/Configuration/Configuration.cs +++ b/Cachet-Monitor/Models/Configuration/Configuration.cs @@ -8,6 +8,8 @@ namespace Cachet_Monitor.Models { public Uri ApiBase { get; set; } public string ApiKey { get; set; } + public LogSeverity LogLevel { get; set; } + public bool LogToFile { get; set; } public List Monitors { get; set; } @@ -15,6 +17,8 @@ namespace Cachet_Monitor.Models { ApiBase = new Uri("https://domain.example.com"), ApiKey = "AAABBBCCC11223344556", + LogLevel = LogSeverity.Info, + LogToFile = true, Monitors = new List { new Monitor diff --git a/Cachet-Monitor/Models/LogSeverity.cs b/Cachet-Monitor/Models/LogSeverity.cs new file mode 100644 index 0000000..5cd12d4 --- /dev/null +++ b/Cachet-Monitor/Models/LogSeverity.cs @@ -0,0 +1,30 @@ +namespace Cachet_Monitor.Models +{ + public enum LogSeverity + { + /// + /// Logs that contain the most severe level of error. This type of error indicate that immediate attention may be required. + /// + Critical = 0, + /// + /// Logs that highlight when the flow of execution is stopped due to a failure. + /// + Error = 1, + /// + /// Logs that highlight an abnormal activity in the flow of execution. + /// + Warning = 2, + /// + /// Logs that track the general flow of the application. + /// + Info = 3, + /// + /// Logs that are used for interactive investigation during development. + /// + Verbose = 4, + /// + /// Logs that contain the most detailed messages. + /// + Debug = 5 + } +} diff --git a/Cachet-Monitor/Program.cs b/Cachet-Monitor/Program.cs index 593a09b..9fe3123 100644 --- a/Cachet-Monitor/Program.cs +++ b/Cachet-Monitor/Program.cs @@ -39,6 +39,8 @@ namespace Cachet_Monitor } } + Log.Configure(Configuration); + Cachet = new CachetClient(Configuration.ApiBase.OriginalString, Configuration.ApiKey); MainAsync().GetAwaiter().GetResult(); @@ -53,7 +55,14 @@ namespace Cachet_Monitor Thread.CurrentThread.IsBackground = true; while (true) { - await DoMonitorCheck(Cachet, monitor).ConfigureAwait(false); + try + { + await DoMonitorCheck(Cachet, monitor).ConfigureAwait(false); + } + catch (Exception ex) + { + File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "lastexception.txt"), ex.ToString()); + } await Task.Delay(monitor.Interval * 1000); } @@ -70,22 +79,38 @@ namespace Cachet_Monitor { case MonitorType.PORT: { - using (TcpClient tcpClient = new TcpClient()) + using TcpClient tcpClient = new TcpClient(); + try { + tcpClient.Connect(monitor.Target, monitor.Settings.Port); try { - tcpClient.Connect(monitor.Target, monitor.Settings.Port); await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent { Status = ComponentStatus.Operational, }); + Log.Verbose("PortMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex) + { + Log.Error("PortMonitorCheck", ex.Message, ex); } - catch (Exception) + } + catch (Exception ex) + { + Log.Error("PortMonitorCheck", ex.Message, ex); + + try { await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent { Status = ComponentStatus.MajorOutage, }); + Log.Verbose("PortMonitorCheck", "Sent to Cachet successfully"); + } + catch(Exception ex2) + { + Log.Error("PortMonitorCheck", ex2.Message, ex2); } } } @@ -102,42 +127,82 @@ namespace Cachet_Monitor PingReply reply = ping.Send(monitor.Target, monitor.Timeout, null, options); - await Cachet.AddMetricPointAsync(monitor.MetricId, new PostMetricPoint + try { - Value = (int)reply.RoundtripTime - }); + await Cachet.AddMetricPointAsync(monitor.MetricId, new PostMetricPoint + { + Value = (int)reply.RoundtripTime + }); + Log.Verbose("IPMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex) + { + Log.Error("IPMonitorCheck", ex.Message, ex); + } if (reply.Status == IPStatus.Success) { - await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.Operational, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.Operational, + }); + Log.Verbose("IPMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex) + { + Log.Error("IPMonitorCheck", ex.Message, ex); + } } if (reply.RoundtripTime >= monitor.Timeout) { - await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.PerformanceIssues, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.PerformanceIssues, + }); + Log.Verbose("IPMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex) + { + Log.Error("IPMonitorCheck", ex.Message, ex); + } } if (reply.Status != IPStatus.Success) { if (reply.Status == IPStatus.TimedOut) { - await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.PartialOutage, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.PartialOutage, + }); + Log.Verbose("IPMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex) + { + Log.Error("IPMonitorCheck", ex.Message, ex); + } } else { - await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.MajorOutage, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.MajorOutage, + }); + Log.Verbose("IPMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex) + { + Log.Error("IPMonitorCheck", ex.Message, ex); + } } } } @@ -158,17 +223,34 @@ namespace Cachet_Monitor if (response.StatusCode == (HttpStatusCode)monitor.Settings.ExpectedStatusCode) { - var x = await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.Operational, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.Operational, + }); + Log.Verbose("WebMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex) + { + Log.Error("WebMonitorCheck", ex.Message, ex); + } } else { - await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.PartialOutage, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.PartialOutage, + }); + Log.Verbose("WebMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex) + { + Log.Error("WebMonitorCheck", ex.Message, ex); + } + } response.Close(); @@ -176,6 +258,7 @@ namespace Cachet_Monitor catch (WebException ex) { timer.Stop(); + Log.Warning("WebMonitorChcek", ex.Message, ex); if (ex.Status == WebExceptionStatus.ProtocolError) { @@ -183,10 +266,18 @@ namespace Cachet_Monitor { if (response.StatusCode == (HttpStatusCode)monitor.Settings.ExpectedStatusCode) { - await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.Operational, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.Operational, + }); + Log.Verbose("WebMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex2) + { + Log.Error("WebMonitorCheck", ex2.Message, ex2); + } } } } @@ -194,17 +285,33 @@ namespace Cachet_Monitor { if (ex.Status == WebExceptionStatus.Timeout) { - await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.PerformanceIssues, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.PerformanceIssues, + }); + Log.Verbose("WebMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex2) + { + Log.Error("WebMonitorCheck", ex2.Message, ex2); + } } else { - await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + try { - Status = ComponentStatus.MajorOutage, - }); + await Cachet.UpdateComponentAsync(monitor.ComponentId, new PutComponent + { + Status = ComponentStatus.MajorOutage, + }); + Log.Verbose("WebMonitorCheck", "Sent to Cachet successfully"); + } + catch (Exception ex2) + { + Log.Error("WebMonitorCheck", ex2.Message, ex2); + } } } } @@ -217,7 +324,7 @@ namespace Cachet_Monitor break; } - Console.WriteLine($"Ran check on \"{monitor.Target}\" Status: {componentStatus}"); + Log.Verbose("DoMonitorCheck", $"Ran check on \"{monitor.Name}\" Status: {componentStatus}"); } } } \ No newline at end of file