// *********************************************************************** // Assembly : GeneratorCode // Author : 黄昕 // Created : 02-15-2019 // // Last Modified By : 黄昕 // Last Modified On : 02-20-2019 // *********************************************************************** // // Copyright © 2019 // // // *********************************************************************** using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Threading; using GeneratorCode.Configure; using JetBrains.Annotations; using Timer = System.Timers.Timer; namespace GeneratorCode.Logs { /// /// Enum CacheOptMode /// public enum CacheOptMode { /// /// The drop /// Drop = 0, /// /// The replease /// Replease } /// /// Enum NLogLevel /// public enum NLogLevel { /// /// The fatal /// Fatal = 0, /// /// The crash /// Crash, /// /// The error /// Error, /// /// The warring /// Warring, /// /// The information /// Info, /// /// The debug /// Debug, /// /// The maximum level /// MaxLevel } /// /// Class NLog. /// public static class NLog { /// /// The log item collection /// private static readonly ConcurrentQueue LogItemCollection = new ConcurrentQueue(); /// /// The log output lock /// private static readonly object LogOutputLock = new object(); /// /// The log CFG /// private static NLogConfig _logCfg; /// /// The log file name /// private static string _logFileName = ""; /// /// The log file number /// private static uint _logFileNum; /// /// The log sw /// private static StreamWriter _logSw; /// /// Logs the out. /// /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. 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; LogItemCollection.TryDequeue(out _); } 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); } } /// /// ns the log finish. /// public static void NLog_Finish() { if (_logCfg.EnFile) { _logSw?.Flush(); _logSw?.Close(); } } /// /// ns the log initialize. /// public static void NLog_Init() { _logCfg = new NLogConfig(); NLogConfig.OnLogCfgChanged += () => { _logSw?.Close(); _logCfg.LogConfig(); ConfigInit(); }; ConfigInit(); var asynWork = new Thread(() => { uint cnt = 1; while (cnt++ > 0) { var lastDt = DateTime.Now; var tolOut = Math.Min(_logCfg.NumOutItems, LogItemCollection.Count); for (var i = 0; i < tolOut; i++) { if (LogItemCollection.TryDequeue(out var logItem)) { LogOutput(logItem); } } Thread.Sleep(_logCfg.SleepTime); // 每秒执行一次维护工作 if (cnt % (1000 / _logCfg.SleepTime) != 0) continue; if (_logCfg.EnSplitLog) { _logSw?.Flush(); var 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; var 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.EnumerateFiles(_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(); var tm = new Timer(1000) {AutoReset = true}; tm.Elapsed += (obj, args) => { if (!_logCfg.AutoCleanup) return; var backFileName = string.Format("{0}[{1:yyyy-MM-dd_HH-mm}]_{2}.zip", _logCfg.DirPath, DateTime.Now, Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName)); const string regexRule = @"\[(?[1,2][0-9][0-9][0-9])-(?[0,1][0-2])-(?[0-3][0-9]).(?[0-2][0-9])-(?[0-5][0-9])\]"; var regex = new Regex(regexRule); var backList = new List(); foreach (var f in Directory.EnumerateFiles(_logCfg.DirPath, "*.zip", SearchOption.TopDirectoryOnly)) { if (regex.IsMatch(f)) { var match = regex.Match(f); var dt = DateTime.ParseExact(match.Value, "[yyyy-MM-dd_HH-mm]", CultureInfo.CurrentCulture); var diff = (DateTime.Now - dt).Days; if (diff > _logCfg.KeepDays) { File.Delete(f); } } } foreach (var f in Directory.EnumerateFiles(_logCfg.DirPath, "*.log", SearchOption.TopDirectoryOnly)) { if (regex.IsMatch(f)) { var match = regex.Match(f); var dt = DateTime.ParseExact(match.Value, "[yyyy-MM-dd_HH-mm]", CultureInfo.CurrentCulture); var diff = (DateTime.Now - dt).Days; if (diff > _logCfg.KeepDays) { File.Delete(f); } else if (diff > _logCfg.BackupDays) { backList.Add(f); } } } foreach (var f in backList) { if (f == _logFileName) continue; using (var fs = new FileStream(backFileName, FileMode.OpenOrCreate)) { using (var archive = new ZipArchive(fs, ZipArchiveMode.Update)) { archive.CreateEntryFromFile(f, Path.GetFileName(f)); File.Delete(f); } } } }; tm.Start(); } /// /// Configurations the initialize. /// 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(); } } /// /// Creates the log file head. /// 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)); _logSw?.WriteLine("--------------------------------------------------"); _logSw?.Flush(); } /// /// Logs the format. /// /// The log level. /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. /// The dt. /// System.String. 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 + ")"; } } msg += ": " + logMsg; return msg; } /// /// Logs the level to string. /// /// The log level. /// System.String. private static string LogLevelToString(NLogLevel logLevel) { string[] level = {"F", "C", "E", "I", "W", "D"}; if ((int) logLevel < level.Length && (int) logLevel >= 0) { return level[(int) logLevel]; } return "U"; } /// /// Logs the out. /// /// The log level. /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. private static void LogOut(NLogLevel logLevel = NLogLevel.Info, [NotNull] string logMsg = "", string fileName = "", string funName = "", int lineNo = 0) { if (logLevel >= _logCfg.LogLevel || !_logCfg.LogEnable) return; if (_logCfg.AsyncMode) { if (LogItemCollection.Count >= _logCfg.MaxItemsCache) { if (_logCfg.CacheMode == CacheOptMode.Drop) return; LogItemCollection.TryDequeue(out _); } 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); } } /// /// Logs the output. /// /// The log item. 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); } } #region LogOutputMethod /// /// Crashes the specified log MSG. /// /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. public static void Crash([NotNull] string logMsg = "", [CallerFilePath] string fileName = "", [CallerMemberName] string funName = "", [CallerLineNumber] int lineNo = 0) { LogOut(NLogLevel.Crash, logMsg, fileName, funName, lineNo); } /// /// Debugs the specified log MSG. /// /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. public static void Debug([NotNull] string logMsg = "", [CallerFilePath] string fileName = "", [CallerMemberName] string funName = "", [CallerLineNumber] int lineNo = 0) { LogOut(NLogLevel.Debug, logMsg, fileName, funName, lineNo); } /// /// Errors the specified log MSG. /// /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. public static void Error([NotNull] string logMsg = "", [CallerFilePath] string fileName = "", [CallerMemberName] string funName = "", [CallerLineNumber] int lineNo = 0) { LogOut(NLogLevel.Error, logMsg, fileName, funName, lineNo); } /// /// Fatals the specified log MSG. /// /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. public static void Fatal([NotNull] string logMsg = "", [CallerFilePath] string fileName = "", [CallerMemberName] string funName = "", [CallerLineNumber] int lineNo = 0) { LogOut(NLogLevel.Fatal, logMsg, fileName, funName, lineNo); } /// /// Informations the specified log MSG. /// /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. public static void Info([NotNull] string logMsg = "", [CallerFilePath] string fileName = "", [CallerMemberName] string funName = "", [CallerLineNumber] int lineNo = 0) { LogOut(NLogLevel.Info, logMsg, fileName, funName, lineNo); } /// /// Warrings the specified log MSG. /// /// The log MSG. /// Name of the file. /// Name of the fun. /// The line no. public static void Warring([NotNull] string logMsg = "", [CallerFilePath] string fileName = "", [CallerMemberName] string funName = "", [CallerLineNumber] int lineNo = 0) { LogOut(NLogLevel.Warring, logMsg, fileName, funName, lineNo); } #endregion LogOutputMethod } /// /// Class NLogConfig. /// public class NLogConfig { /// /// Delegate LogCfgChangedHandle /// public delegate void LogCfgChangedHandle(); /// /// The CFG lock /// private readonly object _cfgLock = new object(); /// /// Initializes a new instance of the class. /// public NLogConfig() { LogConfig(); NConfig.OnConfigChanged += () => { LogCfgChanged(); }; } /// /// Gets or sets a value indicating whether [asynchronous mode]. /// /// true if [asynchronous mode]; otherwise, false. public bool AsyncMode { get; set; } /// /// Gets or sets a value indicating whether [automatic cleanup]. /// /// true if [automatic cleanup]; otherwise, false. public bool AutoCleanup { get; set; } /// /// Gets or sets the backup days. /// /// The backup days. public int BackupDays { get; set; } /// /// Gets or sets the cache mode. /// /// The cache mode. public CacheOptMode CacheMode { get; set; } /// /// Gets or sets the default level. /// /// The default level. public NLogLevel DefaultLevel { get; set; } /// /// Gets or sets the dir path. /// /// The dir path. public string DirPath { get; set; } /// /// Gets or sets a value indicating whether [en console]. /// /// true if [en console]; otherwise, false. public bool EnConsole { get; set; } /// /// Gets or sets a value indicating whether [en debug]. /// /// true if [en debug]; otherwise, false. public bool EnDebug { get; set; } /// /// Gets or sets a value indicating whether [en file]. /// /// true if [en file]; otherwise, false. public bool EnFile { get; set; } /// /// Gets or sets a value indicating whether [en split log]. /// /// true if [en split log]; otherwise, false. public bool EnSplitLog { get; set; } /// /// Gets or sets a value indicating whether [en trace]. /// /// true if [en trace]; otherwise, false. public bool EnTrace { get; set; } /// /// Gets or sets the file name pre. /// /// The file name pre. public string FileNamePre { get; set; } /// /// Gets or sets a value indicating whether [force new line]. /// /// true if [force new line]; otherwise, false. public bool ForceNewLine { get; set; } /// /// Gets or sets the keep days. /// /// The keep days. public int KeepDays { get; set; } /// /// Gets or sets a value indicating whether [log enable]. /// /// true if [log enable]; otherwise, false. public bool LogEnable { get; set; } /// /// Gets or sets the log level. /// /// The log level. public NLogLevel LogLevel { get; set; } /// /// Gets or sets the maximum file number. /// /// The maximum file number. public int MaxFileNum { get; set; } /// /// Gets or sets the maximum items cache. /// /// The maximum items cache. public int MaxItemsCache { get; set; } /// /// Gets or sets the number out items. /// /// The number out items. public int NumOutItems { get; set; } /// /// Gets or sets a value indicating whether [roolback file]. /// /// true if [roolback file]; otherwise, false. public bool RoolbackFile { get; set; } /// /// Gets or sets a value indicating whether [show code file]. /// /// true if [show code file]; otherwise, false. public bool ShowCodeFile { get; set; } /// /// Gets or sets a value indicating whether [show code line]. /// /// true if [show code line]; otherwise, false. public bool ShowCodeLine { get; set; } /// /// Gets or sets a value indicating whether [show date]. /// /// true if [show date]; otherwise, false. public bool ShowDate { get; set; } /// /// Gets or sets a value indicating whether [show function]. /// /// true if [show function]; otherwise, false. public bool ShowFunction { get; set; } /// /// Gets or sets a value indicating whether [show level]. /// /// true if [show level]; otherwise, false. public bool ShowLevel { get; set; } /// /// Gets or sets a value indicating whether [show m sec]. /// /// true if [show m sec]; otherwise, false. public bool ShowMSec { get; set; } /// /// Gets or sets a value indicating whether [show time]. /// /// true if [show time]; otherwise, false. public bool ShowTime { get; set; } /// /// Gets or sets the sleep time. /// /// The sleep time. public int SleepTime { get; set; } /// /// Gets or sets a value indicating whether [split by data]. /// /// true if [split by data]; otherwise, false. public bool SplitByData { get; set; } /// /// Gets or sets the size of the split by. /// /// The size of the split by. public int SplitBySize { get; set; } /// /// Occurs when [on log CFG changed]. /// public static event LogCfgChangedHandle OnLogCfgChanged; /// /// Logs the configuration. /// 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); AutoCleanup = NConfig.GetCfgValue("LogGlobal", "AutoCleanup", true); 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); if (!DirPath.EndsWith(Path.DirectorySeparatorChar.ToString())) { DirPath += Path.DirectorySeparatorChar; } 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); } } if (AutoCleanup) { KeepDays = NConfig.GetCfgValue("CleanUpLog", "KeepSaveDays", 30); BackupDays = NConfig.GetCfgValue("CleanUpLog", "ZipBeforDays", 1); } } } /// /// Logs the CFG changed. /// protected static void LogCfgChanged() { OnLogCfgChanged?.Invoke(); } } /// /// Class NLogItem. /// public class NLogItem { /// /// Initializes a new instance of the class. /// /// The level. /// Content of the log. /// Name of the file. /// Name of the fun. /// The line no. /// The dt. 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; } /// /// Gets or sets the code file. /// /// The code file. public string CodeFile { get; set; } /// /// Gets or sets the code function. /// /// The code function. public string CodeFunction { get; set; } /// /// Gets or sets the code line. /// /// The code line. public int CodeLine { get; set; } /// /// Gets or sets the content of the log. /// /// The content of the log. public string LogContent { get; set; } /// /// Gets or sets the log level. /// /// The log level. public NLogLevel LogLevel { get; set; } /// /// Gets or sets the log stamp. /// /// The log stamp. public DateTime LogStamp { get; set; } } }