Skip to content

Commit

Permalink
Release 1.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
tomFlidr committed Sep 26, 2019
1 parent 87c3757 commit a82cb82
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 81 deletions.
38 changes: 30 additions & 8 deletions Core/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,14 @@ internal static Dictionary<string, int> 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 () {
Expand Down Expand Up @@ -186,7 +184,7 @@ internal static Dictionary<string, object> 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(@"'([^']*)'");
Expand All @@ -196,13 +194,37 @@ internal static Dictionary<string, object> 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<Dictionary<string, object>>(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;
}
Expand Down
8 changes: 4 additions & 4 deletions Core/Dispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
150 changes: 106 additions & 44 deletions Core/Mailer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,26 @@ internal class Mailer {
protected static object queueLock = new object { };
protected static volatile List<object[]> queue = new List<object[]>();
protected static JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
protected static Dictionary<string, object> 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();
}
}
}
}
Expand All @@ -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<string, object> notifySettings = Config.GetNotifySettings();
Dictionary<string, object> 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
+ @"<add key=""Desharp:NotifySettings"" value=""{host:'smtp.host.com',port:25,ssl:false,user:'username',password:'secret',from:'desharp@yourappdomain.com',to:'username@mailbox.com',priority:'high',timeout:30000}"" />"
+ (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) {
Expand All @@ -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<string, object> 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<string, object> 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;
Expand All @@ -112,17 +169,21 @@ protected static MailMessage getMessage (string msg, string logLevel, bool htmlO
}
return result;
}
protected static SmtpClient getSmtpClient (Dictionary<string, object> notifySettings) {
protected static SmtpClient getSmtpClient () {
Dictionary<string, object> 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);
Expand All @@ -134,17 +195,8 @@ protected static SmtpClient getSmtpClient (Dictionary<string, object> 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 () {
Expand All @@ -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<List<string>>(File.ReadAllText(fullPath));
Mailer.successNotifycationLevels = Mailer.jsonSerializer.Deserialize<List<string>>(
File.ReadAllText(fullPath)
);
} catch { }
}
}
Expand All @@ -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;
Expand Down
9 changes: 6 additions & 3 deletions Debug.cs
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,10 @@ public static void Log (Exception exception = null) {
dispatcher.LastError = exception;
bool htmlOut = dispatcher.Output == LogFormat.Html;
List<string> 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");
}
/// <summary>
/// Log any type value to application <c>*.log|*.html</c> file, specified by level param.
Expand Down Expand Up @@ -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);
}
Expand Down
27 changes: 14 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
-->
<add key="Desharp:NotifySettings" value="{
host: 'smtp.host.com',
port: 25,
ssl: false,
user: 'username',
password: 'secret',
from: 'username@host.com',
to: 'mydaily@mailbox.com',
priority: 'high',
timeout: 30000
host: 'smtp.company.com',
port: 587,
ssl: true,
user: 'noreply@company.com',
password: 'your-secret-password',
from: 'noreply@company.com',
to: 'your.name@gmail.com',
priority: 'high',
timeout: 30000
}" />

<!--
Expand Down
Loading

0 comments on commit a82cb82

Please sign in to comment.