mhf-server/Mhf.Cli/LogWriter.cs
2019-11-24 19:43:35 +08:00

368 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Threading;
using Arrowgene.Services.Buffers;
using Arrowgene.Services.Logging;
using Mhf.Cli.Argument;
using Mhf.Server.Logging;
namespace Mhf.Cli
{
public class LogWriter : ISwitchConsumer
{
private readonly object _consoleLock;
private readonly HashSet<ushort> _packetIdWhitelist;
private readonly HashSet<ushort> _packetIdBlacklist;
private readonly ILogger _logger;
private readonly Queue<Log> _logQueue;
private bool _paused;
private bool _continueing;
public LogWriter()
{
_logger = LogProvider.Logger(this);
_packetIdWhitelist = new HashSet<ushort>();
_packetIdBlacklist = new HashSet<ushort>();
_logQueue = new Queue<Log>();
_consoleLock = new object();
Switches = new List<ISwitchProperty>();
_paused = false;
_continueing = false;
Reset();
LoadSwitches();
LogProvider.GlobalLogWrite += LogProviderOnGlobalLogWrite;
}
public List<ISwitchProperty> Switches { get; }
/// <summary>
/// --max-packet-size=64
/// </summary>
public int MaxPacketSize { get; set; }
/// <summary>
/// --no-data=true
/// </summary>
public bool NoData { get; set; }
/// <summary>
/// --log-level=2
/// </summary>
public int MinLogLevel { get; set; }
public void Reset()
{
MaxPacketSize = -1;
NoData = false;
MinLogLevel = (int) LogLevel.Debug;
_packetIdWhitelist.Clear();
_packetIdBlacklist.Clear();
}
public void WhitelistPacket(ushort packetId)
{
if (_packetIdWhitelist.Contains(packetId))
{
_logger.Error($"PacketId:{packetId} is already whitelisted");
return;
}
_packetIdWhitelist.Add(packetId);
}
public void BlacklistPacket(ushort packetId)
{
if (_packetIdBlacklist.Contains(packetId))
{
_logger.Error($"PacketId:{packetId} is already blacklisted");
return;
}
_packetIdBlacklist.Add(packetId);
}
public void Pause()
{
_paused = true;
}
public void Continue()
{
_continueing = true;
while (_logQueue.TryDequeue(out Log log))
{
WriteLog(log);
}
_paused = false;
_continueing = false;
}
private void LoadSwitches()
{
Switches.Add(
new SwitchProperty<bool>(
"--no-data",
"--no-data=true (true|false)",
"Don't display packet data",
bool.TryParse,
(result => NoData = result)
)
);
Switches.Add(
new SwitchProperty<int>(
"--max-packet-size",
"--max-packet-size=64 (integer)",
"Don't display packet data",
int.TryParse,
(result => MaxPacketSize = result)
)
);
Switches.Add(
new SwitchProperty<int>(
"--log-level",
"--log-level=20 (integer) [Debug=10, Info=20, Error=30]",
"Only display logs of the same level or above",
int.TryParse,
(result => MinLogLevel = result)
)
);
Switches.Add(
new SwitchProperty<object>(
"--clear",
"--clear",
"Resets all switches to default",
SwitchProperty<object>.NoOp,
result => Reset()
)
);
Switches.Add(
new SwitchProperty<List<ushort>>(
"--b-list",
"--b-list=1000,2000,0xAA (PacketId[0xA|10])",
"A blacklist that does not logs packets specified",
TryParsePacketIdList,
results => { AssinPacketIdList(results, BlacklistPacket); }
)
);
Switches.Add(
new SwitchProperty<List<ushort>>(
"--w-list",
"--w-list=1000,2000,0xAA (PacketId[0xA|10])",
"A whitelist that only logs packets specified",
TryParsePacketIdList,
results => { AssinPacketIdList(results, WhitelistPacket); }
)
);
}
/// <summary>
/// parses strings like "1:1000,2000,3:4000" into ServerType and PacketId
/// </summary>
private bool TryParsePacketIdList(string value, out List<ushort> result)
{
if (string.IsNullOrEmpty(value))
{
result = null;
return false;
}
string[] values = value.Split(",");
result = new List<ushort>();
foreach (string entry in values)
{
NumberStyles numberStyles;
String entryStr;
if (entry.StartsWith("0x"))
{
entryStr = entry.Substring(2);
numberStyles = NumberStyles.HexNumber;
}
else
{
entryStr = entry;
numberStyles = NumberStyles.Integer;
}
if (!ushort.TryParse(entryStr, numberStyles, null, out ushort val))
{
return false;
}
result.Add(val);
}
return true;
}
private void AssinPacketIdList(List<ushort> results, Action<ushort> addToPacketList)
{
foreach (ushort entry in results)
{
addToPacketList(entry);
}
}
private void LogProviderOnGlobalLogWrite(object sender, LogWriteEventArgs logWriteEventArgs)
{
while (_continueing)
{
Thread.Sleep(10000);
}
if (_paused)
{
_logQueue.Enqueue(logWriteEventArgs.Log);
return;
}
WriteLog(logWriteEventArgs.Log);
}
private void WriteLog(Log log)
{
ConsoleColor consoleColor;
string text;
object tag = log.Tag;
if (tag is MhfLogPacket logPacket)
{
switch (logPacket.LogType)
{
case MhfLogType.PacketIn:
consoleColor = ConsoleColor.Green;
break;
case MhfLogType.PacketOut:
consoleColor = ConsoleColor.Blue;
break;
case MhfLogType.PacketUnhandled:
consoleColor = ConsoleColor.Red;
break;
default:
consoleColor = ConsoleColor.Gray;
break;
}
text = CreatePacketLog(logPacket);
}
else
{
LogLevel logLevel = log.LogLevel;
if ((int) logLevel < MinLogLevel)
{
return;
}
switch (logLevel)
{
case LogLevel.Debug:
consoleColor = ConsoleColor.DarkCyan;
break;
case LogLevel.Info:
consoleColor = ConsoleColor.Cyan;
break;
case LogLevel.Error:
consoleColor = ConsoleColor.Red;
break;
default:
consoleColor = ConsoleColor.Gray;
break;
}
text = log.ToString();
}
if (text == null)
{
return;
}
lock (_consoleLock)
{
Console.ForegroundColor = consoleColor;
Console.WriteLine(text);
Console.ResetColor();
}
}
private bool ExcludeLog(ushort packetId)
{
bool useWhitelist = _packetIdWhitelist.Count > 0;
bool whitelisted = _packetIdWhitelist.Contains(packetId);
bool blacklisted = _packetIdBlacklist.Contains(packetId);
if (useWhitelist && whitelisted)
{
return false;
}
if (useWhitelist)
{
return true;
}
if (blacklisted)
{
return true;
}
return false;
}
private string CreatePacketLog(MhfLogPacket logPacket)
{
ushort packetId = logPacket.Id;
if (ExcludeLog(packetId))
{
return null;
}
int dataSize = logPacket.Data.Size;
StringBuilder sb = new StringBuilder();
sb.Append($"{logPacket.ClientIdentity} Packet Log");
sb.Append(Environment.NewLine);
sb.Append("----------");
sb.Append(Environment.NewLine);
sb.Append($"[{logPacket.TimeStamp:HH:mm:ss}][Typ:{logPacket.LogType}]");
sb.Append(Environment.NewLine);
sb.Append(
$"[Id:0x{logPacket.Id:X2}|{logPacket.Id}][BodyLen:{logPacket.Data.Size}][{logPacket.PacketIdName}]");
sb.Append(Environment.NewLine);
sb.Append(logPacket.Header.ToLogText());
sb.Append(Environment.NewLine);
if (!NoData)
{
IBuffer data = logPacket.Data;
int maxPacketSize = MaxPacketSize;
if (maxPacketSize > 0 && dataSize > maxPacketSize)
{
data = data.Clone(0, maxPacketSize);
sb.Append($"- Truncated Data showing {maxPacketSize} of {dataSize} bytes");
sb.Append(Environment.NewLine);
}
sb.Append("ASCII:");
sb.Append(Environment.NewLine);
sb.Append(data.ToAsciiString(true));
sb.Append(Environment.NewLine);
sb.Append("HEX:");
sb.Append(Environment.NewLine);
sb.Append(data.ToHexString(' '));
sb.Append(Environment.NewLine);
}
sb.Append("----------");
return sb.ToString();
}
}
}