summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/utils/wlog/Layout.c
diff options
context:
space:
mode:
Diffstat (limited to 'winpr/libwinpr/utils/wlog/Layout.c')
-rw-r--r--winpr/libwinpr/utils/wlog/Layout.c375
1 files changed, 375 insertions, 0 deletions
diff --git a/winpr/libwinpr/utils/wlog/Layout.c b/winpr/libwinpr/utils/wlog/Layout.c
new file mode 100644
index 0000000..188c15b
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/Layout.c
@@ -0,0 +1,375 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * WinPR Logger
+ *
+ * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/print.h>
+#include <winpr/sysinfo.h>
+#include <winpr/environment.h>
+
+#include "wlog.h"
+
+#include "Layout.h"
+
+#if defined __linux__ && !defined ANDROID
+#include <unistd.h>
+#include <sys/syscall.h>
+#endif
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+struct format_option_recurse;
+
+struct format_option
+{
+ const char* fmt;
+ size_t fmtlen;
+ const char* replace;
+ size_t replacelen;
+ const char* (*fkt)(void*);
+ void* arg;
+ const char* (*ext)(const struct format_option* opt, const char* str, size_t* preplacelen,
+ size_t* pskiplen);
+ struct format_option_recurse* recurse;
+};
+
+struct format_option_recurse
+{
+ struct format_option* options;
+ size_t nroptions;
+ wLog* log;
+ wLogLayout* layout;
+ wLogMessage* message;
+ char buffer[WLOG_MAX_PREFIX_SIZE];
+};
+
+/**
+ * Log Layout
+ */
+WINPR_ATTR_FORMAT_ARG(3, 0)
+static void WLog_PrintMessagePrefixVA(wLog* log, wLogMessage* message,
+ WINPR_FORMAT_ARG const char* format, va_list args)
+{
+ WINPR_ASSERT(message);
+ vsnprintf(message->PrefixString, WLOG_MAX_PREFIX_SIZE - 1, format, args);
+}
+
+WINPR_ATTR_FORMAT_ARG(3, 4)
+static void WLog_PrintMessagePrefix(wLog* log, wLogMessage* message,
+ WINPR_FORMAT_ARG const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ WLog_PrintMessagePrefixVA(log, message, format, args);
+ va_end(args);
+}
+
+static const char* get_tid(void* arg)
+{
+ char* str = arg;
+ size_t tid = 0;
+#if defined __linux__ && !defined ANDROID
+ /* On Linux we prefer to see the LWP id */
+ tid = (size_t)syscall(SYS_gettid);
+#else
+ tid = (size_t)GetCurrentThreadId();
+#endif
+ sprintf(str, "%08" PRIxz, tid);
+ return str;
+}
+
+static BOOL log_invalid_fmt(const char* what)
+{
+ fprintf(stderr, "Invalid format string '%s'\n", what);
+ return FALSE;
+}
+
+static BOOL check_and_log_format_size(char* format, size_t size, size_t index, size_t add)
+{
+ /* format string must be '\0' terminated, so abort at size - 1 */
+ if (index + add + 1 >= size)
+ {
+ fprintf(stderr,
+ "Format string too long ['%s', max %" PRIuz ", used %" PRIuz ", adding %" PRIuz
+ "]\n",
+ format, size, index, add);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int opt_compare_fn(const void* a, const void* b)
+{
+ const char* what = a;
+ const struct format_option* opt = b;
+ if (!opt)
+ return -1;
+ return strncmp(what, opt->fmt, opt->fmtlen);
+}
+
+static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
+ char* format, size_t formatlen);
+
+static const char* skip_if_null(const struct format_option* opt, const char* fmt,
+ size_t* preplacelen, size_t* pskiplen)
+{
+ WINPR_ASSERT(opt);
+ WINPR_ASSERT(fmt);
+ WINPR_ASSERT(preplacelen);
+ WINPR_ASSERT(pskiplen);
+
+ *preplacelen = 0;
+ *pskiplen = 0;
+
+ const char* str = &fmt[opt->fmtlen]; /* Skip first %{ from string */
+ const char* end = strstr(str, opt->replace);
+ if (!end)
+ return NULL;
+ *pskiplen = end - fmt + opt->replacelen;
+
+ if (!opt->arg)
+ return NULL;
+
+ const size_t replacelen = end - str;
+
+ char buffer[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ memcpy(buffer, str, MIN(replacelen, ARRAYSIZE(buffer) - 1));
+
+ if (!replace_format_string(buffer, opt->recurse, opt->recurse->buffer,
+ ARRAYSIZE(opt->recurse->buffer)))
+ return NULL;
+
+ *preplacelen = strnlen(opt->recurse->buffer, ARRAYSIZE(opt->recurse->buffer));
+ return opt->recurse->buffer;
+}
+
+static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
+ char* format, size_t formatlen)
+{
+ WINPR_ASSERT(FormatString);
+ WINPR_ASSERT(recurse);
+
+ size_t index = 0;
+
+ while (*FormatString)
+ {
+ const struct format_option* opt =
+ bsearch(FormatString, recurse->options, recurse->nroptions,
+ sizeof(struct format_option), opt_compare_fn);
+ if (opt)
+ {
+ size_t replacelen = opt->replacelen;
+ size_t fmtlen = opt->fmtlen;
+ const char* replace = opt->replace;
+ const void* arg = opt->arg;
+
+ if (opt->ext)
+ replace = opt->ext(opt, FormatString, &replacelen, &fmtlen);
+ if (opt->fkt)
+ arg = opt->fkt(opt->arg);
+
+ if (replace && (replacelen > 0))
+ {
+ const int rc = _snprintf(&format[index], formatlen - index, replace, arg);
+ if (rc < 0)
+ return FALSE;
+ if (!check_and_log_format_size(format, formatlen, index, rc))
+ return FALSE;
+ index += rc;
+ }
+ FormatString += fmtlen;
+ }
+ else
+ {
+ /* Unknown format string */
+ if (*FormatString == '%')
+ return log_invalid_fmt(FormatString);
+
+ if (!check_and_log_format_size(format, formatlen, index, 1))
+ return FALSE;
+ format[index++] = *FormatString++;
+ }
+ }
+
+ if (!check_and_log_format_size(format, formatlen, index, 0))
+ return FALSE;
+ return TRUE;
+}
+
+BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message)
+{
+ char format[WLOG_MAX_PREFIX_SIZE] = { 0 };
+
+ WINPR_ASSERT(layout);
+ WINPR_ASSERT(message);
+
+ char tid[32] = { 0 };
+ SYSTEMTIME localTime = { 0 };
+ GetLocalTime(&localTime);
+
+ struct format_option_recurse recurse = {
+ .options = NULL, .nroptions = 0, .log = log, .layout = layout, .message = message
+ };
+
+#define ENTRY(x) x, sizeof(x) - 1
+ struct format_option options[] = {
+ { ENTRY("%ctx"), ENTRY("%s"), log->custom, log->context, NULL, &recurse }, /* log context */
+ { ENTRY("%dw"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDayOfWeek, NULL,
+ &recurse }, /* day of week */
+ { ENTRY("%dy"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDay, NULL,
+ &recurse }, /* day of year */
+ { ENTRY("%fl"), ENTRY("%s"), NULL, (void*)message->FileName, NULL, &recurse }, /* file */
+ { ENTRY("%fn"), ENTRY("%s"), NULL, (void*)message->FunctionName, NULL,
+ &recurse }, /* function */
+ { ENTRY("%hr"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wHour, NULL,
+ &recurse }, /* hours */
+ { ENTRY("%ln"), ENTRY("%" PRIuz), NULL, (void*)(size_t)message->LineNumber, NULL,
+ &recurse }, /* line number */
+ { ENTRY("%lv"), ENTRY("%s"), NULL, (void*)WLOG_LEVELS[message->Level], NULL,
+ &recurse }, /* log level */
+ { ENTRY("%mi"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMinute, NULL,
+ &recurse }, /* minutes */
+ { ENTRY("%ml"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMilliseconds, NULL,
+ &recurse }, /* milliseconds */
+ { ENTRY("%mn"), ENTRY("%s"), NULL, log->Name, NULL, &recurse }, /* module name */
+ { ENTRY("%mo"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wMonth, NULL,
+ &recurse }, /* month */
+ { ENTRY("%pid"), ENTRY("%u"), NULL, (void*)(size_t)GetCurrentProcessId(), NULL,
+ &recurse }, /* process id */
+ { ENTRY("%se"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wSecond, NULL,
+ &recurse }, /* seconds */
+ { ENTRY("%tid"), ENTRY("%s"), get_tid, tid, NULL, &recurse }, /* thread id */
+ { ENTRY("%yr"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wYear, NULL,
+ &recurse }, /* year */
+ { ENTRY("%{"), ENTRY("%}"), NULL, log->context, skip_if_null,
+ &recurse }, /* skip if no context */
+ };
+
+ recurse.options = options;
+ recurse.nroptions = ARRAYSIZE(options);
+
+ if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
+ return FALSE;
+
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
+
+ WLog_PrintMessagePrefix(log, message, format);
+
+ WINPR_PRAGMA_DIAG_POP
+
+ return TRUE;
+}
+
+wLogLayout* WLog_GetLogLayout(wLog* log)
+{
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+ return appender->Layout;
+}
+
+BOOL WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format)
+{
+ free(layout->FormatString);
+ layout->FormatString = NULL;
+
+ if (format)
+ {
+ layout->FormatString = _strdup(format);
+
+ if (!layout->FormatString)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+wLogLayout* WLog_Layout_New(wLog* log)
+{
+ LPCSTR prefix = "WLOG_PREFIX";
+ DWORD nSize = 0;
+ char* env = NULL;
+ wLogLayout* layout = NULL;
+ layout = (wLogLayout*)calloc(1, sizeof(wLogLayout));
+
+ if (!layout)
+ return NULL;
+
+ nSize = GetEnvironmentVariableA(prefix, NULL, 0);
+
+ if (nSize)
+ {
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ {
+ free(layout);
+ return NULL;
+ }
+
+ if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
+ {
+ free(env);
+ free(layout);
+ return NULL;
+ }
+ }
+
+ if (env)
+ layout->FormatString = env;
+ else
+ {
+#ifdef ANDROID
+ layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
+#else
+ layout->FormatString =
+ _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
+#endif
+
+ if (!layout->FormatString)
+ {
+ free(layout);
+ return NULL;
+ }
+ }
+
+ return layout;
+}
+
+void WLog_Layout_Free(wLog* log, wLogLayout* layout)
+{
+ if (layout)
+ {
+ if (layout->FormatString)
+ {
+ free(layout->FormatString);
+ layout->FormatString = NULL;
+ }
+
+ free(layout);
+ }
+}