diff --git a/SS14.Client/UserInterface/CustomControls/DebugConsole.cs b/SS14.Client/UserInterface/CustomControls/DebugConsole.cs index bed0a7514..9ed2365bf 100644 --- a/SS14.Client/UserInterface/CustomControls/DebugConsole.cs +++ b/SS14.Client/UserInterface/CustomControls/DebugConsole.cs @@ -73,17 +73,20 @@ namespace SS14.Client.UserInterface.CustomControls public void AddLine(string text, ChatChannel channel, Color color) { - if (!firstLine) + lock (Contents) { - Contents.NewLine(); + if (!firstLine) + { + Contents.NewLine(); + } + else + { + firstLine = false; + } + Contents.PushColor(color); + Contents.AddText(text); + Contents.Pop(); // Pop the color off. } - else - { - firstLine = false; - } - Contents.PushColor(color); - Contents.AddText(text); - Contents.Pop(); // Pop the color off. } public void AddLine(string text, Color color) @@ -98,48 +101,55 @@ namespace SS14.Client.UserInterface.CustomControls public void AddFormattedLine(FormattedMessage message) { - if (!firstLine) + lock (Contents) { - Contents.NewLine(); - } - else - { - firstLine = false; - } - - var pushCount = 0; - foreach (var tag in message.Tags) - { - switch (tag) + if (!firstLine) { - case FormattedMessage.TagText text: - Contents.AddText(text.Text); - break; - case FormattedMessage.TagColor color: - Contents.PushColor(color.Color); - pushCount++; - break; - case FormattedMessage.TagPop _: - if (pushCount <= 0) - { - throw new InvalidOperationException(); - } - Contents.Pop(); - pushCount--; - break; + Contents.NewLine(); + } + else + { + firstLine = false; } - } - for (; pushCount > 0; pushCount--) - { - Contents.Pop(); + var pushCount = 0; + foreach (var tag in message.Tags) + { + switch (tag) + { + case FormattedMessage.TagText text: + Contents.AddText(text.Text); + break; + case FormattedMessage.TagColor color: + Contents.PushColor(color.Color); + pushCount++; + break; + case FormattedMessage.TagPop _: + if (pushCount <= 0) + { + throw new InvalidOperationException(); + } + + Contents.Pop(); + pushCount--; + break; + } + } + + for (; pushCount > 0; pushCount--) + { + Contents.Pop(); + } } } public void Clear() { - Contents.Clear(); - firstLine = true; + lock (Contents) + { + Contents.Clear(); + firstLine = true; + } } } } diff --git a/SS14.Server/ServerStatus/StatusHost.cs b/SS14.Server/ServerStatus/StatusHost.cs index 66c0a837e..d2cd47d30 100644 --- a/SS14.Server/ServerStatus/StatusHost.cs +++ b/SS14.Server/ServerStatus/StatusHost.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Net; +using System.Security.Cryptography; using System.Text; using System.Threading; using Newtonsoft.Json; @@ -10,6 +11,7 @@ using SS14.Server.Interfaces.ServerStatus; using SS14.Shared.Configuration; using SS14.Shared.Interfaces.Configuration; using SS14.Shared.IoC; +using SS14.Shared.Log; // This entire file is NIHing a REST server because pulling in libraries is effort. // Also it was fun to write. @@ -58,6 +60,7 @@ namespace SS14.Server.ServerStatus { return; } + _stop.Set(); _listenerThread.Join(1000); _listener.Stop(); @@ -81,6 +84,15 @@ namespace SS14.Server.ServerStatus } private void _processRequest(HttpListenerContext context) + { + _processRequestInternal(context); + Logger.DebugS("statushost", "{0} -> {1} {2}", + context.Request.Url.AbsolutePath, + context.Response.StatusCode, + context.Response.StatusDescription); + } + + private void _processRequestInternal(HttpListenerContext context) { var response = context.Response; var request = context.Request; @@ -112,6 +124,7 @@ namespace SS14.Server.ServerStatus response.StatusDescription = "Not Implemented"; response.ContentType = "text/plain"; _respondText(response, "501 Not Implemented", head); + Logger.WarningS("statushost", "OnStatusRequest is not set, responding with a 501."); return; } @@ -146,15 +159,13 @@ namespace SS14.Server.ServerStatus _respondText(response, "404 Not Found", head); } } - catch + catch (Exception e) { response.StatusCode = (int) HttpStatusCode.InternalServerError; response.StatusDescription = "Internal Server Error"; response.ContentType = "text/plain"; _respondText(response, "500 Internal Server Error", head); - // TODO: Logging. - // Logger is not thread safe atm. - // This is a problem. + Logger.ErrorS("statushost", "Exception in StatusHost: {0}", e); } } diff --git a/SS14.Shared/Log/ConsoleLogHandler.cs b/SS14.Shared/Log/ConsoleLogHandler.cs index 7abc86956..ffaba6798 100644 --- a/SS14.Shared/Log/ConsoleLogHandler.cs +++ b/SS14.Shared/Log/ConsoleLogHandler.cs @@ -8,16 +8,21 @@ namespace SS14.Shared.Log /// public sealed class ConsoleLogHandler : ILogHandler { - public void Log(LogMessage message) - { - string name = LogMessage.LogLevelToName(message.Level); - ConsoleColor color = LogLevelToConsoleColor(message.Level); + private readonly object locker = new object(); - System.Console.Write('['); - System.Console.ForegroundColor = color; - System.Console.Write(name); - System.Console.ResetColor(); - System.Console.WriteLine("] {0}: {1}", message.SawmillName, message.Message); + public void Log(in LogMessage message) + { + var name = LogMessage.LogLevelToName(message.Level); + var color = LogLevelToConsoleColor(message.Level); + + lock (locker) + { + System.Console.Write('['); + System.Console.ForegroundColor = color; + System.Console.Write(name); + System.Console.ResetColor(); + System.Console.WriteLine("] {0}: {1}", message.SawmillName, message.Message); + } } private static ConsoleColor LogLevelToConsoleColor(LogLevel level) diff --git a/SS14.Shared/Log/FileLogHandler.cs b/SS14.Shared/Log/FileLogHandler.cs index 7c9529628..82f0618bb 100644 --- a/SS14.Shared/Log/FileLogHandler.cs +++ b/SS14.Shared/Log/FileLogHandler.cs @@ -7,12 +7,12 @@ namespace SS14.Shared.Log { public sealed class FileLogHandler : ILogHandler, IDisposable { - private readonly StreamWriter writer; + private readonly TextWriter writer; public FileLogHandler(string path) { Directory.CreateDirectory(Path.GetDirectoryName(path)); - writer = new StreamWriter(path, true, Encoding.UTF8); + writer = TextWriter.Synchronized(new StreamWriter(path, true, Encoding.UTF8)); } public void Dispose() @@ -20,10 +20,10 @@ namespace SS14.Shared.Log writer.Dispose(); } - public void Log(LogMessage message) + public void Log(in LogMessage message) { var name = message.LogLevelToName(); - writer.WriteLine("{0} [{1}] {2}: {3}", DateTime.Now.ToString("o"), name, message.SawmillName, message.Message); + writer.WriteLine("{0:o} [{1}] {2}: {3}", DateTime.Now, name, message.SawmillName, message.Message); // This probably isn't the best idea. // Remove this flush if it becomes a problem (say performance). diff --git a/SS14.Shared/Log/LogManager.Sawmill.cs b/SS14.Shared/Log/LogManager.Sawmill.cs index bf0042fcf..3458fc5ab 100644 --- a/SS14.Shared/Log/LogManager.Sawmill.cs +++ b/SS14.Shared/Log/LogManager.Sawmill.cs @@ -1,6 +1,7 @@ using SS14.Shared.Interfaces.Log; using System; using System.Collections.Generic; +using System.Threading; namespace SS14.Shared.Log { @@ -26,7 +27,8 @@ namespace SS14.Shared.Log } private LogLevel? _level = null; - private List handlers = new List(); + private readonly List _handlers = new List(); + private readonly ReaderWriterLockSlim _handlerLock = new ReaderWriterLockSlim(); public Sawmill(Sawmill parent, string name) { @@ -36,12 +38,28 @@ namespace SS14.Shared.Log public void AddHandler(ILogHandler handler) { - handlers.Add(handler); + _handlerLock.EnterWriteLock(); + try + { + _handlers.Add(handler); + } + finally + { + _handlerLock.ExitWriteLock(); + } } public void RemoveHandler(ILogHandler handler) { - handlers.Remove(handler); + _handlerLock.EnterWriteLock(); + try + { + _handlers.Remove(handler); + } + finally + { + _handlerLock.ExitWriteLock(); + } } public void Log(LogLevel level, string message, params object[] args) @@ -52,22 +70,30 @@ namespace SS14.Shared.Log public void Log(LogLevel level, string message) { var msg = new LogMessage(message, level, Name); - LogInternal(ref msg); + LogInternal(msg); } - private void LogInternal(ref LogMessage message) + private void LogInternal(in LogMessage message) { if (message.Level < GetPracticalLevel()) { return; } - foreach (var handler in handlers) + _handlerLock.EnterReadLock(); + try { - handler.Log(message); + foreach (var handler in _handlers) + { + handler.Log(message); + } + } + finally + { + _handlerLock.ExitReadLock(); } - Parent?.LogInternal(ref message); + Parent?.LogInternal(message); } private LogLevel GetPracticalLevel() diff --git a/SS14.Shared/Log/LogManager.cs b/SS14.Shared/Log/LogManager.cs index 6b53c6cb8..fb833a738 100644 --- a/SS14.Shared/Log/LogManager.cs +++ b/SS14.Shared/Log/LogManager.cs @@ -1,6 +1,6 @@ using SS14.Shared.Interfaces.Log; using System.Collections.Generic; -using System.Diagnostics.Contracts; +using System.Threading; namespace SS14.Shared.Log { @@ -11,8 +11,36 @@ namespace SS14.Shared.Log private readonly Sawmill rootSawmill; public ISawmill RootSawmill => rootSawmill; - [Pure] + private readonly Dictionary sawmills = new Dictionary(); + private readonly ReaderWriterLockSlim _sawmillsLock = new ReaderWriterLockSlim(); + public ISawmill GetSawmill(string name) + { + _sawmillsLock.EnterReadLock(); + try + { + if (sawmills.TryGetValue(name, out var sawmill)) + { + return sawmill; + } + } + finally + { + _sawmillsLock.ExitReadLock(); + } + + _sawmillsLock.EnterWriteLock(); + try + { + return _getSawmillUnlocked(name); + } + finally + { + _sawmillsLock.ExitWriteLock(); + } + } + + private Sawmill _getSawmillUnlocked(string name) { if (sawmills.TryGetValue(name, out var sawmill)) { @@ -20,24 +48,22 @@ namespace SS14.Shared.Log } var index = name.LastIndexOf('.'); - string parentname; + string parentName; if (index == -1) { - parentname = ROOT; + parentName = ROOT; } else { - parentname = name.Substring(0, index); + parentName = name.Substring(0, index); } - var parent = (Sawmill)GetSawmill(parentname); + var parent = _getSawmillUnlocked(parentName); sawmill = new Sawmill(parent, name); - sawmills[name] = sawmill; + sawmills.Add(name, sawmill); return sawmill; } - private Dictionary sawmills = new Dictionary(); - public LogManager() { rootSawmill = new Sawmill(null, ROOT) diff --git a/SS14.Shared/Log/LogMessage.cs b/SS14.Shared/Log/LogMessage.cs index 1032319fb..f3b7a0733 100644 --- a/SS14.Shared/Log/LogMessage.cs +++ b/SS14.Shared/Log/LogMessage.cs @@ -1,6 +1,6 @@ namespace SS14.Shared.Log { - public struct LogMessage + public readonly struct LogMessage { /// /// The actual log message given. diff --git a/SS14.UnitTesting/LogCatcher.cs b/SS14.UnitTesting/LogCatcher.cs index e412e8850..df43700c4 100644 --- a/SS14.UnitTesting/LogCatcher.cs +++ b/SS14.UnitTesting/LogCatcher.cs @@ -20,12 +20,18 @@ namespace SS14.UnitTesting /// public void Flush() { - _logs.Clear(); + lock (_logs) + { + _logs.Clear(); + } } - void ILogHandler.Log(LogMessage message) + void ILogHandler.Log(in LogMessage message) { - _logs.Add(message); + lock (_logs) + { + _logs.Add(message); + } } } }