585 lines
20 KiB
C#
585 lines
20 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using GeneratorCode.Configure;
|
|
using JetBrains.Annotations;
|
|
|
|
namespace GeneratorCode.Logs
|
|
{
|
|
public enum NLogLevel
|
|
{
|
|
Fatal = 0,
|
|
Crash,
|
|
Error,
|
|
Warring,
|
|
Info,
|
|
Debug,
|
|
MaxLevel
|
|
}
|
|
|
|
public enum CacheOptMode
|
|
{
|
|
Drop = 0,
|
|
Replease
|
|
}
|
|
|
|
public class NLogConfig
|
|
{
|
|
private object _cfgLock = new object();
|
|
public delegate void LogCfgChangedHandle();
|
|
|
|
public static event LogCfgChangedHandle OnLogCfgChanged;
|
|
|
|
public NLogConfig()
|
|
{
|
|
LogConfig();
|
|
NConfig.OnConfigChanged += () => { LogCfgChanged(); };
|
|
}
|
|
|
|
protected static void LogCfgChanged()
|
|
{
|
|
OnLogCfgChanged?.Invoke();
|
|
}
|
|
|
|
public bool LogEnable { get; set; }
|
|
public NLogLevel LogLevel { get; set; }
|
|
public NLogLevel DefaultLevel { get; set; }
|
|
public bool AsyncMode { get; set; }
|
|
public bool ForceNewLine { get; set; }
|
|
|
|
public bool ShowDate { get; set; }
|
|
public bool ShowTime { get; set; }
|
|
public bool ShowMSec { get; set; }
|
|
public bool ShowLevel { get; set; }
|
|
public bool ShowCodeFile { get; set; }
|
|
public bool ShowFunction { get; set; }
|
|
public bool ShowCodeLine { get; set; }
|
|
|
|
public bool EnConsole { get; set; }
|
|
public bool EnTrace { get; set; }
|
|
public bool EnDebug { get; set; }
|
|
public bool EnFile { get; set; }
|
|
|
|
|
|
public int MaxItemsCache { get; set; }
|
|
public CacheOptMode CacheMode { get; set; }
|
|
public int SleepTime { get; set; }
|
|
public int NumOutItems { get; set; }
|
|
|
|
public string DirPath { get; set; }
|
|
public string FileNamePre { get; set; }
|
|
public bool EnSplitLog { get; set; }
|
|
public bool SplitByData { get; set; }
|
|
public int SplitBySize { get; set; }
|
|
public bool RoolbackFile { get; set; }
|
|
public int MaxFileNum { get; set; }
|
|
|
|
public void LogConfig()
|
|
{
|
|
lock (_cfgLock)
|
|
{
|
|
LogEnable = NConfig.GetCfgValue("LogGlobal", "LogEnable", false);
|
|
LogLevel = (NLogLevel)NConfig.GetCfgValue("LogGlobal", "LogLevel", (int)NLogLevel.MaxLevel);
|
|
DefaultLevel = (NLogLevel)NConfig.GetCfgValue("LogGlobal", "DefaultLogLevel", (int)NLogLevel.Info);
|
|
AsyncMode = NConfig.GetCfgValue("LogGlobal", "AsyncMode", false);
|
|
ForceNewLine = NConfig.GetCfgValue("LogGlobal", "AutoForceNewLine", false);
|
|
|
|
ShowDate = NConfig.GetCfgValue("LogFormat", "ShowDate", true);
|
|
ShowTime = NConfig.GetCfgValue("LogFormat", "ShowTime", true);
|
|
ShowMSec = NConfig.GetCfgValue("LogFormat", "ShowMSec", true);
|
|
ShowLevel = NConfig.GetCfgValue("LogFormat", "ShowLevel", true);
|
|
ShowCodeFile = NConfig.GetCfgValue("LogFormat", "ShowCodeFile", true);
|
|
ShowFunction = NConfig.GetCfgValue("LogFormat", "ShowFunction", true);
|
|
ShowCodeLine = NConfig.GetCfgValue("LogFormat", "ShowCodeLine", true);
|
|
|
|
EnConsole = NConfig.GetCfgValue("LogOutput", "Console", true);
|
|
EnTrace = NConfig.GetCfgValue("LogOutput", "Trace", false);
|
|
EnDebug = NConfig.GetCfgValue("LogOutput", "Debug", true);
|
|
EnFile = NConfig.GetCfgValue("LogOutput", "File", false);
|
|
|
|
if (AsyncMode)
|
|
{
|
|
MaxItemsCache = NConfig.GetCfgValue("AsyncLogSetting", "MaxItemsCache", 0);
|
|
|
|
if (MaxItemsCache == 0) MaxItemsCache = int.MaxValue;
|
|
|
|
CacheMode = (CacheOptMode)NConfig.GetCfgValue("AsyncLogSetting", "CacheFullOpts", (int)CacheOptMode.Drop);
|
|
NumOutItems = NConfig.GetCfgValue("AsyncLogSetting", "NumItemsOutEachTime", 10);
|
|
}
|
|
|
|
SleepTime = NConfig.GetCfgValue("AsyncLogSetting", "ThreadSleep", 10);
|
|
|
|
if (EnFile)
|
|
{
|
|
DirPath = NConfig.GetCfgValue("FileLogSetting", "Path", @"");
|
|
|
|
if (DirPath == "" || !Directory.Exists(DirPath) || DirPath == @".\")
|
|
{
|
|
DirPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + @"\";
|
|
}
|
|
|
|
if (!Directory.Exists(DirPath))
|
|
{
|
|
Directory.CreateDirectory(DirPath);
|
|
}
|
|
|
|
FileNamePre = NConfig.GetCfgValue("FileLogSetting", "FileNamePrefix", "");
|
|
EnSplitLog = NConfig.GetCfgValue("FileLogSetting", "AutoSplitFile", true);
|
|
|
|
if (EnSplitLog)
|
|
{
|
|
SplitByData = NConfig.GetCfgValue("SplitFiles", "SplitByDate", true);
|
|
SplitBySize = NConfig.GetCfgValue("SplitFiles", "SplitBySize", 4);
|
|
RoolbackFile = NConfig.GetCfgValue("SplitFiles", "FileNameRollback", true);
|
|
MaxFileNum = NConfig.GetCfgValue("SplitFiles", "MaxFileNameNum", 10);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class NLogItem
|
|
{
|
|
public NLogItem(NLogLevel level = NLogLevel.Debug,
|
|
[NotNull] string logContent = "",
|
|
[NotNull] string fileName = "",
|
|
[NotNull] string funName = "",
|
|
int lineNo = 0,
|
|
DateTime? dt = null)
|
|
{
|
|
if (dt == null)
|
|
{
|
|
LogStamp = DateTime.Now;
|
|
}
|
|
else
|
|
{
|
|
LogStamp = (DateTime)dt;
|
|
}
|
|
|
|
LogLevel = level;
|
|
LogContent = logContent;
|
|
CodeFile = fileName;
|
|
CodeFunction = funName;
|
|
CodeLine = lineNo;
|
|
}
|
|
|
|
public DateTime LogStamp { get; set; }
|
|
public NLogLevel LogLevel { get; set; }
|
|
public string LogContent { get; set; }
|
|
public string CodeFile { get; set; }
|
|
public int CodeLine { get; set; }
|
|
public string CodeFunction { get; set; }
|
|
}
|
|
|
|
public static class NLog
|
|
{
|
|
private static NLogConfig _logCfg;
|
|
private static readonly ConcurrentQueue<NLogItem> _logItemCollection = new ConcurrentQueue<NLogItem>();
|
|
private static readonly object _logOutputLock = new object();
|
|
private static string _logFileName = "";
|
|
private static uint _logFileNum = 0;
|
|
private static StreamWriter _logSw = null;
|
|
|
|
private static void CreateLogFileHead()
|
|
{
|
|
_logSw?.WriteLine("FileName: " + _logFileName);
|
|
_logSw?.WriteLine("CreateTime: " + string.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
|
|
_logSw?.WriteLine("Program: " + Process.GetCurrentProcess().MainModule.FileName);
|
|
_logSw?.WriteLine("PID: " + Process.GetCurrentProcess().Id);
|
|
_logSw?.WriteLine("Split Count: " + (_logFileNum - 1).ToString());
|
|
_logSw?.WriteLine("--------------------------------------------------");
|
|
_logSw?.Flush();
|
|
}
|
|
|
|
private static void ConfigInit()
|
|
{
|
|
if (_logCfg.EnFile)
|
|
{
|
|
_logFileName = string.Format("{0}{1}[{3:yyyy-MM-dd_HH-mm}][{4:d}]_{2}",
|
|
_logCfg.DirPath,
|
|
_logCfg.FileNamePre.Length > 0 ? _logCfg.FileNamePre + "_" : "",
|
|
Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName),
|
|
DateTime.Now,
|
|
Process.GetCurrentProcess().Id);
|
|
|
|
if (_logCfg.EnSplitLog && _logCfg.RoolbackFile && _logCfg.MaxFileNum > 0)
|
|
{
|
|
_logFileName += $"_{_logFileNum:d3}";
|
|
_logFileNum = Convert.ToUInt32((_logFileNum + 1) % _logCfg.MaxFileNum);
|
|
}
|
|
|
|
_logFileName += ".log";
|
|
|
|
if (File.Exists(_logFileName))
|
|
{
|
|
if (_logCfg.EnSplitLog)
|
|
{
|
|
File.Delete(_logFileName);
|
|
}
|
|
}
|
|
|
|
_logSw = new StreamWriter(_logFileName, true);
|
|
CreateLogFileHead();
|
|
}
|
|
}
|
|
|
|
public static void NLog_Finish()
|
|
{
|
|
if (_logCfg.EnFile)
|
|
{
|
|
_logSw?.Flush();
|
|
_logSw?.Close();
|
|
}
|
|
}
|
|
|
|
public static void NLog_Init()
|
|
{
|
|
_logCfg = new NLogConfig();
|
|
|
|
NLogConfig.OnLogCfgChanged += () =>
|
|
{
|
|
_logSw?.Close();
|
|
|
|
_logCfg.LogConfig();
|
|
|
|
ConfigInit();
|
|
};
|
|
|
|
ConfigInit();
|
|
|
|
var asynWork = new Thread(() =>
|
|
{
|
|
uint cnt = 0;
|
|
|
|
while (true)
|
|
{
|
|
var lastDt = DateTime.Now;
|
|
var tolOut = Math.Min(_logCfg.NumOutItems, _logItemCollection.Count);
|
|
|
|
foreach (var val in Enumerable.Range(1, tolOut))
|
|
{
|
|
if (_logItemCollection.TryDequeue(out var logItem))
|
|
{
|
|
LogOutput(logItem);
|
|
}
|
|
}
|
|
|
|
Thread.Sleep(_logCfg.SleepTime);
|
|
|
|
// 每秒执行一次维护工作
|
|
if ((++cnt % (1000 / _logCfg.SleepTime)) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_logSw?.Flush();
|
|
|
|
if (_logCfg.EnSplitLog)
|
|
{
|
|
bool isNeedSplit = false;
|
|
|
|
if (_logCfg.SplitByData && lastDt.Day != DateTime.Now.Day)
|
|
{
|
|
isNeedSplit = true;
|
|
}
|
|
else if (_logCfg.SplitBySize > 0 && (new FileInfo(_logFileName)).Length > _logCfg.SplitBySize * (1024 * 1024))
|
|
{
|
|
isNeedSplit = true;
|
|
}
|
|
|
|
if (isNeedSplit)
|
|
{
|
|
_logSw?.Close();
|
|
_logSw?.Dispose();
|
|
_logSw = null;
|
|
|
|
string parttn = string.Format("*[{0:d3}]_{1}",
|
|
Process.GetCurrentProcess().Id,
|
|
Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName));
|
|
|
|
_logFileName = string.Format("{0}{1}[{3:yyyy-MM-dd_HH-mm}][{4:d}]_{2}",
|
|
_logCfg.DirPath,
|
|
_logCfg.FileNamePre.Length > 0 ? _logCfg.FileNamePre + "_" : "",
|
|
Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName),
|
|
DateTime.Now,
|
|
Process.GetCurrentProcess().Id);
|
|
|
|
if (_logCfg.EnSplitLog && _logCfg.RoolbackFile && _logCfg.MaxFileNum > 0)
|
|
{
|
|
_logFileName += $"_{_logFileNum:d3}";
|
|
parttn += $"_{_logFileNum:d3}";
|
|
_logFileNum = Convert.ToUInt32((_logFileNum + 1) % _logCfg.MaxFileNum);
|
|
}
|
|
|
|
_logFileName += ".log";
|
|
parttn += ".log";
|
|
|
|
foreach (var f in Directory.GetFiles(_logCfg.DirPath, parttn, SearchOption.TopDirectoryOnly))
|
|
{
|
|
File.Delete(f);
|
|
Trace.WriteLine("Delect Rollback log: " + f);
|
|
}
|
|
|
|
_logSw = new StreamWriter(_logFileName, true);
|
|
CreateLogFileHead();
|
|
}
|
|
}
|
|
}
|
|
})
|
|
{
|
|
Name = "Log Async Output Thread",
|
|
IsBackground = true
|
|
};
|
|
|
|
asynWork.Start();
|
|
}
|
|
|
|
private static string LogLevelToString([NotNull] NLogLevel logLevel)
|
|
{
|
|
string[] level = { "F", "C", "E", "I", "W", "D" };
|
|
|
|
if ((int) logLevel < level.Length && (int) logLevel >= 0)
|
|
return level[(int) logLevel];
|
|
return "U";
|
|
}
|
|
|
|
private static string LogFormat(NLogLevel logLevel, string logMsg, string fileName, string funName, int lineNo,
|
|
DateTime dt)
|
|
{
|
|
var msg = "";
|
|
|
|
if (_logCfg.ShowDate || _logCfg.ShowTime)
|
|
{
|
|
msg += "[";
|
|
}
|
|
|
|
if (_logCfg.ShowDate)
|
|
{
|
|
msg += dt.ToString("yyyy-MM-dd");
|
|
|
|
if (_logCfg.ShowTime)
|
|
{
|
|
msg += " ";
|
|
}
|
|
}
|
|
|
|
if (_logCfg.ShowTime)
|
|
{
|
|
msg += dt.ToString("HH:mm:ss");
|
|
|
|
if (_logCfg.ShowMSec)
|
|
{
|
|
msg += "." + dt.ToString("fff");
|
|
}
|
|
}
|
|
|
|
if (_logCfg.ShowDate || _logCfg.ShowTime)
|
|
{
|
|
msg += "]";
|
|
}
|
|
|
|
if (_logCfg.ShowLevel)
|
|
{
|
|
msg += " [" + LogLevelToString(logLevel) + "] ";
|
|
}
|
|
|
|
if (_logCfg.ShowCodeFile)
|
|
{
|
|
msg += "[" + Path.GetFileName(fileName) + "] ";
|
|
}
|
|
|
|
if (_logCfg.ShowFunction)
|
|
{
|
|
msg += "- " + funName;
|
|
}
|
|
|
|
if (_logCfg.ShowCodeFile || _logCfg.ShowFunction)
|
|
{
|
|
if (_logCfg.ShowCodeLine)
|
|
{
|
|
msg += "(" + lineNo.ToString() + ")";
|
|
}
|
|
}
|
|
|
|
msg += ": " + logMsg;
|
|
|
|
return msg;
|
|
}
|
|
|
|
private static void LogOutput(NLogItem logItem)
|
|
{
|
|
var msg = LogFormat(logItem.LogLevel,
|
|
logItem.LogContent,
|
|
logItem.CodeFile,
|
|
logItem.CodeFunction,
|
|
logItem.CodeLine,
|
|
logItem.LogStamp);
|
|
|
|
if (_logCfg.ForceNewLine)
|
|
{
|
|
msg += Environment.NewLine;
|
|
}
|
|
|
|
lock (_logOutputLock)
|
|
{
|
|
if (_logCfg.EnConsole) Console.Write(msg);
|
|
|
|
if (_logCfg.EnDebug | _logCfg.EnTrace)
|
|
{
|
|
if (_logCfg.EnTrace)
|
|
Trace.Write(msg);
|
|
else
|
|
System.Diagnostics.Debug.WriteLine(msg);
|
|
}
|
|
|
|
if (_logCfg.EnFile)
|
|
{
|
|
_logSw?.Write(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void LogOutput2(string logMsg)
|
|
{
|
|
lock (_logOutputLock)
|
|
{
|
|
if (_logCfg.EnConsole) Console.Write(logMsg);
|
|
|
|
if (_logCfg.EnDebug | _logCfg.EnTrace)
|
|
{
|
|
if (_logCfg.EnTrace)
|
|
Trace.Write(logMsg);
|
|
else
|
|
System.Diagnostics.Debug.WriteLine(logMsg);
|
|
}
|
|
|
|
if (_logCfg.EnFile)
|
|
{
|
|
_logSw?.Write(logMsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void LogOut([NotNull] string logMsg = "",
|
|
[CallerFilePath] string fileName = "",
|
|
[CallerMemberName] string funName = "",
|
|
[CallerLineNumber] int lineNo = 0)
|
|
{
|
|
if (NLogLevel.Debug >= _logCfg.LogLevel || !_logCfg.LogEnable) return;
|
|
|
|
if (_logCfg.AsyncMode)
|
|
{
|
|
if (_logItemCollection.Count >= _logCfg.MaxItemsCache)
|
|
{
|
|
if (_logCfg.CacheMode == CacheOptMode.Drop)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
NLogItem val;
|
|
_logItemCollection.TryDequeue(out val);
|
|
}
|
|
}
|
|
|
|
var logItem = new NLogItem(_logCfg.DefaultLevel, logMsg, fileName, funName, lineNo, DateTime.Now);
|
|
_logItemCollection.Enqueue(logItem);
|
|
}
|
|
else
|
|
{
|
|
var logItem = new NLogItem(_logCfg.DefaultLevel, logMsg, fileName, funName, lineNo, DateTime.Now);
|
|
LogOutput(logItem);
|
|
}
|
|
}
|
|
|
|
public static void LogOut(NLogLevel logLevel = NLogLevel.Info,
|
|
[NotNull] string logMsg = "",
|
|
[CallerFilePath] string fileName = "",
|
|
[CallerMemberName] string funName = "",
|
|
[CallerLineNumber] int lineNo = 0)
|
|
{
|
|
if (logLevel >= _logCfg.LogLevel || !_logCfg.LogEnable) return;
|
|
|
|
if (_logCfg.AsyncMode)
|
|
{
|
|
if (_logItemCollection.Count >= _logCfg.MaxItemsCache)
|
|
{
|
|
if (_logCfg.CacheMode == CacheOptMode.Drop)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
NLogItem val;
|
|
_logItemCollection.TryDequeue(out val);
|
|
}
|
|
}
|
|
|
|
var logItem = new NLogItem(NLogLevel.Debug, logMsg, fileName, funName, lineNo, DateTime.Now);
|
|
_logItemCollection.Enqueue(logItem);
|
|
}
|
|
else
|
|
{
|
|
var logItem = new NLogItem(NLogLevel.Debug, logMsg, fileName, funName, lineNo, DateTime.Now);
|
|
LogOutput(logItem);
|
|
}
|
|
}
|
|
|
|
#region LogOutputMethod
|
|
|
|
public static void Debug([NotNull] string logMsg = "",
|
|
[CallerFilePath] string fileName = "",
|
|
[CallerMemberName] string funName = "",
|
|
[CallerLineNumber] int lineNo = 0)
|
|
{
|
|
LogOut(NLogLevel.Debug, logMsg, fileName, funName, lineNo);
|
|
}
|
|
|
|
public static void Warring([NotNull] string logMsg = "",
|
|
[CallerFilePath] string fileName = "",
|
|
[CallerMemberName] string funName = "",
|
|
[CallerLineNumber] int lineNo = 0)
|
|
{
|
|
LogOut(NLogLevel.Warring, logMsg, fileName, funName, lineNo);
|
|
}
|
|
|
|
public static void Info([NotNull] string logMsg = "",
|
|
[CallerFilePath] string fileName = "",
|
|
[CallerMemberName] string funName = "",
|
|
[CallerLineNumber] int lineNo = 0)
|
|
{
|
|
LogOut(NLogLevel.Info, logMsg, fileName, funName, lineNo);
|
|
}
|
|
|
|
public static void Error([NotNull] string logMsg = "",
|
|
[CallerFilePath] string fileName = "",
|
|
[CallerMemberName] string funName = "",
|
|
[CallerLineNumber] int lineNo = 0)
|
|
{
|
|
LogOut(NLogLevel.Error, logMsg, fileName, funName, lineNo);
|
|
}
|
|
|
|
public static void Crash([NotNull] string logMsg = "",
|
|
[CallerFilePath] string fileName = "",
|
|
[CallerMemberName] string funName = "",
|
|
[CallerLineNumber] int lineNo = 0)
|
|
{
|
|
LogOut(NLogLevel.Crash, logMsg, fileName, funName, lineNo);
|
|
}
|
|
|
|
public static void Fatal([NotNull] string logMsg = "",
|
|
[CallerFilePath] string fileName = "",
|
|
[CallerMemberName] string funName = "",
|
|
[CallerLineNumber] int lineNo = 0)
|
|
{
|
|
LogOut(NLogLevel.Fatal, logMsg, fileName, funName, lineNo);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|