using log4net; using log4net.Appender; using log4net.Config; using log4net.Repository.Hierarchy; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using static log4net.Appender.RollingFileAppender; namespace JWH { /// /// log4net을 활용한 로그작성 라이브러리 /// [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)] /// public class XLogger { #region [ Static ] ==================================================== /// /// Logger root /// public static XLogger Instance { get; set; } = new XLogger(); /// /// RollingFileAppender를 생성한다 /// /// Appender.Name /// Appender.File /// Appender.ConversionPattern /// public static RollingFileAppender CreateRollingFileAppender(string name, string path, string pattern) { log4net.Appender.RollingFileAppender appender = new log4net.Appender.RollingFileAppender(); try { appender.Name = name; appender.File = $"{path}{name}_"; appender.DatePattern = $"yyyyMMddHH'.log'"; //appender.MaxSizeRollBackups = 100; appender.MaximumFileSize = "10MB"; appender.StaticLogFileName = false; appender.AppendToFile = true; appender.PreserveLogFileNameExtension = true; appender.RollingStyle = log4net.Appender.RollingFileAppender.RollingMode.Date; appender.LockingModel = new RollingFileAppender.MinimalLock(); //appender.CountDirection = XLogger.GetCountDirection(path, $"{name}_", appender.DatePattern); log4net.Layout.PatternLayout layout = new log4net.Layout.PatternLayout() { ConversionPattern = pattern }; layout.ActivateOptions(); appender.Layout = layout; appender.ActivateOptions(); } catch (Exception ex) { XLogger.Instance.Fatal(ex); } return appender; } /// /// log4net.GlobalContext.Properties /// You must call ConfigureAndWatch /// /// /// public static void AddProperties(string name, string value) { log4net.GlobalContext.Properties[name] = value; } /// /// log4net.GlobalContext.Properties /// /// /// public static string GetProperties(string name) { return log4net.GlobalContext.Properties[name] as string; } /// /// XmlConfigurator.ConfigureAndWatch /// /// public static void ConfigureAndWatch(FileInfo file) { if (file?.Exists == false) return; XmlConfigurator.ConfigureAndWatch(file); } #endregion #region [ Enum ] ====================================================== /// XLogLevel public enum eXLogLevel { None = 0, Debug = 1, Info = 2, Warn = 3, Error = 4, Fatal = 5 } #endregion #region [ Variable ] ================================================== /// Filter Types private List m_FilterTypes = new List(); #endregion #region [ Properties ] ================================================ /// Logger Name public string Name { get; set; } = string.Empty; /// log4net.Repository.Hierarchy.Logger public Logger Logger { get { return this.GetLogger(); } } /// LOG Control public TextBoxBase Control { get; set; } = null; /// Control.ScrollToCaret public bool OnScrollToCaret { get; set; } = true; /// LOG Control : 0.DateTime, 1.LogLevel, 2.LogName, 3.ClassFullName, 4.Method, 5.Message, 6.ClassName /// public string ControlPatternLayout { get; set; } = "[{0}] {1} {2} {3}.{4}() {5}"; /// Last Deleted LogFile public DateTime LastDeletedDateTime { get; set; } = DateTime.MinValue; /// Filter Types public List FilterTypes { get { return m_FilterTypes; } } /// RichTextBox.SelectionColor public Color ForeColor { get; set; } = SystemColors.ControlText; #endregion #region [ XLogger ] =================================================== /// /// XLogger /// public XLogger() { } /// /// XLogger /// /// Logger Name defined in log4net.config public XLogger(string name) { this.Name = name; } #endregion #region [ Method ] ==================================================== #region [ Appender ] -------------------------------------------------- /// /// 동적으로 RollingFileAppender를 추가한다 /// public IAppender AddRollingFileAppender(string path, string pattern) { FileAppender appender = null; try { string name = this.Name; appender = XLogger.CreateRollingFileAppender(name, path, pattern); this.AddAppender(appender); } catch (Exception ex) { XLogger.Instance.Fatal(ex); } return appender; } /// /// 동적으로 RollingFileAppender를 추가한다 /// /// /// /// public void AddRollingFileAppender(string name, string file, string pattern) { try { log4net.Repository.Hierarchy.Logger logger = this.Logger; RollingFileAppender appender = XLogger.CreateRollingFileAppender(name, file, pattern); this.AddAppender(appender); } catch (Exception ex) { XLogger.Instance.Fatal(ex); } } /// /// 다른 XLogger의 Appender를 추가한다 /// /// /// public void AddRollingFileAppender(XLogger srcXLogger, int index = 0) { try { log4net.Repository.Hierarchy.Logger srclogger = srcXLogger.Logger; if (srclogger.Appenders.Count <= index) return; IAppender srcAppender = srclogger.Appenders[index]; this.AddAppender(srcAppender); } catch (Exception ex) { XLogger.Instance.Fatal(ex); } } /// /// Appender를 추가한다 /// /// public void AddAppender(IAppender appender) { try { log4net.Repository.Hierarchy.Logger logger = this.Logger; FileAppender fileAppender = null; IAppender removeItem = null; foreach (IAppender item in logger.Appenders) { if (item.Name == appender.Name) { removeItem = item; break; } } if (removeItem != null) { fileAppender = removeItem as FileAppender; logger.RemoveAppender(removeItem).Close(); } fileAppender = appender as FileAppender; logger.AddAppender(appender); } catch (Exception ex) { XLogger.Instance.Fatal(ex); } } /// /// 모든 Appender를 제거한다 /// public void ClearAppender() { log4net.Repository.Hierarchy.Logger logger = this.Logger; List lstAppender = new List(); foreach (IAppender item in logger.Appenders) lstAppender.Add(item); foreach (IAppender item in lstAppender) logger.RemoveAppender(item).Close(); } #endregion #region [ MDC ] ------------------------------------------------------- /// /// MDC Key /// private enum eMDCKey { Logger, Name, Class, Method } /// /// Set MDC /// /// /// private void SetMDC(eMDCKey mdcKey, string value) { try { MDC.Set(mdcKey.ToString().ToLower(), value); } catch { } } /// /// Set MDC /// /// /// private void SetMDC(string mdcKey, string value) { try { MDC.Set(mdcKey.ToLower(), value); } catch { } } /// /// Clear MDC /// private void ClearMDC() { foreach (string mdcKey in Enum.GetNames(typeof(eMDCKey))) SetMDC(mdcKey, string.Empty); } #endregion #region [ Filter ] ---------------------------------------------------- /// /// Add Filter(Type) /// /// public void AddFilterType(params Type[] types) { m_FilterTypes.AddRange(types); } /// /// Clear Filter(Type) /// public void ClearFilterType() { m_FilterTypes.Clear(); } #endregion #region [ Debug ] ----------------------------------------------------- /// /// Debug /// /// /// public string Debug(object value = null, Color? color = null) { return this.WriteLine(eXLogLevel.Debug, value, color); } /// /// Debug /// /// /// /// public string Debug(string message, Exception e, Color? color = null) { return this.WriteLine(eXLogLevel.Debug, message, e, color); } #endregion #region [ Info ] ------------------------------------------------------ /// /// Info /// /// /// public string Info(object value = null, Color? color = null) { return this.WriteLine(eXLogLevel.Info, value, color); } /// /// Info /// /// /// /// public string Info(string message, Exception e, Color? color = null) { return this.WriteLine(eXLogLevel.Info, message, e, color); } #endregion #region [ Warn ] ------------------------------------------------------ /// /// Warn /// /// /// public string Warn(object value = null, Color? color = null) { return this.WriteLine(eXLogLevel.Warn, value, color); } /// /// Warn /// /// /// /// public string Warn(string message, Exception e, Color? color = null) { return this.WriteLine(eXLogLevel.Warn, message, e, color); } #endregion #region [ Error ] ----------------------------------------------------- /// /// Error /// /// /// public string Error(object value = null, Color? color = null) { return this.WriteLine(eXLogLevel.Error, value, color); } /// /// Error /// /// /// /// public string Error(string message, Exception e, Color? color = null) { return this.WriteLine(eXLogLevel.Error, message, e, color); } #endregion #region [ Fatal ] ----------------------------------------------------- /// /// Fatal /// /// public string Fatal(object value, bool showMessageBox) { string message = this.WriteLine(eXLogLevel.Fatal, value); if (showMessageBox) MessageBox.Show(message, "Fatal", MessageBoxButtons.OK, MessageBoxIcon.Error); return message; } /// /// Fatal /// /// /// public string Fatal(object value = null, Color? color = null) { string result = this.WriteLine(eXLogLevel.Fatal, value, color); return result; } /// /// Fatal /// /// /// /// public string Fatal(string message, Exception e, Color? color = null) { string result = this.WriteLine(eXLogLevel.Fatal, message, e, color); return result; } /// /// Fatal /// /// /// Show MessageBox /// private string xFatal(object value = null, bool showMessageBox = false, Color? color = null) { string result = this.WriteLine(eXLogLevel.Fatal, value, color); if (showMessageBox) MessageBox.Show(result, "Fatal", MessageBoxButtons.OK, MessageBoxIcon.Error); return result; } /// /// Fatal /// /// /// /// Show MessageBox /// private string xFatal(string message, Exception e, bool showMessageBox = false, Color? color = null) { string result = this.WriteLine(eXLogLevel.Fatal, message, e, color); if (showMessageBox) MessageBox.Show(result, "Fatal", MessageBoxButtons.OK, MessageBoxIcon.Error); return result; } #endregion #region [ WriteLine ] ------------------------------------------------- private StackFrame GetStackFrame(StackTrace trace) { try { foreach (StackFrame frame in trace.GetFrames()) { MethodBase method = frame.GetMethod(); if (method.DeclaringType != typeof(XLogger)) return frame; } return trace.GetFrame(2); } catch (Exception ex) { Trace.WriteLine(ex.Message); return trace.GetFrame(2); } } /// /// 지정된 문자열 값을 log4net 혹은 표준 출력 스트림에 출력합니다 /// /// /// public string WriteLine(eXLogLevel level, object value = null, Color? color = null) { try { StackTrace trace = new StackTrace(); //StackFrame frame = trace.GetFrame(2); StackFrame frame = this.GetStackFrame(trace); MethodBase method = frame.GetMethod(); Exception ex = null; string strMessage = string.Empty; if (value != null) { if (value is Exception) { ex = value as Exception; //strMessage = ex.Message; } else { strMessage = value.ToString(); } } return WriteLine(level, strMessage, method, ex, color); } catch (Exception ex) { Trace.WriteLine(ex.Message); return ex.Message; } } /// /// 지정된 문자열 값을 log4net 혹은 표준 출력 스트림에 출력합니다 /// /// /// /// public string WriteLine(eXLogLevel level, string message, Exception e, Color? color = null) { try { StackTrace trace = new StackTrace(); //StackFrame frame = trace.GetFrame(2); StackFrame frame = this.GetStackFrame(trace); MethodBase method = frame.GetMethod(); string strMessage = string.Empty; strMessage = message; return WriteLine(level, strMessage, method, e); } catch (Exception ex) { Trace.WriteLine(ex.Message); return ex.Message; } } /// /// 지정된 문자열 값을 log4net 혹은 표준 출력 스트림에 출력합니다 /// /// /// /// public string WriteLine(eXLogLevel level, string message, MethodBase method, Exception e = null, Color? color = null) { string result = string.Empty; try { string name = this.Name; string strDTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); string strClassFullName = string.Empty; string strClassName = string.Empty; string strMethod = string.Empty; string strLog = string.Empty; string strConsole = string.Empty; ILog log = LogManager.GetLogger(name); ClearMDC(); if (method == null) { StackTrace trace = new StackTrace(); StackFrame frame = trace.GetFrame(1); method = frame.GetMethod(); } if (method != null && method.DeclaringType != null) { if (m_FilterTypes.Count > 0 && m_FilterTypes.Contains(method.DeclaringType) == false) return result; strClassFullName = method.DeclaringType.FullName; strClassName = method.DeclaringType.Name; strMethod = method.Name; } if (message.EndsWith(Environment.NewLine)) message = message.Remove(message.Length - Environment.NewLine.Length); strLog = message; strConsole = string.Format(this.ControlPatternLayout, strDTime, level.ToString().ToUpper(), name, strClassFullName, strMethod, message, strClassName); SetMDC(eMDCKey.Name, name); SetMDC(eMDCKey.Class, strClassFullName); SetMDC(eMDCKey.Method, strMethod); result = $"{strConsole}" + (e != null ? string.Format("{0}{2}", Environment.NewLine, e.Message, e.ToString()) : ""); switch (level) { case eXLogLevel.Debug: if (log.IsDebugEnabled) { log.Debug(strLog, e); this.WriteControl(result, color); } break; case eXLogLevel.Info: if (log.IsInfoEnabled) { log.Info(strLog, e); this.WriteControl(result, color); } break; case eXLogLevel.Warn: if (log.IsWarnEnabled) { log.Warn(strLog, e); this.WriteControl(result, color); } break; case eXLogLevel.Error: if (log.IsErrorEnabled) { log.Error(strLog); this.WriteControl(result, color); } break; case eXLogLevel.Fatal: if (log.IsFatalEnabled) { log.Fatal(strLog, e); this.WriteControl(result, color); } break; } //this.DeleteRollingFiles(); return result; } catch (Exception ex) { Trace.WriteLine(ex.ToString()); return ex.ToString(); } } /// /// Control.Text에 로그를 기록한다 /// /// private void WriteControl(string message, Color? color = null) { try { if (this.Control == null) return; if (color.Equals(Color.Transparent)) return; if (this.Control.InvokeRequired) { this.Control.Invoke((MethodInvoker)delegate { this.WriteControl(message, color); }); return; } Application.DoEvents(); this.Control.SuspendLayout(); if (this.Control.MaxLength <= this.Control.TextLength + message.Length) { int count = 0; while (this.Control.MaxLength * 0.9 <= this.Control.TextLength + message.Length) { this.Control.SelectionStart = 0; this.Control.SelectionLength = this.Control.GetFirstCharIndexFromLine(count + 1); this.Control.SelectedText = string.Empty; count++; } } int index = this.Control.TextLength; this.Control.AppendText(message + Environment.NewLine); if (this.Control.GetType() == typeof(RichTextBox)) { RichTextBox richTextBox = (RichTextBox)this.Control; if (color == null) color = richTextBox.ForeColor; richTextBox.SelectionStart = index; richTextBox.SelectionLength = message.Length; richTextBox.SelectionColor = (Color)color; } if (this.OnScrollToCaret) this.Control.ScrollToCaret(); } catch (Exception ex) { Trace.WriteLine(ex.ToString()); } finally { if (this.Control != null) this.Control.ResumeLayout(); } } #endregion #region [ ETC ] ------------------------------------------------------- /// /// Change LogLevel /// /// public void SetLevel(log4net.Core.Level level) { Logger logger = this.Logger; logger.Repository.Threshold = logger.Repository.LevelMap["DEBUG"]; logger.Level = level; } /// /// Logger를 반환한다 /// /// private log4net.Repository.Hierarchy.Logger GetLogger(string name = "") { if (string.IsNullOrEmpty(name)) name = this.Name; ILog log = LogManager.GetLogger(name); log4net.Repository.Hierarchy.Logger logger = log.Logger as log4net.Repository.Hierarchy.Logger; return logger; } /// /// RollingAppender 로그파일을 삭제합니다. /// /// 동작주기(분) /// /// RollingMode='Date'일 경우 동작하며, /// MaxSizeRollBackups 값을 보관일로 적용하여 기간이 경과된 파일들을 삭제한다 /// public void DeleteRollingFiles(int interval = 30) { try { if (DateTime.Now.Subtract(this.LastDeletedDateTime).TotalMinutes < interval) return; this.LastDeletedDateTime = DateTime.Now; var repositories = LogManager.GetAllRepositories().FirstOrDefault(); if (repositories == null) return; foreach (IAppender appender in repositories.GetAppenders()) { FileAppender fileAppender = appender as FileAppender; RollingFileAppender rollingAppender = appender as RollingFileAppender; if (rollingAppender != null) { int maxSizeRollBackups = rollingAppender.MaxSizeRollBackups; if (maxSizeRollBackups < 1) continue; RollingMode rollingStyle = rollingAppender.RollingStyle; if (rollingStyle != RollingMode.Date) continue; string path = System.IO.Path.GetDirectoryName(rollingAppender.File); DirectoryInfo directory = new DirectoryInfo(path); //string pattern = this.GetDatePatten(rollingAppender.DatePattern); string extension = System.IO.Path.GetExtension(rollingAppender.File); List files = directory.GetFiles($"*{extension}") .Where(x => DateTime.Now.Subtract(x.CreationTime).TotalDays > maxSizeRollBackups) .OrderBy(x => x.CreationTime).ToList(); foreach (FileInfo file in files) { Trace.WriteLine($"Delete {file.FullName}"); file.Delete(); } } } } catch (Exception ex) { Trace.WriteLine(ex.ToString()); } } private string GetDatePatten(string name) { StringBuilder sb = new StringBuilder(); string pattern = @"'([^']*)'"; MatchCollection matches = Regex.Matches(name, pattern); for (int i = 0; i < matches.Count; i++) { sb.Append(matches[i].Groups[1].Value); if (i + 1 < matches.Count) sb.Append("*"); } return sb.ToString(); } public static string GetCallStack() { StringBuilder sb = new StringBuilder(); try { StackTrace trace = new StackTrace(); for (int i = 0; i < trace.FrameCount; i++) { StackFrame frame = trace.GetFrame(i); MethodBase method = frame.GetMethod(); sb.AppendLine($"{method?.DeclaringType?.Name}.{method.Name}"); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } return sb.ToString(); } #endregion #endregion } }