aboutsummaryrefslogtreecommitdiff
path: root/src/dutil/logutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dutil/logutil.cpp')
-rw-r--r--src/dutil/logutil.cpp942
1 files changed, 942 insertions, 0 deletions
diff --git a/src/dutil/logutil.cpp b/src/dutil/logutil.cpp
new file mode 100644
index 00000000..c471e002
--- /dev/null
+++ b/src/dutil/logutil.cpp
@@ -0,0 +1,942 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// globals
6static HMODULE LogUtil_hModule = NULL;
7static BOOL LogUtil_fDisabled = FALSE;
8static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE;
9static LPWSTR LogUtil_sczLogPath = NULL;
10static LPSTR LogUtil_sczPreInitBuffer = NULL;
11static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD;
12static CRITICAL_SECTION LogUtil_csLog = { };
13static BOOL LogUtil_fInitializedCriticalSection = FALSE;
14
15// Customization of certain parts of the string, within a line
16static LPWSTR LogUtil_sczSpecialBeginLine = NULL;
17static LPWSTR LogUtil_sczSpecialEndLine = NULL;
18static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL;
19
20static LPCSTR LOGUTIL_UNKNOWN = "unknown";
21static LPCSTR LOGUTIL_WARNING = "warning";
22static LPCSTR LOGUTIL_STANDARD = "standard";
23static LPCSTR LOGUTIL_VERBOSE = "verbose";
24static LPCSTR LOGUTIL_DEBUG = "debug";
25static LPCSTR LOGUTIL_NONE = "none";
26
27// prototypes
28static HRESULT LogIdWork(
29 __in REPORT_LEVEL rl,
30 __in_opt HMODULE hModule,
31 __in DWORD dwLogId,
32 __in va_list args,
33 __in BOOL fLOGUTIL_NEWLINE
34 );
35static HRESULT LogStringWorkArgs(
36 __in REPORT_LEVEL rl,
37 __in_z __format_string LPCSTR szFormat,
38 __in va_list args,
39 __in BOOL fLOGUTIL_NEWLINE
40 );
41static HRESULT LogStringWork(
42 __in REPORT_LEVEL rl,
43 __in DWORD dwLogId,
44 __in_z LPCWSTR sczString,
45 __in BOOL fLOGUTIL_NEWLINE
46 );
47
48// Hook to allow redirecting LogStringWorkRaw function calls
49static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL;
50static LPVOID s_vpvLogStringWorkRawContext = NULL;
51
52
53/********************************************************************
54 IsLogInitialized - Checks if log is currently initialized.
55********************************************************************/
56extern "C" BOOL DAPI IsLogInitialized()
57{
58 return LogUtil_fInitializedCriticalSection;
59}
60
61/********************************************************************
62 IsLogOpen - Checks if log is currently initialized and open.
63********************************************************************/
64extern "C" BOOL DAPI IsLogOpen()
65{
66 return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath);
67}
68
69
70/********************************************************************
71 LogInitialize - initializes the logutil API
72
73********************************************************************/
74extern "C" void DAPI LogInitialize(
75 __in HMODULE hModule
76 )
77{
78 AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called.");
79
80 LogUtil_hModule = hModule;
81 LogUtil_fDisabled = FALSE;
82
83 ::InitializeCriticalSection(&LogUtil_csLog);
84 LogUtil_fInitializedCriticalSection = TRUE;
85}
86
87
88/********************************************************************
89 LogOpen - creates an application log file
90
91 NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name
92********************************************************************/
93extern "C" HRESULT DAPI LogOpen(
94 __in_z_opt LPCWSTR wzDirectory,
95 __in_z LPCWSTR wzLog,
96 __in_z_opt LPCWSTR wzPostfix,
97 __in_z_opt LPCWSTR wzExt,
98 __in BOOL fAppend,
99 __in BOOL fHeader,
100 __out_z_opt LPWSTR* psczLogPath
101 )
102{
103 HRESULT hr = S_OK;
104 BOOL fEnteredCriticalSection = FALSE;
105 LPWSTR sczLogDirectory = NULL;
106
107 ::EnterCriticalSection(&LogUtil_csLog);
108 fEnteredCriticalSection = TRUE;
109
110 if (wzExt && *wzExt)
111 {
112 hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog);
113 ExitOnFailure(hr, "Failed to create log based on current system time.");
114 }
115 else
116 {
117 hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath);
118 ExitOnFailure(hr, "Failed to combine the log path.");
119
120 hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory);
121 ExitOnFailure(hr, "Failed to get log directory.");
122
123 hr = DirEnsureExists(sczLogDirectory, NULL);
124 ExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory);
125
126 LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
127 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
128 {
129 ExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath);
130 }
131
132 if (fAppend)
133 {
134 ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END);
135 }
136 }
137
138 if (fHeader)
139 {
140 LogHeader();
141 }
142
143 if (NULL != LogUtil_sczPreInitBuffer)
144 {
145 // Log anything that was logged before LogOpen() was called.
146 LogStringWorkRaw(LogUtil_sczPreInitBuffer);
147 ReleaseNullStr(LogUtil_sczPreInitBuffer);
148 }
149
150 if (psczLogPath)
151 {
152 hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0);
153 ExitOnFailure(hr, "Failed to copy log path.");
154 }
155
156 LogUtil_fDisabled = FALSE;
157
158LExit:
159 if (fEnteredCriticalSection)
160 {
161 ::LeaveCriticalSection(&LogUtil_csLog);
162 }
163
164 ReleaseStr(sczLogDirectory);
165
166 return hr;
167}
168
169
170/********************************************************************
171 LogDisable - closes any open files and disables in memory logging.
172
173********************************************************************/
174void DAPI LogDisable()
175{
176 ::EnterCriticalSection(&LogUtil_csLog);
177
178 LogUtil_fDisabled = TRUE;
179
180 ReleaseFileHandle(LogUtil_hLog);
181 ReleaseNullStr(LogUtil_sczLogPath);
182 ReleaseNullStr(LogUtil_sczPreInitBuffer);
183
184 ::LeaveCriticalSection(&LogUtil_csLog);
185}
186
187
188/********************************************************************
189 LogRedirect - Redirects all logging strings to the specified
190 function - or set NULL to disable the hook
191********************************************************************/
192void DAPI LogRedirect(
193 __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw,
194 __in_opt LPVOID pvContext
195 )
196{
197 s_vpfLogStringWorkRaw = vpfLogStringWorkRaw;
198 s_vpvLogStringWorkRawContext = pvContext;
199}
200
201
202/********************************************************************
203 LogRename - Renames a logfile, moving its contents to a new path,
204 and re-opening the file for appending at the new
205 location
206********************************************************************/
207HRESULT DAPI LogRename(
208 __in_z LPCWSTR wzNewPath
209 )
210{
211 HRESULT hr = S_OK;
212 BOOL fEnteredCriticalSection = FALSE;
213
214 ::EnterCriticalSection(&LogUtil_csLog);
215 fEnteredCriticalSection = TRUE;
216
217 ReleaseFileHandle(LogUtil_hLog);
218
219 hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE);
220 ExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath);
221
222 hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0);
223 ExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath);
224
225 LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
226 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
227 {
228 ExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath);
229 }
230
231 // Enable "append" mode by moving file pointer to the end
232 ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END);
233
234LExit:
235 if (fEnteredCriticalSection)
236 {
237 ::LeaveCriticalSection(&LogUtil_csLog);
238 }
239
240 return hr;
241}
242
243
244extern "C" void DAPI LogClose(
245 __in BOOL fFooter
246 )
247{
248 if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter)
249 {
250 LogFooter();
251 }
252
253 ReleaseFileHandle(LogUtil_hLog);
254 ReleaseNullStr(LogUtil_sczLogPath);
255 ReleaseNullStr(LogUtil_sczPreInitBuffer);
256}
257
258
259extern "C" void DAPI LogUninitialize(
260 __in BOOL fFooter
261 )
262{
263 LogClose(fFooter);
264
265 if (LogUtil_fInitializedCriticalSection)
266 {
267 ::DeleteCriticalSection(&LogUtil_csLog);
268 LogUtil_fInitializedCriticalSection = FALSE;
269 }
270
271 LogUtil_hModule = NULL;
272 LogUtil_fDisabled = FALSE;
273
274 ReleaseNullStr(LogUtil_sczSpecialBeginLine);
275 ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp);
276 ReleaseNullStr(LogUtil_sczSpecialEndLine);
277}
278
279
280/********************************************************************
281 LogIsOpen - returns whether log file is open or note
282
283********************************************************************/
284extern "C" BOOL DAPI LogIsOpen()
285{
286 return INVALID_HANDLE_VALUE != LogUtil_hLog;
287}
288
289
290/********************************************************************
291 LogSetSpecialParams - sets a special beginline string, endline
292 string, post-timestamp string, etc.
293********************************************************************/
294HRESULT DAPI LogSetSpecialParams(
295 __in_z_opt LPCWSTR wzSpecialBeginLine,
296 __in_z_opt LPCWSTR wzSpecialAfterTimeStamp,
297 __in_z_opt LPCWSTR wzSpecialEndLine
298 )
299{
300 HRESULT hr = S_OK;
301
302 // Handle special string to be prepended before every full line
303 if (NULL == wzSpecialBeginLine)
304 {
305 ReleaseNullStr(LogUtil_sczSpecialBeginLine);
306 }
307 else
308 {
309 hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0);
310 ExitOnFailure(hr, "Failed to allocate copy of special beginline string");
311 }
312
313 // Handle special string to be appended to every time stamp
314 if (NULL == wzSpecialAfterTimeStamp)
315 {
316 ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp);
317 }
318 else
319 {
320 hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0);
321 ExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string");
322 }
323
324 // Handle special string to be appended before every full line
325 if (NULL == wzSpecialEndLine)
326 {
327 ReleaseNullStr(LogUtil_sczSpecialEndLine);
328 }
329 else
330 {
331 hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0);
332 ExitOnFailure(hr, "Failed to allocate copy of special endline string");
333 }
334
335LExit:
336 return hr;
337}
338
339/********************************************************************
340 LogSetLevel - sets the logging level
341
342 NOTE: returns previous logging level
343********************************************************************/
344extern "C" REPORT_LEVEL DAPI LogSetLevel(
345 __in REPORT_LEVEL rl,
346 __in BOOL fLogChange
347 )
348{
349 AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set");
350
351 REPORT_LEVEL rlPrev = LogUtil_rlCurrent;
352
353 if (LogUtil_rlCurrent != rl)
354 {
355 LogUtil_rlCurrent = rl;
356
357 if (fLogChange)
358 {
359 LPCSTR szLevel = LOGUTIL_UNKNOWN;
360 switch (LogUtil_rlCurrent)
361 {
362 case REPORT_WARNING:
363 szLevel = LOGUTIL_WARNING;
364 break;
365 case REPORT_STANDARD:
366 szLevel = LOGUTIL_STANDARD;
367 break;
368 case REPORT_VERBOSE:
369 szLevel = LOGUTIL_VERBOSE;
370 break;
371 case REPORT_DEBUG:
372 szLevel = LOGUTIL_DEBUG;
373 break;
374 case REPORT_NONE:
375 szLevel = LOGUTIL_NONE;
376 break;
377 }
378
379 LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel);
380 }
381 }
382
383 return rlPrev;
384}
385
386
387/********************************************************************
388 LogGetLevel - gets the current logging level
389
390********************************************************************/
391extern "C" REPORT_LEVEL DAPI LogGetLevel()
392{
393 return LogUtil_rlCurrent;
394}
395
396
397/********************************************************************
398 LogGetPath - gets the current log path
399
400********************************************************************/
401extern "C" HRESULT DAPI LogGetPath(
402 __out_ecount_z(cchLogPath) LPWSTR pwzLogPath,
403 __in DWORD cchLogPath
404 )
405{
406 Assert(pwzLogPath);
407
408 HRESULT hr = S_OK;
409
410 if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one!
411 {
412 ExitFunction1(hr = E_UNEXPECTED);
413 }
414
415 hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath);
416
417LExit:
418 return hr;
419}
420
421
422/********************************************************************
423 LogGetHandle - gets the current log file handle
424
425********************************************************************/
426extern "C" HANDLE DAPI LogGetHandle()
427{
428 return LogUtil_hLog;
429}
430
431
432/********************************************************************
433 LogString - write a string to the log
434
435 NOTE: use printf formatting ("%ls", "%d", etc.)
436********************************************************************/
437extern "C" HRESULT DAPIV LogString(
438 __in REPORT_LEVEL rl,
439 __in_z __format_string LPCSTR szFormat,
440 ...
441 )
442{
443 HRESULT hr = S_OK;
444 va_list args;
445
446 va_start(args, szFormat);
447 hr = LogStringArgs(rl, szFormat, args);
448 va_end(args);
449
450 return hr;
451}
452
453extern "C" HRESULT DAPI LogStringArgs(
454 __in REPORT_LEVEL rl,
455 __in_z __format_string LPCSTR szFormat,
456 __in va_list args
457 )
458{
459 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
460 HRESULT hr = S_OK;
461
462 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
463 {
464 ExitFunction1(hr = S_FALSE);
465 }
466
467 hr = LogStringWorkArgs(rl, szFormat, args, FALSE);
468
469LExit:
470 return hr;
471}
472
473/********************************************************************
474 LogStringLine - write a string plus LOGUTIL_NEWLINE to the log
475
476 NOTE: use printf formatting ("%ls", "%d", etc.)
477********************************************************************/
478extern "C" HRESULT DAPIV LogStringLine(
479 __in REPORT_LEVEL rl,
480 __in_z __format_string LPCSTR szFormat,
481 ...
482 )
483{
484 HRESULT hr = S_OK;
485 va_list args;
486
487 va_start(args, szFormat);
488 hr = LogStringLineArgs(rl, szFormat, args);
489 va_end(args);
490
491 return hr;
492}
493
494extern "C" HRESULT DAPI LogStringLineArgs(
495 __in REPORT_LEVEL rl,
496 __in_z __format_string LPCSTR szFormat,
497 __in va_list args
498 )
499{
500 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
501 HRESULT hr = S_OK;
502
503 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
504 {
505 ExitFunction1(hr = S_FALSE);
506 }
507
508 hr = LogStringWorkArgs(rl, szFormat, args, TRUE);
509
510LExit:
511 return hr;
512}
513
514/********************************************************************
515 LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log
516
517 NOTE: uses format string from MESSAGETABLE resource
518********************************************************************/
519
520extern "C" HRESULT DAPI LogIdModuleArgs(
521 __in REPORT_LEVEL rl,
522 __in DWORD dwLogId,
523 __in_opt HMODULE hModule,
524 __in va_list args
525 )
526{
527 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
528 HRESULT hr = S_OK;
529
530 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
531 {
532 ExitFunction1(hr = S_FALSE);
533 }
534
535 hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE);
536
537LExit:
538 return hr;
539}
540
541extern "C" HRESULT DAPI LogIdModule(
542 __in REPORT_LEVEL rl,
543 __in DWORD dwLogId,
544 __in_opt HMODULE hModule,
545 ...
546 )
547{
548 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
549 HRESULT hr = S_OK;
550 va_list args;
551
552 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
553 {
554 ExitFunction1(hr = S_FALSE);
555 }
556
557 va_start(args, hModule);
558 hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE);
559 va_end(args);
560
561LExit:
562 return hr;
563}
564
565
566
567
568/********************************************************************
569 LogError - write an error to the log
570
571 NOTE: use printf formatting ("%ls", "%d", etc.)
572********************************************************************/
573extern "C" HRESULT DAPIV LogErrorString(
574 __in HRESULT hrError,
575 __in_z __format_string LPCSTR szFormat,
576 ...
577 )
578{
579 HRESULT hr = S_OK;
580
581 va_list args;
582 va_start(args, szFormat);
583 hr = LogErrorStringArgs(hrError, szFormat, args);
584 va_end(args);
585
586 return hr;
587}
588
589extern "C" HRESULT DAPI LogErrorStringArgs(
590 __in HRESULT hrError,
591 __in_z __format_string LPCSTR szFormat,
592 __in va_list args
593 )
594{
595 HRESULT hr = S_OK;
596 LPWSTR sczFormat = NULL;
597 LPWSTR sczMessage = NULL;
598
599 hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP);
600 ExitOnFailure(hr, "Failed to convert format string to wide character string");
601
602 // format the string as a unicode string - this is necessary to be able to include
603 // international characters in our output string. This does have the counterintuitive effect
604 // that the caller's "%s" is interpreted differently
605 // (so callers should use %hs for LPSTR and %ls for LPWSTR)
606 hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args);
607 ExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat);
608
609 hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage);
610
611LExit:
612 ReleaseStr(sczFormat);
613 ReleaseStr(sczMessage);
614
615 return hr;
616}
617
618
619/********************************************************************
620 LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log
621
622 NOTE: uses format string from MESSAGETABLE resource
623 can log no more than three strings in the error message
624********************************************************************/
625extern "C" HRESULT DAPI LogErrorIdModule(
626 __in HRESULT hrError,
627 __in DWORD dwLogId,
628 __in_opt HMODULE hModule,
629 __in_z_opt LPCWSTR wzString1 = NULL,
630 __in_z_opt LPCWSTR wzString2 = NULL,
631 __in_z_opt LPCWSTR wzString3 = NULL
632 )
633{
634 HRESULT hr = S_OK;
635 WCHAR wzError[11];
636 WORD cStrings = 1; // guaranteed wzError is in the list
637
638 hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError);
639 ExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError);
640
641 cStrings += wzString1 ? 1 : 0;
642 cStrings += wzString2 ? 1 : 0;
643 cStrings += wzString3 ? 1 : 0;
644
645 hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3);
646 ExitOnFailure(hr, "Failed to log id module.");
647
648LExit:
649 return hr;
650}
651
652/********************************************************************
653 LogHeader - write a standard header to the log
654
655********************************************************************/
656extern "C" HRESULT DAPI LogHeader()
657{
658 HRESULT hr = S_OK;
659 WCHAR wzComputerName[MAX_PATH];
660 DWORD cchComputerName = countof(wzComputerName);
661 WCHAR wzPath[MAX_PATH];
662 DWORD dwMajorVersion = 0;
663 DWORD dwMinorVersion = 0;
664 LPCSTR szLevel = LOGUTIL_UNKNOWN;
665 LPWSTR sczCurrentDateTime = NULL;
666
667 //
668 // get the interesting data
669 //
670 if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath)))
671 {
672 memset(wzPath, 0, sizeof(wzPath));
673 }
674
675 hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion);
676 if (FAILED(hr))
677 {
678 dwMajorVersion = 0;
679 dwMinorVersion = 0;
680 }
681
682 if (!::GetComputerNameW(wzComputerName, &cchComputerName))
683 {
684 ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName));
685 }
686
687 TimeCurrentDateTime(&sczCurrentDateTime, FALSE);
688
689 //
690 // write data to the log
691 //
692 LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime);
693 LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF);
694 LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName);
695 switch (LogUtil_rlCurrent)
696 {
697 case REPORT_WARNING:
698 szLevel = LOGUTIL_WARNING;
699 break;
700 case REPORT_STANDARD:
701 szLevel = LOGUTIL_STANDARD;
702 break;
703 case REPORT_VERBOSE:
704 szLevel = LOGUTIL_VERBOSE;
705 break;
706 case REPORT_DEBUG:
707 szLevel = LOGUTIL_DEBUG;
708 break;
709 case REPORT_NONE:
710 szLevel = LOGUTIL_NONE;
711 break;
712 }
713 LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel);
714
715 hr = S_OK;
716
717 ReleaseStr(sczCurrentDateTime);
718
719 return hr;
720}
721
722
723/********************************************************************
724 LogFooterWork - write a standard footer to the log
725
726********************************************************************/
727
728static HRESULT LogFooterWork(
729 __in_z __format_string LPCSTR szFormat,
730 ...
731 )
732{
733 HRESULT hr = S_OK;
734
735 va_list args;
736 va_start(args, szFormat);
737 hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE);
738 va_end(args);
739
740 return hr;
741}
742
743extern "C" HRESULT DAPI LogFooter()
744{
745 HRESULT hr = S_OK;
746 LPWSTR sczCurrentDateTime = NULL;
747 TimeCurrentDateTime(&sczCurrentDateTime, FALSE);
748 hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime);
749 ReleaseStr(sczCurrentDateTime);
750 return hr;
751}
752
753/********************************************************************
754 LogStringWorkRaw - Write a raw, unformatted string to the log
755
756********************************************************************/
757extern "C" HRESULT LogStringWorkRaw(
758 __in_z LPCSTR szLogData
759 )
760{
761 Assert(szLogData && *szLogData);
762
763 HRESULT hr = S_OK;
764 DWORD cbLogData = 0;
765 DWORD cbTotal = 0;
766 DWORD cbWrote = 0;
767
768 cbLogData = lstrlenA(szLogData);
769
770 // If the log hasn't been initialized yet, store it in a buffer
771 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
772 {
773 hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0);
774 ExitOnFailure(hr, "Failed to concatenate string to pre-init buffer");
775
776 ExitFunction1(hr = S_OK);
777 }
778
779 // write the string
780 while (cbTotal < cbLogData)
781 {
782 if (!::WriteFile(LogUtil_hLog, reinterpret_cast<const BYTE*>(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL))
783 {
784 ExitOnLastError(hr, "Failed to write output to log: %ls - %ls", LogUtil_sczLogPath, szLogData);
785 }
786
787 cbTotal += cbWrote;
788 }
789
790LExit:
791 return hr;
792}
793
794//
795// private worker functions
796//
797static HRESULT LogIdWork(
798 __in REPORT_LEVEL rl,
799 __in_opt HMODULE hModule,
800 __in DWORD dwLogId,
801 __in va_list args,
802 __in BOOL fLOGUTIL_NEWLINE
803 )
804{
805 HRESULT hr = S_OK;
806 LPWSTR pwz = NULL;
807 DWORD cch = 0;
808
809 // get the string for the id
810#pragma prefast(push)
811#pragma prefast(disable:25028)
812#pragma prefast(disable:25068)
813 cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
814 static_cast<LPCVOID>(hModule), dwLogId, 0, reinterpret_cast<LPWSTR>(&pwz), 0, &args);
815#pragma prefast(pop)
816
817 if (0 == cch)
818 {
819 ExitOnLastError(hr, "failed to log id: %d", dwLogId);
820 }
821
822 if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1])
823 {
824 pwz[cch-2] = L'\0'; // remove newline from message table
825 }
826
827 LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE);
828
829LExit:
830 if (pwz)
831 {
832 ::LocalFree(pwz);
833 }
834
835 return hr;
836}
837
838
839static HRESULT LogStringWorkArgs(
840 __in REPORT_LEVEL rl,
841 __in_z __format_string LPCSTR szFormat,
842 __in va_list args,
843 __in BOOL fLOGUTIL_NEWLINE
844 )
845{
846 Assert(szFormat && *szFormat);
847
848 HRESULT hr = S_OK;
849 LPWSTR sczFormat = NULL;
850 LPWSTR sczMessage = NULL;
851
852 hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP);
853 ExitOnFailure(hr, "Failed to convert format string to wide character string");
854
855 // format the string as a unicode string
856 hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args);
857 ExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat);
858
859 hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE);
860 ExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage);
861
862LExit:
863 ReleaseStr(sczFormat);
864 ReleaseStr(sczMessage);
865
866 return hr;
867}
868
869
870static HRESULT LogStringWork(
871 __in REPORT_LEVEL rl,
872 __in DWORD dwLogId,
873 __in_z LPCWSTR sczString,
874 __in BOOL fLOGUTIL_NEWLINE
875 )
876{
877 Assert(sczString && *sczString);
878
879 HRESULT hr = S_OK;
880 BOOL fEnteredCriticalSection = FALSE;
881 LPWSTR scz = NULL;
882 LPCWSTR wzLogData = NULL;
883 LPSTR sczMultiByte = NULL;
884
885 // If logging is disabled, just bail.
886 if (LogUtil_fDisabled)
887 {
888 ExitFunction();
889 }
890
891 ::EnterCriticalSection(&LogUtil_csLog);
892 fEnteredCriticalSection = TRUE;
893
894 if (fLOGUTIL_NEWLINE)
895 {
896 // get the process and thread id.
897 DWORD dwProcessId = ::GetCurrentProcessId();
898 DWORD dwThreadId = ::GetCurrentThreadId();
899
900 // get the time relative to GMT.
901 SYSTEMTIME st = { };
902 ::GetLocalTime(&st);
903
904 DWORD dwId = dwLogId & 0xFFFFFFF;
905 DWORD dwType = dwLogId & 0xF0000000;
906 LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i";
907
908 // add line prefix and trailing newline
909 hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"",
910 dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId,
911 LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n");
912 ExitOnFailure(hr, "Failed to format line prefix.");
913 }
914
915 wzLogData = scz ? scz : sczString;
916
917 // Convert to UTF-8 before writing out to the log file
918 hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8);
919 ExitOnFailure(hr, "Failed to convert log string to UTF-8");
920
921 if (s_vpfLogStringWorkRaw)
922 {
923 hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext);
924 ExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString);
925 }
926 else
927 {
928 hr = LogStringWorkRaw(sczMultiByte);
929 ExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString);
930 }
931
932LExit:
933 if (fEnteredCriticalSection)
934 {
935 ::LeaveCriticalSection(&LogUtil_csLog);
936 }
937
938 ReleaseStr(scz);
939 ReleaseStr(sczMultiByte);
940
941 return hr;
942}