20#include <winpr/config.h>
27#include <winpr/assert.h>
28#include <winpr/print.h>
29#include <winpr/sysinfo.h>
30#include <winpr/environment.h>
36#if defined __linux__ && !defined ANDROID
38#include <sys/syscall.h>
42#define MIN(x, y) (((x) < (y)) ? (x) : (y))
45struct format_option_recurse;
58 const char* (*fkt)(
void*);
65 const char* (*ext)(
const struct format_option* opt,
const char* str,
size_t* preplacelen,
67 struct format_option_recurse* recurse;
70struct format_option_recurse
72 struct format_option* options;
77 char buffer[WLOG_MAX_PREFIX_SIZE];
83WINPR_ATTR_FORMAT_ARG(3, 0)
84static
void WLog_PrintMessagePrefixVA(
char* prefix,
size_t prefixlen,
85 WINPR_FORMAT_ARG const
char* format, va_list args)
87 (void)vsnprintf(prefix, prefixlen, format, args);
90WINPR_ATTR_FORMAT_ARG(3, 4)
91static
void WLog_PrintMessagePrefix(
char* prefix,
size_t prefixlen,
92 WINPR_FORMAT_ARG const
char* format, ...)
94 va_list args = WINPR_C_ARRAY_INIT;
95 va_start(args, format);
96 WLog_PrintMessagePrefixVA(prefix, prefixlen, format, args);
100static const char* get_tid(
void* arg)
102 struct format_tid_arg* targ = arg;
106#if defined __linux__ && !defined ANDROID
108 tid = (size_t)syscall(SYS_gettid);
110 tid = (size_t)GetCurrentThreadId();
112 (void)_snprintf(targ->tid,
sizeof(targ->tid),
"%08" PRIxz, tid);
116static BOOL log_invalid_fmt(
const char* what)
118 (void)fprintf(stderr,
"Invalid format string '%s'\n", what);
122static BOOL check_and_log_format_size(
char* format,
size_t size,
size_t index,
size_t add)
125 if (index + add + 1 >= size)
127 (void)fprintf(stderr,
128 "Format string too long ['%s', max %" PRIuz
", used %" PRIuz
129 ", adding %" PRIuz
"]\n",
130 format, size, index, add);
136static int opt_compare_fn(
const void* a,
const void* b)
138 const char* what = a;
139 const struct format_option* opt = b;
142 return strncmp(what, opt->fmt, opt->fmtlen);
145static BOOL replace_format_string(
const char* FormatString,
struct format_option_recurse* recurse,
146 char* format,
size_t formatlen);
148static const char* skip_if_null(
const struct format_option* opt,
const char* fmt,
149 size_t* preplacelen,
size_t* pskiplen)
153 WINPR_ASSERT(preplacelen);
154 WINPR_ASSERT(pskiplen);
159 const char* str = &fmt[opt->fmtlen];
160 const char* end = strstr(str, opt->replace);
163 *pskiplen = WINPR_ASSERTING_INT_CAST(
size_t, end - fmt) + opt->replacelen;
168 const size_t replacelen = WINPR_ASSERTING_INT_CAST(
size_t, end - str);
170 char buffer[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
171 memcpy(buffer, str, MIN(replacelen, ARRAYSIZE(buffer) - 1));
173 if (!replace_format_string(buffer, opt->recurse, opt->recurse->buffer,
174 ARRAYSIZE(opt->recurse->buffer)))
177 *preplacelen = strnlen(opt->recurse->buffer, ARRAYSIZE(opt->recurse->buffer));
178 return opt->recurse->buffer;
181static BOOL replace_format_string(
const char* FormatString,
struct format_option_recurse* recurse,
182 char* format,
size_t formatlen)
184 WINPR_ASSERT(FormatString);
185 WINPR_ASSERT(recurse);
188 const char* prefix = WLog_GetGlobalPrefix();
191 const int res = _snprintf(format, formatlen,
"{%s}", prefix);
197 while (*FormatString)
199 const struct format_option* opt =
200 bsearch(FormatString, recurse->options, recurse->nroptions,
201 sizeof(
struct format_option), opt_compare_fn);
204 size_t replacelen = opt->replacelen;
205 size_t fmtlen = opt->fmtlen;
206 const char* replace = opt->replace;
207 const void* arg = opt->arg.cpv;
210 replace = opt->ext(opt, FormatString, &replacelen, &fmtlen);
212 arg = opt->fkt(opt->arg.pv);
214 if (replace && (replacelen > 0))
216 WINPR_PRAGMA_DIAG_PUSH
217 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
218 const int rc = _snprintf(&format[index], formatlen - index, replace, arg);
219 WINPR_PRAGMA_DIAG_POP
222 if (!check_and_log_format_size(format, formatlen, index,
223 WINPR_ASSERTING_INT_CAST(
size_t, rc)))
225 index += WINPR_ASSERTING_INT_CAST(
size_t, rc);
227 FormatString += fmtlen;
232 if (*FormatString ==
'%')
233 return log_invalid_fmt(FormatString);
235 if (!check_and_log_format_size(format, formatlen, index, 1))
237 format[index++] = *FormatString++;
241 return check_and_log_format_size(format, formatlen, index, 0);
244BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout,
const wLogMessage* message,
245 char* prefix,
size_t prefixlen)
247 char format[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
249 WINPR_ASSERT(layout);
250 WINPR_ASSERT(message);
251 WINPR_ASSERT(prefix);
253 struct format_tid_arg targ = WINPR_C_ARRAY_INIT;
256 GetLocalTime(&localTime);
258 struct format_option_recurse recurse = {
259 .options =
nullptr, .nroptions = 0, .log = log, .layout = layout, .message = message
262#define ENTRY(x) x, sizeof(x) - 1
263 struct format_option options[] = {
267 { .pv = log->context },
273 { .s = localTime.wDayOfWeek },
279 { .s = localTime.wDay },
286 { .cpv = message->FileName },
293 { .cpv = message->FunctionName },
300 { .s = localTime.wHour },
307 { .s = message->LineNumber },
313 { .cpv = WLOG_LEVELS[message->Level] },
319 { .s = localTime.wMinute },
326 { .s = localTime.wMilliseconds },
329 { ENTRY(
"%mn"), ENTRY(
"%s"),
nullptr, { .cpv = log->Name },
nullptr, &recurse },
334 { .s = localTime.wMonth },
341 { .s = GetCurrentProcessId() },
347 { .s = localTime.wSecond },
351 { ENTRY(
"%tid"), ENTRY(
"%s"), get_tid, { .pv = &targ },
nullptr, &recurse },
352 { ENTRY(
"%yr"), ENTRY(
"%u"),
nullptr, { .s = localTime.wYear },
nullptr, &recurse },
357 { .pv = log->context },
362 recurse.options = options;
363 recurse.nroptions = ARRAYSIZE(options);
365 if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
368 WINPR_PRAGMA_DIAG_PUSH
369 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
371 WLog_PrintMessagePrefix(prefix, prefixlen, format);
373 WINPR_PRAGMA_DIAG_POP
378wLogLayout* WLog_GetLogLayout(wLog* log)
380 wLogAppender* appender =
nullptr;
381 appender = WLog_GetLogAppender(log);
382 return appender->Layout;
385BOOL WLog_Layout_SetPrefixFormat(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout,
388 free(layout->FormatString);
389 layout->FormatString =
nullptr;
393 layout->FormatString = _strdup(format);
395 if (!layout->FormatString)
402wLogLayout* WLog_Layout_New(WINPR_ATTR_UNUSED wLog* log)
404 LPCSTR prefix =
"WLOG_PREFIX";
407 wLogLayout* layout =
nullptr;
408 layout = (wLogLayout*)calloc(1,
sizeof(wLogLayout));
413 nSize = GetEnvironmentVariableA(prefix,
nullptr, 0);
417 env = (LPSTR)malloc(nSize);
425 if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
434 layout->FormatString = env;
438 layout->FormatString = _strdup(
"[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
440 layout->FormatString =
441 _strdup(
"[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
444 if (!layout->FormatString)
454void WLog_Layout_Free(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout)
458 if (layout->FormatString)
460 free(layout->FormatString);
461 layout->FormatString =
nullptr;