Improve logging and rewrite email helper

This commit is contained in:
Slendy 2023-01-02 00:48:37 -06:00
commit fb90371ad1
No known key found for this signature in database
GPG key ID: 7288D68361B91428
3 changed files with 89 additions and 31 deletions

View file

@ -1,48 +1,106 @@
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Mail;
using System.Threading;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Logging;
namespace LBPUnion.ProjectLighthouse.Helpers;
public static class SMTPHelper
public class SMTPHelper
{
private static readonly SmtpClient client;
private static readonly MailAddress fromAddress;
static SMTPHelper()
internal static readonly SMTPHelper Instance = new();
private readonly SmtpClient client;
private readonly MailAddress fromAddress;
private readonly ConcurrentQueue<EmailEntry> emailQueue = new();
private readonly SemaphoreSlim emailSemaphore = new(0);
private bool stopSignal;
private readonly Task emailThread;
private SMTPHelper()
{
if (!ServerConfiguration.Instance.Mail.MailEnabled) return;
client = new SmtpClient(ServerConfiguration.Instance.Mail.Host, ServerConfiguration.Instance.Mail.Port)
this.client = new SmtpClient(ServerConfiguration.Instance.Mail.Host, ServerConfiguration.Instance.Mail.Port)
{
EnableSsl = ServerConfiguration.Instance.Mail.UseSSL,
Credentials = new NetworkCredential(ServerConfiguration.Instance.Mail.Username, ServerConfiguration.Instance.Mail.Password),
};
this.fromAddress = new MailAddress(ServerConfiguration.Instance.Mail.FromAddress, ServerConfiguration.Instance.Mail.FromName);
fromAddress = new MailAddress(ServerConfiguration.Instance.Mail.FromAddress, ServerConfiguration.Instance.Mail.FromName);
this.stopSignal = false;
this.emailThread = Task.Factory.StartNew(this.EmailQueue);
}
private async void EmailQueue()
{
while (!this.stopSignal)
{
await this.emailSemaphore.WaitAsync();
if (!this.emailQueue.TryDequeue(out EmailEntry entry)) continue;
try
{
this.client.Send(entry.Message);
entry.Result.SetResult(true);
}
catch (Exception e)
{
Logger.Error($"Failed to send email: {e}", LogArea.Email);
entry.Result.SetResult(false);
}
}
}
public static void Dispose()
{
Instance.stopSignal = true;
Instance.emailThread.Wait();
Instance.emailThread.Dispose();
}
public static bool SendEmail(string recipientAddress, string subject, string body)
{
if (!ServerConfiguration.Instance.Mail.MailEnabled) return false;
TaskCompletionSource<bool> resultTask = new();
Instance.SendEmail(recipientAddress, subject, body, resultTask);
return resultTask.Task.Result;
}
MailMessage message = new(fromAddress, new MailAddress(recipientAddress))
public void SendEmail(string recipientAddress, string subject, string body, TaskCompletionSource<bool> resultTask)
{
if (!ServerConfiguration.Instance.Mail.MailEnabled)
{
resultTask.SetResult(false);
return;
}
MailMessage message = new(Instance.fromAddress, new MailAddress(recipientAddress))
{
Subject = subject,
Body = body,
};
try
{
client.Send(message);
}
catch(Exception e)
{
Console.WriteLine(e);
return false;
}
return true;
this.emailQueue.Enqueue(new EmailEntry(message, resultTask));
this.emailSemaphore.Release();
}
internal class EmailEntry
{
public MailMessage Message { get; set; }
public TaskCompletionSource<bool> Result { get; set; }
public EmailEntry(MailMessage message, TaskCompletionSource<bool> result)
{
this.Message = message;
this.Result = result;
}
}
}

View file

@ -26,4 +26,5 @@ public enum LogArea
Score,
RateLimit,
Deserialization,
Email,
}

View file

@ -73,6 +73,11 @@ public class Logger
/// </summary>
private readonly ConcurrentQueue<LogLine> logQueue = new();
/// <summary>
/// Used to signal the logging loop that a new message has arrived
/// </summary>
private readonly SemaphoreSlim logSemaphore = new(0);
/// <summary>
/// Adds a <see cref="LogLine"/> to the queue. Only used internally.
/// </summary>
@ -86,17 +91,12 @@ public class Logger
public Logger() // Start queue thread on first Logger access
{
Task.Factory.StartNew
(
() =>
(async () =>
{
while (true)
{
bool logged = this.queueLoop();
Thread.Sleep(logged ? 10 : 100);
// We wait 100ms if we dont log since it's less likely that the program logged again.
// If we did log, wait 10ms before looping again.
// This is all so we use as little CPU as possible. This is an endless while loop, after all.
await this.logSemaphore.WaitAsync();
this.queueLoop();
}
}
);
@ -133,16 +133,14 @@ public class Logger
/// A function used by the queue thread
/// </summary>
/// <returns></returns>
private bool queueLoop()
private void queueLoop()
{
if (!this.logQueue.TryDequeue(out LogLine line)) return false;
if (!this.logQueue.TryDequeue(out LogLine line)) return;
foreach (ILogger logger in this.loggers)
{
logger.Log(line);
}
return true;
}
#endregion
@ -215,6 +213,7 @@ public class Logger
Area = area,
Trace = getTrace(extraTraceLines),
});
this.logSemaphore.Release();
}
#endregion
}