From a82cb82aa6d2d4f7d1ad81d29f79e415394cf61d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Fl=C3=ADdr?= Date: Thu, 26 Sep 2019 10:22:38 +0200 Subject: [PATCH] Release 1.3.0 --- Core/Config.cs | 38 +++++++-- Core/Dispatcher.cs | 8 +- Core/Mailer.cs | 150 +++++++++++++++++++++++---------- Debug.cs | 9 +- README.md | 27 +++--- content/Desharp.config.example | 21 +++-- 6 files changed, 172 insertions(+), 81 deletions(-) diff --git a/Core/Config.cs b/Core/Config.cs index 7c2e94a..c36c8d9 100644 --- a/Core/Config.cs +++ b/Core/Config.cs @@ -113,16 +113,14 @@ internal static Dictionary GetLevels () { key = rawItem; value = 1; } - if (result.ContainsKey(key)) { + if (!result.ContainsKey(key)) result[key] = value; - } } } int allLevelsDefaultValue = result.Count > 0 ? 0 : 1; - foreach (string levelKey in allPossiblelevels) { + foreach (string levelKey in allPossiblelevels) if (!result.ContainsKey(levelKey)) result.Add(levelKey, allLevelsDefaultValue); - } return result; } internal static int GetLogWriteMilisecond () { @@ -186,7 +184,7 @@ internal static Dictionary GetNotifySettings () { Regex r1 = new Regex("[\r\n\t]"); rawJson = r1.Replace(rawJson, ""); // fix all keys with missing begin and end double quotes - Regex r2 = new Regex(@"([^""])([a-zA-Z0-9_]+):"); + Regex r2 = new Regex(@"([^""])([a-zA-Z]+):"); rawJson = r2.Replace(rawJson, @"$1""$2"":"); // change all values with single quots to double quots Regex r3 = new Regex(@"'([^']*)'"); @@ -196,13 +194,37 @@ internal static Dictionary GetNotifySettings () { rawJson = r4.Replace(rawJson, @""":"); // remove all spaces between value and another key Regex r5 = new Regex(@""",\s+"""); - rawJson = r5.Replace(rawJson, ""); + rawJson = r5.Replace(rawJson, @""","""); Regex r6 = new Regex(@"""\s+\}"); - rawJson = r6.Replace(rawJson, ""); + rawJson = r6.Replace(rawJson, @"""}"); // so let's deserialize string data JavaScriptSerializer jsonSerializer = new JavaScriptSerializer(); result = jsonSerializer.Deserialize>(rawJson); - } catch { } + } catch (Exception ex) { + result.Add("error", ex.Message); + } + } + if (result.ContainsKey("port")) { + string portStr = result["port"].ToString(); + int port = 25; + Int32.TryParse(portStr, out port); + result["port"] = port; + } + if (result.ContainsKey("timeout")) { + string timeoutStr = result["timeout"].ToString(); + int timeout = 10000; + Int32.TryParse(timeoutStr, out timeout); + result["timeout"] = timeout; + } + if (result.ContainsKey("ssl")) { + result["ssl"] = Boolean.Parse(result["ssl"].ToString().ToLower()); + } else { + result["ssl"] = false; + } + if (result.ContainsKey("background")) { + result["background"] = Boolean.Parse(result["background"].ToString().ToLower()); + } else { + result["background"] = false; } return result; } diff --git a/Core/Dispatcher.cs b/Core/Dispatcher.cs index 5b18b0b..97be4de 100644 --- a/Core/Dispatcher.cs +++ b/Core/Dispatcher.cs @@ -160,9 +160,9 @@ static Dispatcher () { Dispatcher.staticInitDirectory(Config.GetDirectory()); FileLog.StaticInit(); Dispatcher.StaticInitLock.ExitWriteLock(); - /*} catch (Exception e3) { - Debug.InitErrors.Add(e3); - }*/ + //} catch (Exception e3) { + // Debug.InitErrors.Add(e3); + //} } } internal static Dispatcher GetCurrent (bool createIfNecessary = true) { @@ -654,7 +654,7 @@ internal void WebRequestError () { // keep everything bad, what should be written in response } else { // write exception into hard drive - Debug.Log(lastException, Level.ERROR); + Debug.Log(lastException); // clear everything bad, what shoud be written in response HttpContext.Current.Response.Clear(); // transmit error page at request end diff --git a/Core/Mailer.cs b/Core/Mailer.cs index be245c9..b308e7a 100644 --- a/Core/Mailer.cs +++ b/Core/Mailer.cs @@ -18,17 +18,26 @@ internal class Mailer { protected static object queueLock = new object { }; protected static volatile List queue = new List(); protected static JavaScriptSerializer jsonSerializer = new JavaScriptSerializer(); + protected static Dictionary notifySettings = null; internal static void Notify (string msg, string logLevel, bool htmlOut) { - lock (Mailer.queueLock) { - Mailer.queue.Add(new object[] { msg, logLevel, htmlOut }); - if (Mailer.bgNotifyThread == null || (Mailer.bgNotifyThread is Thread && !Mailer.bgNotifyThread.IsAlive)) { - Mailer.bgNotifyThread = new Thread(new ThreadStart(delegate () { - object[] args = Mailer.unshiftQueue(); - if (args.Length == 0) return; - Mailer.bgNotify(args[0].ToString(), args[1].ToString(), (bool)args[2]); - })); - Mailer.bgNotifyThread.IsBackground = true; - Mailer.bgNotifyThread.Start(); + lock (Mailer.queueLock) { + if (Mailer.notifySettings == null) + Mailer.notifySettings = Config.GetNotifySettings(); + if (Mailer.notifySettings.ContainsKey("background") && (bool)Mailer.notifySettings["background"] == false) { + Mailer.bgNotify(msg, logLevel, htmlOut); + } else { + Mailer.queue.Add(new object[] { msg, logLevel, htmlOut }); + bool notifyThreadNotAlive = Mailer.bgNotifyThread is Thread && !Mailer.bgNotifyThread.IsAlive; + if (Mailer.bgNotifyThread == null || notifyThreadNotAlive) { + if (notifyThreadNotAlive) Mailer.bgNotifyThread.Abort(); + Mailer.bgNotifyThread = new Thread(new ThreadStart(delegate () { + object[] args = Mailer.unshiftQueue(); + if (args.Length == 0) return; + Mailer.bgNotify(args[0].ToString(), args[1].ToString(), (bool)args[2]); + })); + Mailer.bgNotifyThread.IsBackground = true; + Mailer.bgNotifyThread.Start(); + } } } } @@ -44,23 +53,43 @@ protected static object[] unshiftQueue (bool useLock = true) { } protected static void bgNotify (string msg, string logLevel, bool htmlOut) { if (Mailer.failFileExist() || Mailer.notificationSended(logLevel)) return; - Dictionary notifySettings = Config.GetNotifySettings(); + Dictionary notifySettings = Mailer.notifySettings; try { - if (notifySettings.Count == 0) + bool errorMsgPresented = (notifySettings.Count == 1 && notifySettings.ContainsKey("error")); + if (notifySettings.Count == 0 || errorMsgPresented) throw new Exception( "Configuration doesn't contain key 'Desharp:NotifySettings' or it has wrong JSON format. Try:" + Environment.NewLine + @"" + + (errorMsgPresented ? Environment.NewLine + Environment.NewLine + notifySettings["error"] : "") ); - MailMessage message = Mailer.getMessage(msg, logLevel, htmlOut, notifySettings); - SmtpClient smtp = Mailer.getSmtpClient(notifySettings); + MailMessage message = Mailer.getMessage(msg, logLevel, htmlOut); + SmtpClient smtp = Mailer.getSmtpClient(); smtp.SendCompleted += new SendCompletedEventHandler(Mailer.bgNotifySended); smtp.Send(message); Mailer.storeSuccess(logLevel); } catch (Exception sendException) { - Mailer.storeFailure( - sendException.GetType().FullName + ": " + sendException.Message + Environment.NewLine + sendException.StackTrace - ); + string failureStr = + sendException.GetType().FullName + ": " + sendException.Message + + Environment.NewLine + + sendException.StackTrace; + if (sendException.InnerException is Exception) { + Exception innerEx1 = sendException.InnerException; + failureStr += + Environment.NewLine + + innerEx1.GetType().FullName + ": " + innerEx1.Message + + Environment.NewLine + + innerEx1.StackTrace; + if (innerEx1.InnerException is Exception) { + Exception innerEx2 = innerEx1.InnerException; + failureStr += + Environment.NewLine + + innerEx2.GetType().FullName + ": " + innerEx2.Message + + Environment.NewLine + + innerEx2.StackTrace; + } + } + Mailer.storeFailure(failureStr); } object[] args = Mailer.unshiftQueue(); if (args.Length > 0) { @@ -71,26 +100,54 @@ protected static void bgNotify (string msg, string logLevel, bool htmlOut) { } } private static void bgNotifySended (object sender, AsyncCompletedEventArgs e) { - string msg = ""; + string failureStr = ""; if (e.Error is Exception) { - msg = e.Error.GetType().FullName + ": " + e.Error.Message + Environment.NewLine + e.Error.StackTrace; + failureStr = e.Error.GetType().FullName + ": " + e.Error.Message + + Environment.NewLine + + e.Error.StackTrace; + if (e.Error.InnerException is Exception) { + Exception innerEx1 = e.Error.InnerException; + failureStr += + Environment.NewLine + + innerEx1.GetType().FullName + ": " + innerEx1.Message + + Environment.NewLine + + innerEx1.StackTrace; + if (innerEx1.InnerException is Exception) { + Exception innerEx2 = innerEx1.InnerException; + failureStr += + Environment.NewLine + + innerEx2.GetType().FullName + ": " + innerEx2.Message + + Environment.NewLine + + innerEx2.StackTrace; + } + } } else if (e.Cancelled) { - msg = "Sending notification has been canceled."; + failureStr = "Sending notification has been canceled."; } - if (msg.Length > 0) Mailer.storeFailure(msg); + if (failureStr.Length > 0) Mailer.storeFailure(failureStr); } - protected static MailMessage getMessage (string msg, string logLevel, bool htmlOut, Dictionary notifySettings) { + protected static MailMessage getMessage (string msg, string logLevel, bool htmlOut) { MailMessage result = new MailMessage(); - Assembly entryAssembly = Dispatcher.EnvType == EnvType.Web ? Tools.GetWebEntryAssembly() : Tools.GetWindowsEntryAssembly() ; - result.Subject = String.Format("Desharp event: '{0}', assembly: '{1}'.", logLevel, entryAssembly.GetName().Name); + Dictionary notifySettings = Mailer.notifySettings; + Assembly entryAssembly = Dispatcher.EnvType == EnvType.Web + ? Tools.GetWebEntryAssembly() + : Tools.GetWindowsEntryAssembly() ; + string logLevelName = logLevel.Substring(0, 1).ToUpper() + logLevel.Substring(1); + result.Subject = String.Format( + "Desharp {0} - assembly: {1}", + logLevelName, entryAssembly.GetName().Name + ); if (!notifySettings.ContainsKey("from") && !notifySettings.ContainsKey("user")) throw new Exception("Configuration JSON doesn't contain key 'from' or any credentials."); - string from = notifySettings.ContainsKey("from") ? notifySettings["from"].ToString() : notifySettings["user"].ToString(); + string from = notifySettings.ContainsKey("from") + ? notifySettings["from"].ToString() + : notifySettings["user"].ToString(); result.From = new MailAddress(from); if (!notifySettings.ContainsKey("to")) throw new Exception("Configuration JSON doesn't contain key 'to'."); string[] toRecps = notifySettings["to"].ToString().Split(new char[] { ';' }); - for (int i = 0, l = toRecps.Length; i < l; i += 1) result.To.Add(new MailAddress(toRecps[i])); + for (int i = 0, l = toRecps.Length; i < l; i += 1) + result.To.Add(new MailAddress(toRecps[i])); result.IsBodyHtml = htmlOut; result.SubjectEncoding = System.Text.Encoding.UTF8; result.BodyEncoding = System.Text.Encoding.UTF8; @@ -112,17 +169,21 @@ protected static MailMessage getMessage (string msg, string logLevel, bool htmlO } return result; } - protected static SmtpClient getSmtpClient (Dictionary notifySettings) { + protected static SmtpClient getSmtpClient () { + Dictionary notifySettings = Mailer.notifySettings; if (!notifySettings.ContainsKey("host")) throw new Exception("Configuration JSON doesn't contain key 'host'."); string host = notifySettings["host"].ToString(); - int port = notifySettings.ContainsKey("port") ? Int32.Parse(notifySettings["port"].ToString()) : 25; + int port = notifySettings.ContainsKey("port") + ? (int)notifySettings["port"] + : 25; SmtpClient smtp = new SmtpClient(host, port); if (notifySettings.ContainsKey("user") && notifySettings.ContainsKey("password")) { string user = notifySettings["user"].ToString(); string password = notifySettings["password"].ToString(); string domain = ""; - if (notifySettings.ContainsKey("domain")) domain = notifySettings["domain"].ToString(); + if (notifySettings.ContainsKey("domain")) + domain = notifySettings["domain"].ToString(); NetworkCredential credential; if (domain.Length > 0) { credential = new NetworkCredential(user, password, domain); @@ -134,17 +195,8 @@ protected static SmtpClient getSmtpClient (Dictionary notifySett smtp.UseDefaultCredentials = true; } smtp.DeliveryMethod = SmtpDeliveryMethod.Network; - if (notifySettings.ContainsKey("ssl")) { - smtp.EnableSsl = Boolean.Parse(notifySettings["ssl"].ToString()); - } else { - smtp.EnableSsl = false; - } - int timeout = 10000; - if (notifySettings.ContainsKey("timeout")) { - string timeoutStr = notifySettings["timeout"].ToString(); - Int32.TryParse(timeoutStr, out timeout); - } - smtp.Timeout = timeout; + smtp.EnableSsl = notifySettings.ContainsKey("ssl") ? (bool)notifySettings["ssl"] : false; + smtp.Timeout = notifySettings.ContainsKey("timeout") ? (int)notifySettings["timeout"] : 10000; return smtp; } protected static bool failFileExist () { @@ -159,7 +211,9 @@ protected static bool notificationSended (string levelValue) { string fullPath = Dispatcher.Directory + "/" + Mailer.MAIL_SENDED_FILE; if (File.Exists(fullPath)) { try { - Mailer.successNotifycationLevels = Mailer.jsonSerializer.Deserialize>(File.ReadAllText(fullPath)); + Mailer.successNotifycationLevels = Mailer.jsonSerializer.Deserialize>( + File.ReadAllText(fullPath) + ); } catch { } } } @@ -168,14 +222,22 @@ protected static bool notificationSended (string levelValue) { private static void storeSuccess (string logLevel) { Mailer.successNotifycationLevels.Add(logLevel); try { - string jsonData = Mailer.jsonSerializer.Serialize(Mailer.successNotifycationLevels); - File.WriteAllText(Dispatcher.Directory + "/" + Mailer.MAIL_SENDED_FILE, jsonData, System.Text.Encoding.UTF8); + string jsonData = Mailer.jsonSerializer.Serialize( + Mailer.successNotifycationLevels + ); + File.WriteAllText( + Dispatcher.Directory + "/" + Mailer.MAIL_SENDED_FILE, + jsonData, System.Text.Encoding.UTF8 + ); } catch { } } protected static void storeFailure (string msg) { try { - File.WriteAllText(Dispatcher.Directory + "/" + Mailer.MAIL_NOT_SENDED_FILE, msg, System.Text.Encoding.UTF8); + File.WriteAllText( + Dispatcher.Directory + "/" + Mailer.MAIL_NOT_SENDED_FILE, + msg, System.Text.Encoding.UTF8 + ); } catch { } Mailer.failureFileExists = true; diff --git a/Debug.cs b/Debug.cs index 9191be0..26d9efc 100644 --- a/Debug.cs +++ b/Debug.cs @@ -385,8 +385,10 @@ public static void Log (Exception exception = null) { dispatcher.LastError = exception; bool htmlOut = dispatcher.Output == LogFormat.Html; List renderedExceptions = Exceptions.RenderExceptions(exception, true, htmlOut, true); - if (Dispatcher.Levels["exception"] == 2) Mailer.Notify(String.Join(Environment.NewLine, renderedExceptions), "exception", htmlOut); - foreach (string renderedException in renderedExceptions) FileLog.Log(renderedException + Environment.NewLine, "exception"); + if (Dispatcher.Levels["exception"] == 2) + Mailer.Notify(String.Join(Environment.NewLine, renderedExceptions), "exception", htmlOut); + foreach (string renderedException in renderedExceptions) + FileLog.Log(renderedException + Environment.NewLine, "exception"); } /// /// Log any type value to application *.log|*.html file, specified by level param. @@ -421,7 +423,8 @@ public static void Log (object obj = null, Level level = Level.INFO, int maxDept renderedObj = Exceptions.RenderCurrentApplicationPoint( renderedObj, "Value", true, htmlOut ) + Environment.NewLine; - if (Dispatcher.Levels[logLevelValue] == 2) Mailer.Notify(renderedObj, logLevelValue, htmlOut); + if (Dispatcher.Levels[logLevelValue] == 2) + Mailer.Notify(renderedObj, logLevelValue, htmlOut); } FileLog.Log(renderedObj, logLevelValue); } diff --git a/README.md b/README.md index d69cd90..052d27e 100644 --- a/README.md +++ b/README.md @@ -385,24 +385,25 @@ where are all detailed configuration options you can copy and paste: - `host`: Required, mail server smtp domain | IPv4 | IPv6. - `port`: Not required, `25` by default. - `ssl`: Not required, `false` by default. - - `from`: Required if no username and password specified, email address to - specify sender, if no value specified, there is used `username` value. - - `username` and `password`: Required if no `from` sender specified, mail server - username/password credentials for sender account, always necessary to use together. + - `from`: Required if no username and password specified, email address to specify sender, if no value specified, there is used `username` value. + - `username` and `password`: Required if no `from` sender specified, mail server username/password credentials for sender account, always necessary to use together. + - `domain` - Not required, no default value. Used only as third parametter for `System.Net.NetworkCredential` if presented. - `to`: Required, single recepient email adress or multiple adresses separated by semicolon `;`. - `priority`: Not required, possible values: `low` | `normal` | `high` (`normal` by defaut). - `timeout`: Not required, smtp server timeout specified in miliseconds, `10000` by default (10 seconds). + - `background`: Not required at all. Default value is `true` to send all notifications in background thread. + Use `false` value only to debug email sending. -->