2026-06-05 13:44:39 +09:00

440 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Reflection;
namespace CoreUnit.Common
{
public class LogUnit
{
public delegate void EventHandlerLog(LogItem item);
public delegate void EventDisplayLog(LogItem item, eLogLevel logLevel);
public enum eLogProtocol : int { Time = 0, Msg, Position, Type };
public enum eLogLevel { LOW, MIDDLE, HIGH }; //로그를 표시하는 레벨을 정리함(High: detail한 로그까지 표시(Debug모드), Low: 단순한 로그(System로그)만 표시)
public enum eLogType { ERROR, ALARM, PRODUCT, SYSTEM, BUTTON, USER_ACCOUNT, MCC, COMMAND, COMMUNICATION, DEBUG, INITIALIZE, DATA, MEASURE };
/// <summary>
///ERROR : 중알람 - SW 정지됨
///ALARM : 경알람 - SW 정지 안 됨
///PRODUCT : 생산 관련
///SYSTEM : SW 시스템
///BUTTON : UI 버튼
///USER_ACCOUNT : 장비 사용자
///MCC : 시퀀스 차트
///COMMAND : ReactiveLayer(Resource, Behavior)의 수행 정보를 DeliberateLayer(GUI) 로 전달
///COMMUNICATION : 통신
///DEBUG : 디버그용(테스트용)
///INITIALIZZE : SW 초기화 시에만 사용
/// </summary>
public struct LogItem
{
public DateTime time;
public string msg;
public string position;
public eLogType type;
public eLogLevel level;
public int unitNo;
public LogItem Clone()
{
LogItem item = new LogItem();
item.time = this.time;
item.msg = this.msg;
item.position = this.position;
item.type = this.type;
item.level = this.level;
item.unitNo = this.unitNo;
return item;
}
}
object logBufferLock = new object();
List<LogItem> m_logBuffer = new List<LogItem>();
public List<LogItem> LogBuffer
{
get { return m_logBuffer; }
set { m_logBuffer = value; }
}
object logDisplayErrorItemLock = new object();
List<LogItem> m_displayedErrorItem = new List<LogItem>();
public List<LogItem> DisplayedErrorItem
{
get { return m_displayedErrorItem; }
set { m_displayedErrorItem = value; }
}
object logBufferForSavingLock= new object();
Dictionary<eLogType, List<string>> m_logBufferForSaving = new Dictionary<eLogType, List<string>>();
public Dictionary<eLogType, List<string>> LogBufferForSaving
{
get { return m_logBufferForSaving; }
set { m_logBufferForSaving = value; }
}
eLogLevel m_logLevel = eLogLevel.HIGH;
public eLogLevel LogLevel
{
get { lock (this) { return m_logLevel; } }
set { lock (this) { m_logLevel = value; } }
}
public LogUnit()
{
lock (logBufferForSavingLock)
{
foreach (eLogType logType in Enum.GetValues(typeof(eLogType)))
{
LogBufferForSaving[logType] = new List<string>();
}
}
}
public void DisplayLog(ListView lsvLog, List<LogItem> logItemList, eLogLevel masterLogLevel)
{
if (0 < logItemList.Count)
{
lsvLog.BeginUpdate();
foreach (LogItem item in logItemList)
{
if (item.level <= masterLogLevel || item.type == eLogType.ERROR || item.type == eLogType.ALARM) // 로그 레벨에 따른 로그 필터 기능 추가
{
ListViewItem lvi = new ListViewItem(item.time.ToString("yyyyMMdd HH:mm:ss.fff"));
lvi.SubItems.Add(item.msg);
lsvLog.Items.Add(lvi);
}
}
int count = lsvLog.Items.Count;
uint maxLogCount = 200;
if (0 < maxLogCount && maxLogCount < count)
{
for (int i = 0; i < count - maxLogCount; i++)
{
lsvLog.Items.RemoveAt(0);
}
}
lsvLog.EndUpdate();
if (0 < lsvLog.Items.Count)
{
lsvLog.Items[lsvLog.Items.Count - 1].EnsureVisible();
}
}
}
public void WriteLogToFile(string systemDataFolderPath)
{
foreach (eLogType logType in Enum.GetValues(typeof(eLogType)))
{
lock (logBufferForSavingLock)
{
if (0 < LogBufferForSaving[logType].Count) //로그가 있는 경우에 file 기록함.
{
try
{
string logFolder = string.Format(@"{0}\Log\{1}\{2}", systemDataFolderPath, logType.ToString(), DateTime.Now.ToString("yyyy.MM.dd"));
string logFile = string.Format(@"{0}\{1}.log", logFolder, DateTime.Now.ToString("HH"));
if (Directory.Exists(logFolder) == false)
{
Directory.CreateDirectory(logFolder);
}
using (FileStream file = new FileStream(logFile, FileMode.Append, FileAccess.Write))
{
using (StreamWriter sw = new StreamWriter(file, System.Text.Encoding.Default))
{
for (int i = 0; i < LogBufferForSaving[logType].Count; i++)
{
sw.WriteLine(LogBufferForSaving[logType][i]);
}
}
}
LogBufferForSaving[logType].Clear();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
}
public void SetLog(eLogType type, string position, string msg, eLogLevel level = eLogLevel.MIDDLE)
{
SetLog(type, position, msg, -1, level);
}
public void SetLog(eLogType type, string position, string msg, int unitNo, eLogLevel level = eLogLevel.MIDDLE)
{
LogItem logItem = new LogItem();
logItem.time = DateTime.Now;
logItem.msg = msg;
logItem.position = position;
logItem.level = level;
logItem.unitNo = unitNo;
if (type == eLogType.ERROR)
{
var callSite = (new System.Diagnostics.StackTrace(1, true)).GetFrame(0);
logItem.position += string.Format("(FILE NAME:{0},LINE NO:{1})", callSite.GetFileName(), callSite.GetFileLineNumber());
}
logItem.type = type;
lock (logBufferLock)
{
LogBuffer.Add(logItem);
}
}
public bool FilterSameErrorLog(LogItem currLogItem)
{
if (currLogItem.type == eLogType.ERROR || currLogItem.type == eLogType.ALARM)
{
lock (logDisplayErrorItemLock)
{
foreach (LogItem log in DisplayedErrorItem)
{
if (log.msg == currLogItem.msg)
{
return true;
}
}
DisplayedErrorItem.Add(currLogItem);
}
}
return false;
}
public void ClearDisplayerErrorLog()
{
lock (logDisplayErrorItemLock)
{
if (DisplayedErrorItem.Count !=0)
{
DisplayedErrorItem.Clear();
}
}
}
public void HandleLog(EventHandlerLog evtHandlerLog, string systemDataFolderPath, ListView lsvLog, eLogLevel masterLogLevel, bool enableDebugMode)
{
List<LogItem> arrangedLogItemList = new List<LogItem>();
lock (logBufferLock)
{
if (0 < LogBuffer.Count)
{
for (int i = 0; i < LogBuffer.Count; i++)
{
LogItem logItem = LogBuffer[i];
if (!FilterSameErrorLog(logItem))
{
arrangedLogItemList.Add(logItem.Clone());
SaveLogToBuffer(enableDebugMode, logItem, masterLogLevel);
}
}
LogBuffer.Clear();
Thread.Sleep(1);
}
}
WriteLogToFile(systemDataFolderPath);
DisplayLog(lsvLog, arrangedLogItemList, masterLogLevel);
foreach (LogItem item in arrangedLogItemList)
{
evtHandlerLog(item); // 이 안에서 문제가 될 코드(예를 들면, 로그 기록)가 지나치게 많아서 lock을 걸 수가 없다 (정리 필요)
}
}
public void HandleLogByUnit(EventHandlerLog evtHandlerLog, string systemDataFolderPath, ListView[] lsvLog, eLogLevel masterLogLevel, bool enableDebugMode)
{
List<LogItem> arrangedLogItemListAll = new List<LogItem>();
List<LogItem> arrangedLogItemList1 = new List<LogItem>();
List<LogItem> arrangedLogItemList2 = new List<LogItem>();
List<LogItem> arrangedLogItemList3 = new List<LogItem>();
List<LogItem> arrangedLogItemList4 = new List<LogItem>();
lock (logBufferLock)
{
if (0 < LogBuffer.Count)
{
for (int i = 0; i < LogBuffer.Count; i++)
{
LogItem logItem = LogBuffer[i];
if (!FilterSameErrorLog(logItem))
{
SaveLogToBuffer(enableDebugMode, logItem, masterLogLevel);
arrangedLogItemListAll.Add(logItem.Clone());
switch (logItem.unitNo)
{
case 1:
arrangedLogItemList1.Add(logItem.Clone());
break;
case 2:
arrangedLogItemList2.Add(logItem.Clone());
break;
case 3:
arrangedLogItemList3.Add(logItem.Clone());
break;
case 4:
arrangedLogItemList4.Add(logItem.Clone());
break;
default:
break;
}
}
}
LogBuffer.Clear();
Thread.Sleep(1);
}
}
WriteLogToFile(systemDataFolderPath);
if (3 < lsvLog.Length)
{
DisplayLog(lsvLog[0], arrangedLogItemList1, masterLogLevel);
DisplayLog(lsvLog[1], arrangedLogItemList2, masterLogLevel);
DisplayLog(lsvLog[2], arrangedLogItemList3, masterLogLevel);
DisplayLog(lsvLog[3], arrangedLogItemList4, masterLogLevel);
}
foreach (LogItem item in arrangedLogItemListAll)
{
evtHandlerLog(item); // 이 안에서 문제가 될 코드(예를 들면, 로그 기록)가 지나치게 많아서 lock을 걸 수가 없다 (정리 필요)
}
}
private void SaveLogToBuffer(bool enableDebugMode, LogItem logItem, eLogLevel masterLogLevel)
{
string msg = string.Format("[TIME]:{0}\t[TYPE]:{1}\t[CONTENT]:{2}\t[POSITION]:{3}"
, logItem.time.ToString("yyyy-MM-dd HH:mm:ss.fff")
, Enum.GetName(typeof(eLogType), logItem.type)
, logItem.msg
, logItem.position);
lock (logBufferForSavingLock)
{
if (enableDebugMode) //DB.Dao.SystemRecipe.EnableDebugMode
{
LogBufferForSaving[eLogType.DEBUG].Add(msg); //DEBUG 폴더에는 모든 로그를 저장
}
if ((logItem.level >= masterLogLevel) || (logItem.type == eLogType.ERROR) || (logItem.type == eLogType.ALARM)) //로그 레벨에 따른 로그 필터 기능 추가
{
if (enableDebugMode && logItem.type == eLogType.DEBUG)
{
// 로그 중복 기록 방지
}
else
{
LogBufferForSaving[logItem.type].Add(msg);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
public string GetExceptionInformation(eLogType logType, Exception ex)
{
string msg = string.Format("[Type]\r\n{0}\r\n[Message]\r\n{1}\r\n[Exception]\r\n{2}\r\n{3}\r\n", logType.ToString(), ex.Message, ex.TargetSite.ToString(), ex.StackTrace.ToString());
if (ex.InnerException != null)
{
msg += "[InnerException]\r\n" + ex.InnerException.TargetSite.ToString() + "\r\n" + ex.InnerException.StackTrace.ToString();
}
return msg;
}
public void DeleteLogOverUserPeriod(string logDataFolder, int logDeletePeriod, string[] dataFolders = null)
{
DirectoryInfo logFolder;
//1.로그 폴더 삭제
foreach (eLogType logType in Enum.GetValues(typeof(eLogType)))
{
string folderName = string.Format(@"{0}\Log\{1}", logDataFolder, logType.ToString());
if (Directory.Exists(folderName) == true)
{
logFolder = new DirectoryInfo(folderName);
foreach (DirectoryInfo datetimeDir in logFolder.GetDirectories())
{
if (datetimeDir.CreationTime < DateTime.Now.AddDays(-1 * logDeletePeriod))
{
datetimeDir.Delete(true);
UtilUnit.logUnit.SetLog(eLogType.SYSTEM, MethodBase.GetCurrentMethod().Name, string.Format(@"로그 폴더 삭제(폴더명:{0}\{1})", logFolder, datetimeDir.Name), eLogLevel.HIGH);
}
Thread.Sleep(1);
}
}
}
if (dataFolders != null)
{
//2.사용자 정의 폴더 삭제
foreach (string dataFolder in dataFolders)
{
logFolder = new DirectoryInfo(dataFolder);
foreach (DirectoryInfo dir in logFolder.GetDirectories())
{
DirectoryInfo reviewFolder = new DirectoryInfo(dir.FullName);
foreach (DirectoryInfo datetimeDir in reviewFolder.GetDirectories())
{
if (datetimeDir.CreationTime < DateTime.Now.AddDays(-1 * logDeletePeriod))
{
//날짜폴더 삭제
datetimeDir.Delete(true);
UtilUnit.logUnit.SetLog(eLogType.SYSTEM, MethodBase.GetCurrentMethod().Name, string.Format("검사 폴더 삭제(폴더명:{0})", datetimeDir.Name), eLogLevel.HIGH);
}
Thread.Sleep(1);
}
}
}
}
}
}
}