2017. 2. 3. 10:17ㆍ장비제어개발관한이야기
로그가 필요하였을때!
고단한 한 주를 뒤로하고 편히 침대에서 잠을 자는 시간 핸드폰이 울렸다. 거래처에서 장비의 작동이 안된다는 이야기 였다. 정말로 잘잘못을 따지기 전에 짜증이나는 일이 아닐수 없다. 거래처 엔지니어도 짜증나는 일일 것이다.
업데이트후에 특정상황에 계속 장비제어 프로그램이 다운된다는 이야기다.
나는 노트북을 열어 정상적인 루트상의 "Log"와 비정상의 루트상의 "Log"를 확인 후 엔지니어에게 다시 묻는다.
전혀일어 나지 않을 꺼라고 생각했던, 특수 경우가 일어난것이다. 항상 대비해야 하는 개발자란 직업이지만, 명백한 실수다.
가야 하나 생각 했지만, 한가지 꼼수가 생각나서 밤늦게 출장을 가는 불쌍사는 없었다.
장비개발에서 뿐만 아니라 Log는 상당한 역할을 한다.
이 Log는 개발자가 의도한 진행의 순서로 현상태를 파일을 쓰는것을 말한다. 물론 화면에 Diplay하는것도 Log에 포함된다고 할수 있다.
"Log는 무엇인가?" 는 중요하지 않지만 Log를 쓰는 이유는 상당히 중요하다.
첫째로는 개발자가 특정상황(프로그램다운) 상태를 보고 싶을경우에 쓰인다. 물론, 개발자가 자기개발에 문제가 있을꺼라 생각하고 방어적으로 로그를 넣어 놔야 쓸모가 있을것이다.
둘째로는 감시자의 역할이다.엔지니어-사용자 가 어떤 파라미터를 만지느냐 특정현상에 어떤 행동을 하는 감시자 역할을 한다.
물론, 엔지니어가 어떤 파라미터를 만지든지 특정현상에 대한 확실한 알람이 필요하다.
사실 Log로서의 기능은 충실하지만 프로그래밍 적으로 완성도가 낮아서 공개를 해야 하나 고민이 있었지만, 일단 공개를 해야 바꾸겠다는 생각에 일단 공개를 합니다. 단순히 이렇게 구현된다만 참고 해주시길 바랍니다.
개발자답게 소스코드를 봅시다!
<Header>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
// SLogClass.h: interface for the CSLogClass class.
//
//////////////////////////////////////////////////////////////////////
#pragma once
#include "StdAfx.h"
using namespace std;
class CLogThread;
class SLogClass;
#define DEF_MAX_CHAR_SIZE 5000
enum
{
iU_LEVEL_ERROR,
LEVEL_INFO,
LEVEL_WARN,
LEVEL_DBG,
};
class SLogClass
{
public:
SLogClass()
{
};
CString m_strDirectory;
CString m_strSaveFile;
CString s_strMsgBuf;
CString m_strFileLoc;
int m_nLine;
void Log(CString strLogKind, int level, LPCTSTR lpszFmt, ...);
static SLogClass& SrcPos(LPCSTR lpszFileLoc, int nLine)
{
SLogClass* _Log = new SLogClass();
_Log->m_strFileLoc = lpszFileLoc;
_Log->m_nLine = nLine;
return *_Log;
};
};
class CLogThread : public CWinThread
{
DECLARE_DYNCREATE(CLogThread)
public:
CLogThread(); // protected constructor used by dynamic creation
// Attributes
public:
void AddLog(SLogClass* logData);
//void DelLog();
protected:
CRITICAL_SECTION m_Sync1;
bool m_bThreadExit;
// Operations
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
public:
virtual ~CLogThread();
};
#define FileLog SLogClass::SrcPos(__FILE__, __LINE__).Log
|
cs |
<CPP>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
// SLog.cpp: implementation of the CSLog class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SLogClass.h"
#include "MFileUtil.h"
#include <stdio.h>
#include <stdlib.h>
#include <share.h>
void SLogClass::Log(CString strLogKind, int level, LPCTSTR lpszFmt, ...)
{
TCHAR s_szMsgBuf[DEF_MAX_CHAR_SIZE];
TCHAR s_szMsgBuf_sub[DEF_MAX_CHAR_SIZE];
CString strLogDirectory;
CString strLogFileName;
SYSTEMTIME sTime;
MFileUtil FileUtil;
int iPos;
GetLocalTime(&sTime);
strLogDirectory.Format(_T("%s%04d_%02d\\%02d\\"), DEF_LOG_PATH, sTime.wYear, sTime.wMonth, sTime.wDay);
FileUtil.MakeDir(strLogDirectory);
strLogFileName = strLogKind;
iPos = m_strFileLoc.Find(_T("\\"),0);
if(iPos > 0)
{
m_strFileLoc.MakeReverse();
iPos = m_strFileLoc.Find(_T("\\"),0);
m_strFileLoc.Delete(iPos, m_strFileLoc.GetLength() - iPos);
m_strFileLoc.MakeReverse();
}
LPTSTR cxtBuf = s_szMsgBuf;
va_list arg;
va_start(arg, lpszFmt);
_vstprintf_s(cxtBuf,DEF_MAX_CHAR_SIZE,lpszFmt, arg);
va_end(arg);
_tcsncpy_s(s_szMsgBuf_sub, s_szMsgBuf, sizeof(char[DEF_MAX_CHAR_SIZE]));
_stprintf_s( s_szMsgBuf, _T("%s <<%d:%d:%d>> [dbg]Lv:%d, %s(%d)"),
s_szMsgBuf_sub, sTime.wHour,sTime.wMinute,sTime.wSecond,level, m_strFileLoc, m_nLine);
m_strDirectory = strLogDirectory;
m_strSaveFile = strLogFileName;
s_strMsgBuf = s_szMsgBuf;
CLogThread* m_LogThread = System.GetLogMsgthreadComponent();
m_LogThread->AddLog(this);
}
IMPLEMENT_DYNCREATE(CLogThread, CWinThread)
CTypedPtrArray<CPtrArray, SLogClass*> m_LogClassList;
CLogThread::CLogThread()
{
InitializeCriticalSection(&m_Sync1);
}
CLogThread::~CLogThread()
{
}
BOOL CLogThread::InitInstance()
{
//USES_CONVERSION;
char cBuffer[1024]; // 크기 설정
int i,j,forcnt;
j = 0;
int nCount = 0;
int nAcc = 0;
FILE *stream;
m_bThreadExit = TRUE;
while(m_bThreadExit)
{
Sleep(1);
nCount = 0;
EnterCriticalSection(&m_Sync1);
nCount = (int)m_LogClassList.GetSize();
if(10000 < nCount)
{
for(i = 0; i < (nCount - 10000); i++) // 1000 log items more
{
if( (stream = _tfsopen(m_LogClassList.GetAt(0)->m_strDirectory + DEF_LOG_WRITE_FAIL, _T("at"),
_SH_DENYNO )) != NULL )
{
CString str = m_LogClassList.GetAt(0)->s_strMsgBuf + _T("\n");
int len = str.GetLength();
// 멀티바이트 길이를 구한다.
if(len < 1000)
{
int nMultiByteLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
// 멀티바이트 길이만큼 변환한다.
WideCharToMultiByte(CP_ACP, 0, str, -1, cBuffer, nMultiByteLen, NULL, NULL);
fprintf(stream, "%s", cBuffer);
}
fclose(stream);
}
TRACE("Logging Fail : %s\n", m_LogClassList.GetAt(0)->s_strMsgBuf);
delete(m_LogClassList[0]);
m_LogClassList.RemoveAt(0);
}
}
nCount = (int)m_LogClassList.GetSize();
if(0 < nCount)
{
forcnt = nCount;
if(100 < forcnt)
forcnt = 100;
for(i = 0; i < forcnt; i++)
{
if(m_LogClassList[0] != NULL) //20121207
{
if( (stream = _tfsopen(m_LogClassList.GetAt(0)->m_strDirectory +
m_LogClassList.GetAt(0)->m_strSaveFile, _T("at"), _SH_DENYNO )) != NULL )
{
CString str = m_LogClassList.GetAt(0)->s_strMsgBuf + _T("\n");
int len = str.GetLength();
// 멀티바이트 길이를 구한다.
if(len < 1000) {
int nMultiByteLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
// 멀티바이트 길이만큼 변환한다.
WideCharToMultiByte(CP_ACP, 0, str, -1, cBuffer, nMultiByteLen, NULL, NULL);
fprintf(stream, "%s", cBuffer);
}
fclose(stream);
delete(m_LogClassList[0]);
m_LogClassList.RemoveAt(0);
}
else {
break;
}
}
}
}
LeaveCriticalSection(&m_Sync1);
}
return TRUE;
}
int CLogThread::ExitInstance()
{
/// 소멸 시키기.
m_bThreadExit = FALSE;
Sleep(1000);
int numItems = (int)m_LogClassList.GetUpperBound();
for(int m = 0; m <= numItems; m++)
delete(m_LogClassList[m]);
m_LogClassList.RemoveAll();
return CWinThread::ExitInstance();
}
void CLogThread::AddLog(SLogClass* LogData)
{
EnterCriticalSection(&m_Sync1);
m_LogClassList.Add(LogData);
LeaveCriticalSection(&m_Sync1);
}
|
cs |
FileLog("생성파일이름",레벨,"내용") 의 C 형태로 호출되게 구현 되어있습니다.
생성만 해주고 위와 같이 쓰기만하면 됩니다.
원하는 형식으로 변환해주는 SLogClass와 실제로 구동되는 CLogThread로 구성되어 쓰레드로서 로그들을 경로에 차곡 차곡 쌓게 됩니다.
실제로 파일에 바로쓰게 되면 실제 파일쓰는 시간이 생각보다 시간을 많이 필요하기 때문에 쓰레드로 구성하는게 프로그램 구동상 유리한 기능을 하게 됩니다.
'장비제어개발관한이야기' 카테고리의 다른 글
업계통신표준 SECS/GEM! (0) | 2017.02.20 |
---|---|
장비제어에서 Offline Mode 와 Online모드 (0) | 2017.02.07 |
파일관련 유틸 클래스 (0) | 2017.02.02 |
장비제어 - 쓰레드의 생성 (0) | 2017.02.01 |
카메라 제어하자 - 카메라종류 (0) | 2017.01.23 |