반응형
C#에서 Logging을 하기위해서 log4J를 사용할수도 있지만 Log의 컨텐츠나 기타 여러가지를 핸들링하기 위하여 간단하게 Logger를 만들어 보았습니다.
- Queue를 사용하여 log로 인한 logic에 blocking이 발생하지 않도록 함
- 파일에 log를 Json 포맷으로 기록하여 LogViewer 를 쉽게 만들수 있도록 함
using Extention;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Web.Script.Serialization;
namespace Utility
{
public class Logger
{
enum LogLevel
{
DEBUG = 0,
INFO = 1,
INPORTANT = 2,
ERROR = 3,
}
private static Logger instance;
private bool isStop = false;
private JavaScriptSerializer jsonConvertor;
private ConcurrentQueue<LogData> logDataQueue;
private string logDirPath;
private Thread logFileWriteThread;
private Logger(string logDir = null)
{
if (logDir.IsNullOrEmpty())
{
string baseDirPath = AppDomain.CurrentDomain.BaseDirectory;
baseDirPath = baseDirPath.TrimEnd(new char[] { '\\' });
logDir = baseDirPath + @"\Log";
}
jsonConvertor = new JavaScriptSerializer();
StartLogger(logDir);
}
public static void Debug(string log, params object[] objects)
{
if (instance == null) return;
instance.Write(LogLevel.DEBUG, instance.GetLogString(log, objects));
}
public static void Error(string log, params object[] objects)
{
if (instance == null) return;
instance.Write(LogLevel.ERROR, instance.GetLogString(log, objects));
}
public static void Exception(Exception exception)
{
if (instance == null) return;
instance.Write(LogLevel.ERROR, exception.ToString());
}
public static void Important(string log, params object[] objects)
{
if (instance == null) return;
instance.Write(LogLevel.INPORTANT, instance.GetLogString(log, objects));
}
public static void Infomation(string log, params object[] objects)
{
if (instance == null) return;
instance.Write(LogLevel.INFO, instance.GetLogString(log, objects));
}
public static void InitInstance(string dirPath = null)
{
if (instance != null) return;
instance = new Logger(dirPath);
}
public static void StopLogger()
{
instance.isStop = true;
if (instance.logFileWriteThread != null)
{
instance.logFileWriteThread = null;
}
}
public static void Trace(string log, params object[] objects)
{
#if DEBUG
System.Diagnostics.Trace.WriteLine(instance.GetLogString(log, objects));
#endif
}
private void DeleteOldLogFiles()
{
try
{
string[] dirs = Directory.GetDirectories(logDirPath);
foreach (string dir in dirs)
{
DirectoryInfo dirInfo = new DirectoryInfo(dir);
if (IsValidDate(dirInfo.Name))
{
if (GetDayLength(dirInfo.Name) > 30)
{
FileUtil.DeleteDirectorySafely(dir, true);
Trace("Delete : " + dir);
}
}
}
}
catch
{
}
}
private int GetDayLength(string dateValue)
{
DateTime dt;
DateTime.TryParseExact(dateValue, "yyyyMMdd", null, DateTimeStyles.None, out dt);
TimeSpan sp = DateTime.Now - dt;
return sp.Days;
}
private string GetLogFilePath(LogData logdata)
{
FileUtil.CreateDirectorySafely(logDirPath);
return string.Format("{0}\\{1}.log",
logDirPath,
logdata.GetDateString());
}
private string GetLogString(string log, object[] objects)
{
string logValue = string.Empty;
try
{
if (objects != null && objects.Length > 0)
{
logValue = string.Format(log, objects);
}
else
{
logValue = log;
}
}
catch
{
logValue = log;
}
return logValue;
}
private bool IsValidDate(string dateValue)
{
DateTime dt;
return DateTime.TryParseExact(dateValue, "yyyyMMdd", null, DateTimeStyles.None, out dt);
}
private void LogFileWriteThreadProc()
{
while (true)
{
Thread.Sleep(10);
if (isStop) break;
try
{
if (logDataQueue.Count == 0) continue;
LogData logdata = new LogData();
if (!logDataQueue.TryDequeue(out logdata)) continue;
WriteLogDataToFile(logdata);
}
catch (Exception ex)
{
Trace(ex.ToString());
}
}
}
private void StartLogger(string logDir)
{
logDirPath = logDir;
if (Directory.Exists(logDirPath) == false)
{
Directory.CreateDirectory(logDirPath);
}
else
{
DeleteOldLogFiles();
}
logDataQueue = new ConcurrentQueue<LogData>();
if (logFileWriteThread == null)
{
logFileWriteThread = new Thread(LogFileWriteThreadProc);
logFileWriteThread.IsBackground = true;
}
logFileWriteThread.Start();
}
private void Write(LogLevel nLevel, string strMessage)
{
#if DEBUG
if (nLevel != LogLevel.DEBUG)
{
Trace(string.Format("[{0}] : {1}", nLevel, strMessage));
}
#endif
try
{
StackTrace stackTrace = new StackTrace(); // get call stack
LogData logdata = new LogData();
logdata.LogLevel = nLevel;
logdata.Message = strMessage;
logdata.LogTime = DateTime.Now;
logdata.ThreadID = Thread.CurrentThread.ManagedThreadId;
logdata.StackFrams = stackTrace.GetFrames();
logDataQueue.Enqueue(logdata);
}
catch (Exception ex)
{
Trace(ex.ToString());
}
}
private void WriteLogDataToFile(LogData logdata)
{
try
{
string LogFilePath = GetLogFilePath(logdata);
string LogText = jsonConvertor.Serialize(new LogDataForFile(logdata));
FileStream LogFileStream = new FileStream(LogFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
StreamWriter StreamWriter = new StreamWriter(LogFileStream);
//StreamWriter.WriteLine(LogText);
StreamWriter.WriteLine(LogText);
StreamWriter.Close();
LogFileStream.Close();
}
catch (Exception ex)
{
Exception(ex);
}
}
private struct LogData
{
public LogLevel LogLevel { get; set; }
public DateTime LogTime { get; set; }
public string Message { get; set; }
public StackFrame[] StackFrams { get; set; }
public int ThreadID { get; set; }
public string GetDateString()
{
return LogTime.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
}
private string GetCaller()
{
string Caller = "";
for (int i = 0; i < StackFrams.Length; i++)
{
if (StackFrams[i].GetMethod().DeclaringType == GetType())
{
continue;
}
if (StackFrams[i].GetMethod().ReflectedType.FullName.Contains("System."))
{
return Caller;
}
if (Caller.Length > 0)
{
Caller = "-" + Caller;
}
string CallerInfo = string.Format("{0}:{1}",
StackFrams[i].GetMethod().DeclaringType.Name,
StackFrams[i].GetMethod().Name);
Caller = CallerInfo + Caller;
}
return "Unknown";
}
private void GetClassMethodInfo(ref string ClassName, ref string MethodName)
{
for (int i = 0; i < StackFrams.Length; i++)
{
if (StackFrams[i].GetMethod().DeclaringType != GetType())
{
ClassName = StackFrams[i].GetMethod().DeclaringType.Name;
MethodName = StackFrams[i].GetMethod().Name;
return;
}
}
}
private string GetTimeData()
{
return string.Format("{0:00}:{1:00}:{2:00}.{3:000}",
LogTime.Hour.ToString("00"),
LogTime.Minute.ToString("00"),
LogTime.Second.ToString("00"),
LogTime.Millisecond.ToString("000"));
}
}
private class LogDataForFile
{
public LogDataForFile(LogData logdata)
{
LogLevel = logdata.LogLevel;
LogTime = logdata.LogTime.ToString("yyyy/MM/dd HH:mm:ss.fff");
Message = logdata.Message;
StackFrams = GetCaller(logdata.StackFrams);
ThreadID = logdata.ThreadID;
}
public LogLevel LogLevel { get; set; }
public string LogTime { get; set; }
public int ThreadID { get; set; }
public string Message { get; set; }
public string StackFrams { get; set; }
private string GetCaller(StackFrame[] stackFrams)
{
string Caller = "";
for (int i = 0; i < stackFrams.Length; i++)
{
if (stackFrams[i].GetMethod().DeclaringType == GetType())
{
continue;
}
if (stackFrams[i].GetMethod().ReflectedType.FullName.Contains("System."))
{
return Caller;
}
if (Caller.Length > 0)
{
Caller = "-" + Caller;
}
string CallerInfo = string.Format("{0}:{1}",
stackFrams[i].GetMethod().DeclaringType.Name,
stackFrams[i].GetMethod().Name);
Caller = CallerInfo + Caller;
}
return "Unknown";
}
}
}
}
Logger 사용
Logger.InitInstance();
Logger.Debug("Test Log!!");
Logger.Infomation("Execute Function");
저장된 Log
{"LogLevel":0,"LogTime":"2021-01-07 12:41:34.106","ThreadID":9,"Message":"Test Log!!","StackFrams":"Program:Main-MainForm:.ctor-Logger:Debug-Logger:Write"}
{"LogLevel":1,"LogTime":"2021-01-07 12:41:40.618","ThreadID":9,"Message":"Execute UsbCamera","StackFrams":"MainForm:btnTest_Click-Logger:Infomation-Logger:Write"}
반응형
'[====== Development ======] > C#' 카테고리의 다른 글
REST API 사용 (1) | 2021.01.07 |
---|---|
Extension Method (0) | 2021.01.07 |
C# Expression (0) | 2021.01.04 |
WPF/C# 에서 이미지파일 인쇄 하기 (0) | 2020.11.16 |
GoF의 디자인 패턴 (C#) (0) | 2020.11.05 |