Logging improvements.

1. Logging is now thread safe.
2. Slight optimizations.
This commit is contained in:
Pieter-Jan Briers
2018-11-28 22:31:09 +01:00
parent 04396b4397
commit e3f702781f
8 changed files with 164 additions and 80 deletions

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -8,16 +8,21 @@ namespace SS14.Shared.Log
/// </summary>
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)

View File

@@ -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).

View File

@@ -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<ILogHandler> handlers = new List<ILogHandler>();
private readonly List<ILogHandler> _handlers = new List<ILogHandler>();
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()

View File

@@ -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<string, Sawmill> sawmills = new Dictionary<string, Sawmill>();
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<string, Sawmill> sawmills = new Dictionary<string, Sawmill>();
public LogManager()
{
rootSawmill = new Sawmill(null, ROOT)

View File

@@ -1,6 +1,6 @@
namespace SS14.Shared.Log
{
public struct LogMessage
public readonly struct LogMessage
{
/// <summary>
/// The actual log message given.

View File

@@ -20,12 +20,18 @@ namespace SS14.UnitTesting
/// </summary>
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);
}
}
}
}