1 //-----------------------------------------------------------------------------
2 //
3 // LogImpl.cpp
4 //
5 // Windows implementation of message and error logging
6 //
7 // Copyright (c) 2010 Mal Lansell <mal@lansell.org>
8 // All rights reserved.
9 //
10 // SOFTWARE NOTICE AND LICENSE
11 //
12 // This file is part of OpenZWave.
13 //
14 // OpenZWave is free software: you can redistribute it and/or modify
15 // it under the terms of the GNU Lesser General Public License as published
16 // by the Free Software Foundation, either version 3 of the License,
17 // or (at your option) any later version.
18 //
19 // OpenZWave is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU Lesser General Public License for more details.
23 //
24 // You should have received a copy of the GNU Lesser General Public License
25 // along with OpenZWave. If not, see <http://www.gnu.org/licenses/>.
26 //
27 //-----------------------------------------------------------------------------
28 #include <windows.h>
29 #include <cerrno>
30 #include <list>
31
32 #include "Defs.h"
33 #include "LogImpl.h"
34
35 #ifdef MINGW
36
37 #define vsprintf_s vsnprintf
38
39 #define strcpy_s(DEST, NUM, SOURCE) strncpy(DEST, SOURCE, NUM)
40
fopen_s(FILE ** pFile,const char * filename,const char * mode)41 errno_t fopen_s(FILE** pFile, const char *filename, const char *mode)
42 {
43 if (!pFile)
44 {
45 #if defined(_MSC_VER) && _MSC_VER >= 1400
46 _set_errno(EINVAL);
47 #elif defined(__MINGW64__)
48 _set_errno(EINVAL);
49 #else
50 errno = EINVAL;
51 #endif
52 return EINVAL;
53 }
54
55 *pFile = fopen(filename, mode);
56
57 if (!*pFile)
58 {
59 return errno;
60 }
61
62 return 0;
63 }
64
65 #endif
66
67 namespace OpenZWave
68 {
69 namespace Internal
70 {
71 namespace Platform
72 {
73
74 //-----------------------------------------------------------------------------
75 // <LogImpl::LogImpl>
76 // Constructor
77 //-----------------------------------------------------------------------------
LogImpl(string const & _filename,bool const _bAppendLog,bool const _bConsoleOutput,LogLevel const _saveLevel,LogLevel const _queueLevel,LogLevel const _dumpTrigger)78 LogImpl::LogImpl(string const& _filename, bool const _bAppendLog, bool const _bConsoleOutput, LogLevel const _saveLevel, LogLevel const _queueLevel, LogLevel const _dumpTrigger) :
79 m_filename(_filename), // name of log file
80 m_bConsoleOutput(_bConsoleOutput), // true to provide a copy of output to console
81 m_bAppendLog(_bAppendLog), // true to append (and not overwrite) any existing log
82 m_saveLevel(_saveLevel), // level of messages to log to file
83 m_queueLevel(_queueLevel), // level of messages to log to queue
84 m_dumpTrigger(_dumpTrigger) // dump queued messages when this level is seen
85 {
86 string accessType;
87
88 // create an adjusted file name and timestamp string
89 string timeStr = GetTimeStampString();
90
91 if (m_bAppendLog)
92 {
93 accessType = "a";
94 }
95 else
96 {
97 accessType = "w";
98 }
99
100 FILE* pFile;
101 if (!fopen_s(&pFile, _filename.c_str(), accessType.c_str()))
102 {
103 fprintf(pFile, "\nLogging started %s\n\n", timeStr.c_str());
104 fclose(pFile);
105 }
106 }
107
108 //-----------------------------------------------------------------------------
109 // <LogImpl::~LogImpl>
110 // Destructor
111 //-----------------------------------------------------------------------------
~LogImpl()112 LogImpl::~LogImpl()
113 {
114 }
toEscapeCode(LogLevel _level)115 unsigned int LogImpl::toEscapeCode(LogLevel _level)
116 {
117 unsigned short code;
118
119 switch (_level)
120 {
121 case LogLevel_Debug:
122 code = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
123 break; //blue
124 case LogLevel_Detail:
125 code = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
126 break; //blue
127 case LogLevel_Info:
128 code = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
129 break; // white
130 case LogLevel_Alert:
131 code = FOREGROUND_RED | FOREGROUND_GREEN;
132 break; // orange
133 case LogLevel_Warning:
134 code = FOREGROUND_RED | FOREGROUND_GREEN;
135 break; // orange
136 case LogLevel_Error:
137 code = FOREGROUND_RED;
138 break; // red
139 case LogLevel_Fatal:
140 code = FOREGROUND_RED | FOREGROUND_INTENSITY;
141 break; // light red
142 case LogLevel_Always:
143 code = FOREGROUND_GREEN;
144 break; // green
145 default:
146 code = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
147 break; // white
148 }
149
150 return (code);
151 }
152
153 //-----------------------------------------------------------------------------
154 // <LogImpl::Write>
155 // Write to the log
156 //-----------------------------------------------------------------------------
Write(LogLevel _logLevel,uint8 const _nodeId,char const * _format,va_list _args)157 void LogImpl::Write(LogLevel _logLevel, uint8 const _nodeId, char const* _format, va_list _args)
158 {
159 // create a timestamp string
160 string timeStr = GetTimeStampString();
161 string nodeStr = GetNodeString(_nodeId);
162 string logLevelStr = GetLogLevelString(_logLevel);
163
164 // handle this message
165 if ((_logLevel <= m_queueLevel) || (_logLevel == LogLevel_Internal)) // we're going to do something with this message...
166 {
167 char lineBuf[1024];
168 if (!_format || (_format[0] == 0))
169 {
170 strcpy_s(lineBuf, 1024, "");
171 }
172 else
173 {
174 vsprintf_s(lineBuf, sizeof(lineBuf), _format, _args);
175 }
176
177 // should this message be saved to file (and possibly written to console?)
178 if ((_logLevel <= m_saveLevel) || (_logLevel == LogLevel_Internal))
179 {
180 // save to file
181 FILE* pFile = NULL;
182 if (!fopen_s(&pFile, m_filename.c_str(), "a") || m_bConsoleOutput)
183 {
184 if (_logLevel != LogLevel_Internal) // don't add a second timestamp to display of queued messages
185 {
186 if (pFile != NULL)
187 {
188 fprintf(pFile, "%s%s%s", timeStr.c_str(), logLevelStr.c_str(), nodeStr.c_str());
189 }
190 if (m_bConsoleOutput)
191 {
192 printf("%s%s%s", timeStr.c_str(), logLevelStr.c_str(), nodeStr.c_str());
193 }
194 }
195
196 // print message to file (and possibly screen)
197 if (pFile != NULL)
198 {
199 fprintf(pFile, "%s", lineBuf);
200 fprintf(pFile, "\n");
201 fclose(pFile);
202 }
203 if (m_bConsoleOutput)
204 {
205 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), toEscapeCode(_logLevel));
206 printf("%s", lineBuf);
207 printf("\n");
208 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
209 }
210
211 }
212 }
213
214 if (_logLevel != LogLevel_Internal)
215 {
216 char queueBuf[1024];
217 string threadStr = GetThreadId();
218 sprintf_s(queueBuf, sizeof(queueBuf), "%s%s%s", timeStr.c_str(), threadStr.c_str(), lineBuf);
219 Queue(queueBuf);
220 }
221 }
222
223 // now check to see if the _dumpTrigger has been hit
224 if ((_logLevel <= m_dumpTrigger) && (_logLevel != LogLevel_Internal) && (_logLevel != LogLevel_Always))
225 {
226 QueueDump();
227 }
228 }
229
230 //-----------------------------------------------------------------------------
231 // <LogImpl::Queue>
232 // Write to the log queue
233 //-----------------------------------------------------------------------------
Queue(char const * _buffer)234 void LogImpl::Queue(char const* _buffer)
235 {
236 string bufStr = _buffer;
237 m_logQueue.push_back(bufStr);
238
239 // rudimentary queue size management
240 if (m_logQueue.size() > 500)
241 {
242 m_logQueue.pop_front();
243 }
244 }
245
246 //-----------------------------------------------------------------------------
247 // <LogImpl::QueueDump>
248 // Dump the LogQueue to output device
249 //-----------------------------------------------------------------------------
QueueDump()250 void LogImpl::QueueDump()
251 {
252 Log::Write(LogLevel_Internal, "\n\nDumping queued log messages\n");
253 list<string>::iterator it = m_logQueue.begin();
254 while (it != m_logQueue.end())
255 {
256 string strTemp = *it;
257 Log::Write(LogLevel_Internal, "%s", strTemp.c_str());
258 ++it;
259 }
260 m_logQueue.clear();
261 Log::Write(LogLevel_Internal, "\nEnd of queued log message dump\n\n");
262 }
263
264 //-----------------------------------------------------------------------------
265 // <LogImpl::Clear>
266 // Clear the LogQueue
267 //-----------------------------------------------------------------------------
QueueClear()268 void LogImpl::QueueClear()
269 {
270 m_logQueue.clear();
271 }
272
273 //-----------------------------------------------------------------------------
274 // <LogImpl::SetLoggingState>
275 // Sets the various log state variables
276 //-----------------------------------------------------------------------------
SetLoggingState(LogLevel _saveLevel,LogLevel _queueLevel,LogLevel _dumpTrigger)277 void LogImpl::SetLoggingState(LogLevel _saveLevel, LogLevel _queueLevel, LogLevel _dumpTrigger)
278 {
279 m_saveLevel = _saveLevel;
280 m_queueLevel = _queueLevel;
281 m_dumpTrigger = _dumpTrigger;
282 }
283
284 //-----------------------------------------------------------------------------
285 // <LogImpl::GetTimeStampAndThreadId>
286 // Generate a string with formatted current time
287 //-----------------------------------------------------------------------------
GetTimeStampString()288 string LogImpl::GetTimeStampString()
289 {
290 // Get a timestamp
291 SYSTEMTIME time;
292 ::GetLocalTime(&time);
293
294 // create a time stamp string for the log message
295 char buf[100];
296 sprintf_s(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d.%03d ", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds);
297 string str = buf;
298 return str;
299 }
300
301 //-----------------------------------------------------------------------------
302 // <LogImpl::GetNodeString>
303 // Generate a string with formatted node id
304 //-----------------------------------------------------------------------------
GetNodeString(uint8 const _nodeId)305 string LogImpl::GetNodeString(uint8 const _nodeId)
306 {
307 if (_nodeId == 0)
308 {
309 return "";
310 }
311 else if (_nodeId == 255) // should make distinction between broadcast and controller better for SwitchAll broadcast
312 {
313 return "contrlr, ";
314 }
315 else
316 {
317 char buf[20];
318 snprintf(buf, sizeof(buf), "Node%03d, ", _nodeId);
319 return buf;
320 }
321 }
322
323 //-----------------------------------------------------------------------------
324 // <LogImpl::GetThreadId>
325 // Generate a string with formatted thread id
326 //-----------------------------------------------------------------------------
GetThreadId()327 string LogImpl::GetThreadId()
328 {
329 char buf[20];
330 DWORD dwThread = ::GetCurrentThreadId();
331 sprintf_s(buf, sizeof(buf), "%04d ", dwThread);
332 string str = buf;
333 return str;
334 }
335
336 //-----------------------------------------------------------------------------
337 // <LogImpl::SetLogFileName>
338 // Provide a new log file name (applicable to future writes)
339 //-----------------------------------------------------------------------------
SetLogFileName(const string & _filename)340 void LogImpl::SetLogFileName(const string &_filename)
341 {
342 m_filename = _filename;
343 }
344 //-----------------------------------------------------------------------------
345 // <LogImpl::GetLogLevelString>
346 // Provide a new log file name (applicable to future writes)
347 //-----------------------------------------------------------------------------
GetLogLevelString(LogLevel _level)348 string LogImpl::GetLogLevelString(LogLevel _level)
349 {
350 if ((_level >= LogLevel_None) && (_level <= LogLevel_Internal))
351 {
352 char buf[20];
353 snprintf(buf, sizeof(buf), "%s, ", LogLevelString[_level]);
354 return buf;
355 }
356 else
357 return "Unknown, ";
358 }
359 } // namespace Platform
360 } // namespace Internal
361 } // namespace OpenZWave
362