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