C Logging: use instead of printf for messages
authorCampbell Barton <ideasman42@gmail.com>
Thu, 29 Mar 2018 18:38:32 +0000 (20:38 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 30 Mar 2018 16:57:41 +0000 (18:57 +0200)
- See `--log` help message for usage.
- Supports enabling categories.
- Color severity.
- Optionally logs to a file.
- Currently use to replace printf calls in wm module.

See D3120 for details.

21 files changed:
build_files/cmake/macros.cmake
intern/CMakeLists.txt
intern/clog/CLG_log.h [new file with mode: 0644]
intern/clog/CMakeLists.txt [new file with mode: 0644]
intern/clog/clog.c [new file with mode: 0644]
release/scripts/startup/bl_ui/space_view3d.py
source/blender/blenkernel/BKE_global.h
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/blender.c
source/blender/depsgraph/intern/eval/deg_eval.cc
source/blender/editors/util/CMakeLists.txt
source/blender/python/mathutils/mathutils_Euler.c
source/blender/windowmanager/CMakeLists.txt
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_init_exit.c
source/blender/windowmanager/intern/wm_keymap.c
source/blender/windowmanager/intern/wm_operators.c
source/creator/CMakeLists.txt
source/creator/creator.c
source/creator/creator_args.c

index f4d37c192bab45fa1c4983c01df4087e7e33e824..f0cff75c4170081b1990472c016c36611d80ea18 100644 (file)
@@ -674,6 +674,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
                extern_sdlew
 
                bf_intern_glew_mx
+               bf_intern_clog
        )
 
        if(NOT WITH_SYSTEM_GLOG)
index bfe230250ae7db42cc40cb5188f0a3d597b4ca24..499b10486553270737bc6512e4438d5a7279149b 100644 (file)
@@ -24,6 +24,7 @@
 # ***** END GPL LICENSE BLOCK *****
 
 # add_subdirectory(atomic)  # header only
+add_subdirectory(clog)
 add_subdirectory(string)
 add_subdirectory(ghost)
 add_subdirectory(guardedalloc)
diff --git a/intern/clog/CLG_log.h b/intern/clog/CLG_log.h
new file mode 100644 (file)
index 0000000..2da5a7d
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __CLOG_H__
+#define __CLOG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef __GNUC__
+#  define _CLOG_ATTR_NONNULL(args ...) __attribute__((nonnull(args)))
+#else
+#  define _CLOG_ATTR_NONNULL(...)
+#endif
+
+#ifdef __GNUC__
+#  define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param) __attribute__((format(printf, format_param, dots_param)))
+#else
+#  define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+#define STRINGIFY_ARG(x) "" #x
+#define STRINGIFY_APPEND(a, b) "" a #b
+#define STRINGIFY(x) STRINGIFY_APPEND("", x)
+
+struct CLogContext;
+
+/* Don't typedef enums. */
+enum CLG_LogFlag {
+       CLG_FLAG_USE = (1 << 0),
+};
+
+enum CLG_Severity {
+       CLG_SEVERITY_INFO = 0,
+       CLG_SEVERITY_WARN,
+       CLG_SEVERITY_ERROR,
+       CLG_SEVERITY_FATAL,
+};
+#define CLG_SEVERITY_LEN (CLG_SEVERITY_FATAL + 1)
+
+/* Each logger ID has one of these. */
+typedef struct CLG_LogType {
+       struct CLG_LogType *next;
+       char identifier[64];
+       /** FILE output. */
+       struct CLogContext *ctx;
+       /** Control behavior. */
+       int level;
+       enum CLG_LogFlag flag;
+} CLG_LogType;
+
+typedef struct CLG_LogRef {
+       const char *identifier;
+       CLG_LogType *type;
+} CLG_LogRef;
+
+void CLG_log_str(
+        CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+        const char *message)
+       _CLOG_ATTR_NONNULL(1, 3, 4, 5);
+void CLG_logf(
+        CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+        const char *format, ...)
+       _CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6);
+
+/* Main initializer and distructor (per session, not logger). */
+void CLG_init(void);
+void CLG_exit(void);
+
+void CLG_output_set(void *file_handle);
+void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
+
+void CLG_type_filter(const char *type_filter, int type_filter_len);
+
+void CLG_logref_init(CLG_LogRef *clg_ref);
+
+/** Declare outside function, declare as extern in header. */
+#define CLG_LOGREF_DECLARE_GLOBAL(var, id) \
+       static CLG_LogRef _static_ ## var = {id}; \
+       CLG_LogRef *var = &_static_ ## var
+
+/** Initialize struct once. */
+#define CLOG_ENSURE(clg_ref) \
+       ((clg_ref)->type ? (clg_ref)->type : (CLG_logref_init(clg_ref), (clg_ref)->type))
+
+#define CLOG_AT_SEVERITY(clg_ref, severity, verbose_level, ...) { \
+       CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+       if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+               CLG_logf(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
+       } \
+} ((void)0)
+
+#define CLOG_STR_AT_SEVERITY(clg_ref, severity, verbose_level, str) { \
+       CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+       if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+               CLG_log_str(lg, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
+       } \
+} ((void)0)
+
+#define CLOG_STR_AT_SEVERITY_N(clg_ref, severity, verbose_level, str) { \
+       CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+       if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+               const char *_str = str; \
+               CLG_log_str(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, _str); \
+               MEM_freeN((void *)_str); \
+       } \
+} ((void)0)
+
+#define CLOG_INFO(clg_ref, level, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_WARN(clg_ref, ...)        CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_ERROR(clg_ref, ...)       CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_FATAL(clg_ref, ...)       CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+#define CLOG_STR_INFO(clg_ref, level, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_STR_WARN(clg_ref, ...)        CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_STR_ERROR(clg_ref, ...)       CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_STR_FATAL(clg_ref, ...)       CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+/* Allocated string which is immediately freed. */
+#define CLOG_STR_INFO_N(clg_ref, level, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_STR_WARN_N(clg_ref, ...)        CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_STR_ERROR_N(clg_ref, ...)       CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_STR_FATAL_N(clg_ref, ...)       CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CLOG_H__ */
diff --git a/intern/clog/CMakeLists.txt b/intern/clog/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8bf7b66
--- /dev/null
@@ -0,0 +1,34 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+       .
+       ../guardedalloc
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+       clog.c
+
+       CLG_log.h
+)
+
+blender_add_lib(bf_intern_clog "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/intern/clog/clog.c b/intern/clog/clog.c
new file mode 100644 (file)
index 0000000..66267dd
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <assert.h>
+
+/* For 'isatty' to check for color. */
+#if defined(__unix__)
+#  include <unistd.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+/* own include. */
+#include "CLG_log.h"
+
+/* Local utility defines */
+#define STREQ(a, b) (strcmp(a, b) == 0)
+#define STREQLEN(a, b, n) (strncmp(a, b, n) == 0)
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Types
+ * \{ */
+
+typedef struct CLG_IDFilter {
+       struct CLG_IDFilter *next;
+       /** Over alloc. */
+       char match[0];
+} CLG_IDFilter;
+
+typedef struct CLogContext {
+       /** Single linked list of types.  */
+       CLG_LogType *types;
+       CLG_IDFilter *filters;
+       bool use_color;
+
+       /** Borrowed, not owned. */
+       FILE *output;
+
+       /** For new types. */
+       struct {
+               int level;
+       } default_type;
+
+       struct {
+               void (*fatal_fn)(void *file_handle);
+       } callbacks;
+} CLogContext;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mini Buffer Functionality
+ *
+ * Use so we can do a single call to write.
+ * \{ */
+
+#define CLOG_BUF_LEN_INIT 512
+
+typedef struct CLogStringBuf {
+       char *data;
+       uint  len;
+       uint  len_alloc;
+       bool is_alloc;
+} CLogStringBuf;
+
+static void clg_str_init(CLogStringBuf *cstr, char *buf_stack, uint buf_stack_len)
+{
+       cstr->data = buf_stack;
+       cstr->len_alloc = buf_stack_len;
+       cstr->len = 0;
+       cstr->is_alloc = false;
+}
+
+static void clg_str_free(CLogStringBuf *cstr)
+{
+       if (cstr->is_alloc) {
+               MEM_freeN(cstr->data);
+       }
+}
+
+static void clg_str_reserve(CLogStringBuf *cstr, const uint len)
+{
+       if (len > cstr->len_alloc) {
+               if (cstr->is_alloc == false) {
+                       cstr->is_alloc = true;
+                       cstr->data = NULL;
+               }
+               cstr->len_alloc *= 2;
+               if (len > cstr->len_alloc) {
+                       cstr->len_alloc = len;
+               }
+               cstr->data = MEM_reallocN(cstr->data, len);
+               cstr->len_alloc = len;
+       }
+}
+
+static void clg_str_append_with_len(CLogStringBuf *cstr, const char *str, const uint len)
+{
+       uint len_next = cstr->len + len;
+       clg_str_reserve(cstr, len_next);
+       char *str_dst = cstr->data + cstr->len;
+       memcpy(str_dst, str, len);
+#if 0 /* no need. */
+       str_dst[len] = '\0';
+#endif
+       cstr->len = len_next;
+}
+
+static void clg_str_append(CLogStringBuf *cstr, const char *str)
+{
+       clg_str_append_with_len(cstr, str, strlen(str));
+}
+
+static void clg_str_vappendf(CLogStringBuf *cstr, const char *fmt, va_list args)
+{
+       /* Use limit because windows may use '-1' for a formatting error. */
+       const uint len_max = 65535;
+       uint len_avail = (cstr->len_alloc - cstr->len);
+       if (len_avail == 0) {
+               len_avail = CLOG_BUF_LEN_INIT;
+               clg_str_reserve(cstr, len_avail);
+       }
+       while (true) {
+               va_list args_cpy;
+               va_copy(args_cpy, args);
+               int retval = vsnprintf(cstr->data + cstr->len, len_avail, fmt, args_cpy);
+               va_end(args_cpy);
+               if (retval != -1) {
+                       break;
+               }
+               else {
+                       len_avail *= 2;
+                       if (len_avail >= len_max) {
+                               break;
+                       }
+                       clg_str_reserve(cstr, len_avail);
+               }
+       }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Utilities
+ * \{ */
+
+enum eCLogColor {
+       COLOR_DEFAULT,
+       COLOR_RED,
+       COLOR_GREEN,
+       COLOR_YELLOW,
+
+       COLOR_RESET,
+};
+#define COLOR_LEN (COLOR_RESET + 1)
+
+static const char *clg_color_table[COLOR_LEN] = {NULL};
+
+static void clg_color_table_init(bool use_color)
+{
+       for (int i = 0; i < COLOR_LEN; i++) {
+               clg_color_table[i] = "";
+       }
+       if (use_color) {
+#ifdef _WIN32
+               /* TODO */
+#else
+               clg_color_table[COLOR_DEFAULT]      = "\033[1;37m";
+               clg_color_table[COLOR_RED]          = "\033[1;31m";
+               clg_color_table[COLOR_GREEN]        = "\033[1;32m";
+               clg_color_table[COLOR_YELLOW]       = "\033[1;33m";
+               clg_color_table[COLOR_RESET]        = "\033[0m";
+#endif
+       }
+}
+
+static const char *clg_severity_str[CLG_SEVERITY_LEN] = {
+       [CLG_SEVERITY_INFO] =       "INFO",
+       [CLG_SEVERITY_WARN] =       "WARN",
+       [CLG_SEVERITY_ERROR] =      "ERROR",
+       [CLG_SEVERITY_FATAL] =      "FATAL",
+};
+
+static const char *clg_severity_as_text(enum CLG_Severity severity)
+{
+       bool ok = (unsigned int)severity < CLG_SEVERITY_LEN;
+       assert(ok);
+       if (ok) {
+               return clg_severity_str[severity];
+       }
+       else {
+               return "INVALID_SEVERITY";
+       }
+}
+
+static enum eCLogColor clg_severity_to_color(enum CLG_Severity severity)
+{
+       bool ok = (unsigned int)severity < CLG_SEVERITY_LEN;
+       assert(ok);
+       enum eCLogColor color = COLOR_DEFAULT;
+       switch (severity) {
+               case CLG_SEVERITY_INFO:
+                       color = COLOR_DEFAULT;
+                       break;
+               case CLG_SEVERITY_WARN:
+                       color = COLOR_YELLOW;
+                       break;
+               case CLG_SEVERITY_ERROR:
+               case CLG_SEVERITY_FATAL:
+                       color = COLOR_RED;
+                       break;
+               default:
+                       /* should never get here. */
+                       assert(false);
+       }
+       return color;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Context Type Access
+ * \{ */
+
+/**
+ * Filter the indentifier based on very basic globbing.
+ * - `foo` exact match of `foo`.
+ * - `foo.bar` exact match for `foo.bar`
+ * - `foo.*` match for `foo` & `foo.bar` & `foo.bar.baz`
+ * - `*` matches everything.
+ */
+static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
+{
+       const CLG_IDFilter *flt = ctx->filters;
+       const int identifier_len = strlen(identifier);
+       while (flt != NULL) {
+               const int len = strlen(flt->match);
+               if (STREQ(flt->match, "*") ||
+                   ((len == identifier_len) && (STREQ(identifier, flt->match))))
+               {
+                       return true;
+               }
+               if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) {
+                       if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) ||
+                           ((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1)))
+                       {
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+/**
+ * \note This should never be called per logging call.
+ * Searching is only to get an initial handle.
+ */
+static CLG_LogType *clg_ctx_type_find_by_name(CLogContext *ctx, const char *identifier)
+{
+       for (CLG_LogType *ty = ctx->types; ty; ty = ty->next) {
+               if (STREQ(identifier, ty->identifier)) {
+                       return ty;
+               }
+       }
+       return NULL;
+}
+
+static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifier)
+{
+       assert(clg_ctx_type_find_by_name(ctx, identifier) == NULL);
+       CLG_LogType *ty = MEM_callocN(sizeof(*ty), __func__);
+       ty->next = ctx->types;
+       ctx->types = ty;
+       strncpy(ty->identifier, identifier, sizeof(ty->identifier) - 1);
+       ty->ctx = ctx;
+       ty->level = ctx->default_type.level;
+
+       if (clg_ctx_filter_check(ctx, ty->identifier)) {
+               ty->flag |= CLG_FLAG_USE;
+       }
+       return ty;
+}
+
+static void clg_ctx_fatal_action(CLogContext *ctx, FILE *file_handle)
+{
+       if (ctx->callbacks.fatal_fn != NULL) {
+               ctx->callbacks.fatal_fn(file_handle);
+       }
+       fflush(file_handle);
+       abort();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging API
+ * \{ */
+
+static void write_severity(CLogStringBuf *cstr, enum CLG_Severity severity, bool use_color)
+{
+       assert((unsigned int)severity < CLG_SEVERITY_LEN);
+       if (use_color) {
+               enum eCLogColor color = clg_severity_to_color(severity);
+               clg_str_append(cstr, clg_color_table[color]);
+               clg_str_append(cstr, clg_severity_as_text(severity));
+               clg_str_append(cstr, clg_color_table[COLOR_RESET]);
+       }
+       else {
+               clg_str_append(cstr, clg_severity_as_text(severity));
+       }
+}
+
+static void write_type(CLogStringBuf *cstr, CLG_LogType *lg)
+{
+       clg_str_append(cstr, " (");
+       clg_str_append(cstr, lg->identifier);
+       clg_str_append(cstr, "): ");
+}
+
+static void write_file_line_fn(CLogStringBuf *cstr, const char *file_line, const char *fn)
+{
+       clg_str_append(cstr, file_line);
+       clg_str_append(cstr, " ");
+       clg_str_append(cstr, fn);
+       clg_str_append(cstr, ": ");
+}
+
+void CLG_log_str(
+        CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+        const char *message)
+{
+       CLogStringBuf cstr;
+       char cstr_stack_buf[CLOG_BUF_LEN_INIT];
+       clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
+
+       write_severity(&cstr, severity, lg->ctx->use_color);
+       write_type(&cstr, lg);
+
+       {
+               write_file_line_fn(&cstr, file_line, fn);
+               clg_str_append(&cstr, message);
+       }
+       clg_str_append(&cstr, "\n");
+
+       /* could be optional */
+       fwrite(cstr.data, cstr.len, 1, lg->ctx->output);
+       fflush(lg->ctx->output);
+
+       clg_str_free(&cstr);
+
+       if (severity == CLG_SEVERITY_FATAL) {
+               clg_ctx_fatal_action(lg->ctx, lg->ctx->output);
+       }
+}
+
+void CLG_logf(
+        CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+        const char *fmt, ...)
+{
+       CLogStringBuf cstr;
+       char cstr_stack_buf[CLOG_BUF_LEN_INIT];
+       clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
+
+       // FILE *fh = lg->ctx->output;
+       write_severity(&cstr, severity, lg->ctx->use_color);
+       write_type(&cstr, lg);
+
+       {
+               write_file_line_fn(&cstr, file_line, fn);
+
+               va_list ap;
+               va_start(ap, fmt);
+               clg_str_vappendf(&cstr, fmt, ap);
+               va_end(ap);
+       }
+       clg_str_append(&cstr, "\n");
+
+       /* could be optional */
+       fwrite(cstr.data, cstr.len, 1, lg->ctx->output);
+       fflush(lg->ctx->output);
+
+       if (severity == CLG_SEVERITY_FATAL) {
+               clg_ctx_fatal_action(lg->ctx, lg->ctx->output);
+       }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging Context API
+ * \{ */
+
+static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
+{
+       ctx->output = file_handle;
+#if defined(__unix__)
+       ctx->use_color = isatty(fileno(file_handle));
+#endif
+}
+
+/** Action on fatal severity. */
+static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
+{
+       ctx->callbacks.fatal_fn = fatal_fn;
+}
+
+static void CLG_ctx_type_filter(CLogContext *ctx, const char *type_match, int type_match_len)
+{
+       CLG_IDFilter *flt = MEM_callocN(sizeof(*flt) + (type_match_len + 1), __func__);
+       flt->next = ctx->filters;
+       ctx->filters = flt;
+       memcpy(flt->match, type_match, type_match_len);
+       /* no need to null terminate since we calloc'd */
+}
+
+static CLogContext *CLG_ctx_init(void)
+{
+       CLogContext *ctx = MEM_callocN(sizeof(*ctx), __func__);
+       ctx->use_color = true;
+       ctx->default_type.level = 1;
+       CLG_ctx_output_set(ctx, stdout);
+
+       return ctx;
+}
+
+static void CLG_ctx_free(CLogContext *ctx)
+{
+       while (ctx->types != NULL) {
+               CLG_LogType *item = ctx->types;
+               ctx->types = item->next;
+               MEM_freeN(item);
+       }
+       while (ctx->filters != NULL) {
+               CLG_IDFilter *item = ctx->filters;
+               ctx->filters = item->next;
+               MEM_freeN(item);
+       }
+       MEM_freeN(ctx);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Logging API
+ *
+ * Currently uses global context.
+ * \{ */
+
+/* We could support multiple at once, for now this seems not needed. */
+struct CLogContext *g_ctx = NULL;
+
+void CLG_init(void)
+{
+       g_ctx = CLG_ctx_init();
+
+       clg_color_table_init(g_ctx->use_color);
+}
+
+void CLG_exit(void)
+{
+       CLG_ctx_free(g_ctx);
+}
+
+void CLG_output_set(void *file_handle)
+{
+       CLG_ctx_output_set(g_ctx, file_handle);
+}
+
+void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
+{
+       CLG_ctx_fatal_fn_set(g_ctx, fatal_fn);
+}
+
+void CLG_type_filter(const char *type_match, int type_match_len)
+{
+       CLG_ctx_type_filter(g_ctx, type_match, type_match_len);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging Reference API
+ * Use to avoid lookups each time.
+ * \{ */
+
+void CLG_logref_init(CLG_LogRef *clg_ref)
+{
+       assert(clg_ref->type == NULL);
+       CLG_LogType *clg_ty = clg_ctx_type_find_by_name(g_ctx, clg_ref->identifier);
+       clg_ref->type = clg_ty ? clg_ty : clg_ctx_type_register(g_ctx, clg_ref->identifier);
+}
+
+/** \} */
index a618ef7fd4e2af8a6d32f9d2b38bec28f19a074f..fbda0df18a630844fdb419e19d687cc06a063b9a 100644 (file)
@@ -2668,8 +2668,8 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
         layout.operator("object.vertex_parent_set")
 
 
-class VIEW3D_MT_edit_mesh_edges_data(Menu):
-    bl_label = "Edge Data"
+class VIEW3D_MT_edit_mesh_edges(Menu):
+    bl_label = "Edges"
 
     def draw(self, context):
         layout = self.layout
@@ -2678,6 +2678,13 @@ class VIEW3D_MT_edit_mesh_edges_data(Menu):
 
         layout.operator_context = 'INVOKE_REGION_WIN'
 
+        layout.operator("mesh.edge_face_add")
+        layout.operator("mesh.subdivide")
+        layout.operator("mesh.subdivide_edgering")
+        layout.operator("mesh.unsubdivide")
+
+        layout.separator()
+
         layout.operator("transform.edge_crease")
         layout.operator("transform.edge_bevelweight")
 
@@ -2698,26 +2705,6 @@ class VIEW3D_MT_edit_mesh_edges_data(Menu):
             layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
             layout.separator()
 
-
-class VIEW3D_MT_edit_mesh_edges(Menu):
-    bl_label = "Edges"
-
-    def draw(self, context):
-        layout = self.layout
-
-        layout.operator_context = 'INVOKE_REGION_WIN'
-
-        layout.operator("mesh.edge_face_add")
-        layout.operator("mesh.subdivide")
-        layout.operator("mesh.subdivide_edgering")
-        layout.operator("mesh.unsubdivide")
-
-        layout.separator()
-
-        layout.menu("VIEW3D_MT_edit_mesh_edges_data")
-
-        layout.separator()
-
         layout.operator("mesh.edge_rotate", text="Rotate Edge CW").use_ccw = False
         layout.operator("mesh.edge_rotate", text="Rotate Edge CCW").use_ccw = True
 
@@ -4124,7 +4111,6 @@ classes = (
     VIEW3D_MT_edit_mesh_extrude,
     VIEW3D_MT_edit_mesh_vertices,
     VIEW3D_MT_edit_mesh_edges,
-    VIEW3D_MT_edit_mesh_edges_data,
     VIEW3D_MT_edit_mesh_faces,
     VIEW3D_MT_edit_mesh_normals,
     VIEW3D_MT_edit_mesh_clean,
index 832b41646131e91eb8f2c63da1263bd368d47493..ce8de456697403fb9c9dd133ec6b29ab46e9fb4b 100644 (file)
@@ -80,6 +80,13 @@ typedef struct Global {
         * however this is now only used for runtime options */
        int f;
 
+       struct {
+               /* Logging vars (different loggers may use). */
+               int level;
+               /* FILE handle or use stderr (we own this so close when done). */
+               void *file;
+       } log;
+
        /* debug flag, G_DEBUG, G_DEBUG_PYTHON & friends, set python or command line args */
        int debug;
 
index 231810aee313f991517a2fc1a4db23cb38872b25..25954f277b8aedc40ca870cc4cd5cf92cc504cd0 100644 (file)
@@ -48,6 +48,7 @@ set(INC
        ../../../intern/mikktspace
        ../../../intern/smoke/extern
        ../../../intern/atomic
+       ../../../intern/clog
        ../../../intern/libmv
        ../../../extern/curve_fit_nd
 )
index a27f075a3465d9905c0ff8e56c34a2ca2233be8e..55b2d5b9c0de4b775725e14c0e58acfe7326a03d 100644 (file)
@@ -83,6 +83,10 @@ void BKE_blender_free(void)
        BKE_main_free(G.main);
        G.main = NULL;
 
+       if (G.log.file != NULL) {
+               fclose(G.log.file);
+       }
+
        BKE_spacetypes_free();      /* after free main, it uses space callbacks */
        
        IMB_exit();
@@ -130,6 +134,8 @@ void BKE_blender_globals_init(void)
 #else
        G.f &= ~G_SCRIPT_AUTOEXEC;
 #endif
+
+       G.log.level = 1;
 }
 
 void BKE_blender_globals_clear(void)
index fc71b5ccb7baba8e5bf5bbd24e0e0ab39ea84719..92518ba73e421892c338e8277f35b7a17e142065 100644 (file)
@@ -252,9 +252,6 @@ void deg_evaluate_on_refresh(EvaluationContext *eval_ctx,
                              Depsgraph *graph,
                              const unsigned int layers)
 {
-       /* Set time for the current graph evaluation context. */
-       TimeSourceDepsNode *time_src = graph->find_time_source();
-       eval_ctx->ctime = time_src->cfra;
        /* Nothing to update, early out. */
        if (BLI_gset_len(graph->entry_tags) == 0) {
                return;
@@ -265,6 +262,9 @@ void deg_evaluate_on_refresh(EvaluationContext *eval_ctx,
                         graph->layers);
        const bool do_time_debug = ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0);
        const double start_time = do_time_debug ? PIL_check_seconds_timer() : 0;
+       /* Set time for the current graph evaluation context. */
+       TimeSourceDepsNode *time_src = graph->find_time_source();
+       eval_ctx->ctime = time_src->cfra;
        /* Set up evaluation context for depsgraph itself. */
        DepsgraphEvalState state;
        state.eval_ctx = eval_ctx;
index c0b30f93939b410c40b52053e357de2e83f727f9..a8225bb64d118970d4631d83def004f805452fb3 100644 (file)
@@ -30,6 +30,7 @@ set(INC
        ../../makesrna
        ../../windowmanager
        ../../../../intern/guardedalloc
+       ../../../../intern/clog
        ../../../../intern/glew-mx
 )
 
index 026384743bd7fe3650121da483e355db41f51f5e..9492b6d67f3dcd4bb15bffa1a929d7c89088bba9 100644 (file)
@@ -689,8 +689,6 @@ PyDoc_STRVAR(euler_doc,
 "\n"
 "   This object gives access to Eulers in Blender.\n"
 "\n"
-"   .. seealso:: `Euler angles <https://en.wikipedia.org/wiki/Euler_angles>`__ on Wikipedia.\n"
-"\n"
 "   :param angles: Three angles, in radians.\n"
 "   :type angles: 3d vector\n"
 "   :param order: Optional order of the angles, a permutation of ``XYZ``.\n"
index c9278822b9a925ec97a73049ab9ac1500cd36d7b..50f99251489712e0ac3b99fbdf03fa5ddea309d4 100644 (file)
@@ -39,6 +39,7 @@ set(INC
        ../nodes
        ../render/extern/include
        ../../gameengine/BlenderRoutines
+       ../../../intern/clog
        ../../../intern/ghost
        ../../../intern/guardedalloc
        ../../../intern/glew-mx
index 8fca0ce959e77d328afa2c25c6bc9109b0abb3ad..8c94c2ff043180e2b5bb7d04abfdb7bb7899b348 100644 (file)
@@ -719,6 +719,14 @@ typedef struct RecentFile {
        char *filepath;
 } RecentFile;
 
+/* Logging */
+struct CLG_LogRef;
+/* wm_init_exit.c */
+extern struct CLG_LogRef *WM_LOG_OPERATORS;
+extern struct CLG_LogRef *WM_LOG_HANDLERS;
+extern struct CLG_LogRef *WM_LOG_EVENTS;
+extern struct CLG_LogRef *WM_LOG_KEYMAPS;
+
 
 #ifdef __cplusplus
 }
index b18e9f050c297480e0c97156115ce78767b5932e..b2df53321c01e19e4a689382ff5f972e24088259 100644 (file)
@@ -43,6 +43,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "GHOST_C-api.h"
 
 #include "BLI_blenlib.h"
@@ -353,13 +355,11 @@ void wm_event_do_notifiers(bContext *C)
 
 
                                                ED_screen_set(C, note->reference);  // XXX hrms, think this over!
-                                               if (G.debug & G_DEBUG_EVENTS)
-                                                       printf("%s: screen set %p\n", __func__, note->reference);
+                                               CLOG_INFO(WM_LOG_EVENTS, 1, "screen set %p", note->reference);
                                        }
                                        else if (note->data == ND_SCREENDELETE) {
                                                ED_screen_delete(C, note->reference);   // XXX hrms, think this over!
-                                               if (G.debug & G_DEBUG_EVENTS)
-                                                       printf("%s: screen delete %p\n", __func__, note->reference);
+                                               CLOG_INFO(WM_LOG_EVENTS, 1, "screen delete %p", note->reference);
                                        }
                                }
                        }
@@ -545,14 +545,6 @@ int WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
        return wm_operator_call_internal(C, ot, NULL, NULL, context, true);
 }
 
-static void wm_operator_print(bContext *C, wmOperator *op)
-{
-       /* context is needed for enum function */
-       char *buf = WM_operator_pystring(C, op, false, true);
-       puts(buf);
-       MEM_freeN(buf);
-}
-
 /**
  * Sets the active region for this space from the context.
  *
@@ -711,12 +703,9 @@ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool ca
                        CTX_wm_region_set(C, ar_prev);
                }
        }
-       
+
        if (retval & OPERATOR_FINISHED) {
-               if (G.debug & G_DEBUG_WM) {
-                       /* todo - this print may double up, might want to check more flags then the FINISHED */
-                       wm_operator_print(C, op);
-               }
+               CLOG_STR_INFO_N(WM_LOG_OPERATORS, 1, WM_operator_pystring(C, op, false, true));
 
                if (caller_owns_reports == false) {
                        BKE_reports_print(op->reports, RPT_DEBUG); /* print out reports to console. */
@@ -1041,9 +1030,7 @@ bool WM_operator_last_properties_init(wmOperator *op)
                IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
                PropertyRNA *iterprop;
 
-               if (G.debug & G_DEBUG_WM) {
-                       printf("%s: loading previous properties for '%s'\n", __func__, op->type->idname);
-               }
+               CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname);
 
                iterprop = RNA_struct_iterator_property(op->type->srna);
 
@@ -1088,9 +1075,7 @@ bool WM_operator_last_properties_store(wmOperator *op)
        }
 
        if (op->properties) {
-               if (G.debug & G_DEBUG_WM) {
-                       printf("%s: storing properties for '%s'\n", __func__, op->type->idname);
-               }
+               CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname);
                op->type->last_properties = IDP_CopyProperty(op->properties);
                return true;
        }
@@ -1140,11 +1125,12 @@ static int wm_operator_invoke(
                        WM_operator_last_properties_init(op);
                }
 
-               if ((G.debug & G_DEBUG_HANDLERS) && ((event == NULL) || (event->type != MOUSEMOVE))) {
-                       printf("%s: handle evt %d win %d op %s\n",
-                              __func__, event ? event->type : 0, CTX_wm_screen(C)->subwinactive, ot->idname);
+               if ((event == NULL) || (event->type != MOUSEMOVE)) {
+                       CLOG_INFO(WM_LOG_HANDLERS, 2,
+                                 "handle evt %d win %d op %s",
+                                 event ? event->type : 0, CTX_wm_screen(C)->subwinactive, ot->idname);
                }
-               
+
                if (op->type->invoke && event) {
                        wm_region_mouse_co(C, event);
 
@@ -1169,9 +1155,9 @@ static int wm_operator_invoke(
                }
                else {
                        /* debug, important to leave a while, should never happen */
-                       printf("%s: invalid operator call '%s'\n", __func__, ot->idname);
+                       CLOG_ERROR(WM_LOG_OPERATORS, "invalid operator call '%s'", op->idname);
                }
-               
+
                /* Note, if the report is given as an argument then assume the caller will deal with displaying them
                 * currently python only uses this */
                if (!(retval & OPERATOR_HANDLED) && (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED))) {
@@ -1446,8 +1432,10 @@ int WM_operator_call_py(
                if (is_undo && op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
                        wm->op_undo_depth--;
        }
-       else
-               printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
+       else {
+               CLOG_WARN(WM_LOG_OPERATORS, "\"%s\" operator has no exec function, Python cannot call it", op->type->name);
+       }
+
 #endif
 
        /* not especially nice using undo depth here, its used so py never
@@ -1491,8 +1479,9 @@ static void wm_handler_op_context(bContext *C, wmEventHandler *handler, const wm
                        if (sa == NULL) {
                                /* when changing screen layouts with running modal handlers (like render display), this
                                 * is not an error to print */
-                               if (handler->op == NULL)
-                                       printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
+                               if (handler->op == NULL) {
+                                       CLOG_ERROR(WM_LOG_HANDLERS, "internal error: handler (%s) has invalid area", handler->op->type->idname);
+                               }
                        }
                        else {
                                ARegion *ar;
@@ -1805,10 +1794,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
                                        //retval &= ~OPERATOR_PASS_THROUGH;
                                }
                        }
-                       
                }
                else {
-                       printf("%s: error '%s' missing modal\n", __func__, op->idname);
+                       CLOG_ERROR(WM_LOG_HANDLERS, "missing modal '%s'", op->idname);
                }
        }
        else {
@@ -2099,19 +2087,15 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
                                                        action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
                                                        if (action & WM_HANDLER_BREAK) {
                                                                /* not always_pass here, it denotes removed handler */
-                                                               
-                                                               if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
-                                                                       printf("%s:       handled! '%s'\n", __func__, kmi->idname);
-
+                                                               CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
                                                                break;
                                                        }
                                                        else {
                                                                if (action & WM_HANDLER_HANDLED) {
-                                                                       if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
-                                                                               printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
+                                                                       CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname);
                                                                }
                                                                else {
-                                                                       PRINT("%s:       un-handled '%s'\n", __func__, kmi->idname);
+                                                                       CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname);
                                                                }
                                                        }
                                                }
@@ -2237,10 +2221,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
                                        {
                                                event->val = KM_CLICK;
 
-                                               if (G.debug & (G_DEBUG_HANDLERS)) {
-                                                       printf("%s: handling CLICK\n", __func__);
-                                               }
-
+                                               CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK");
+       
                                                action |= wm_handlers_do_intern(C, event, handlers);
 
                                                event->val = KM_RELEASE;
@@ -2461,13 +2443,14 @@ void wm_event_do_handlers(bContext *C)
 
                        if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
                                printf("\n%s: Handling event\n", __func__);
+
                                WM_event_print(event);
                        }
 
                        /* take care of pie event filter */
                        if (wm_event_pie_filter(win, event)) {
-                               if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
-                                       printf("\n%s: event filtered due to pie button pressed\n", __func__);
+                               if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+                                       CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
                                }
                                BLI_remlink(&win->queue, event);
                                wm_event_free(event);
@@ -2751,7 +2734,7 @@ wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap
        wmEventHandler *handler;
 
        if (!keymap) {
-               printf("%s: called with NULL keymap\n", __func__);
+               CLOG_WARN(WM_LOG_HANDLERS, "called with NULL keymap");
                return NULL;
        }
 
@@ -3372,8 +3355,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                        
                        /* double click test */
                        if (wm_event_is_double_click(&event, evt)) {
-                               if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
-                                       printf("%s Send double click\n", __func__);
+                               CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
                                event.val = KM_DBL_CLICK;
                        }
                        if (event.val == KM_PRESS) {
@@ -3427,7 +3409,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 
                                /* ghost should do this already for key up */
                                if (event.utf8_buf[0]) {
-                                       printf("%s: ghost on your platform is misbehaving, utf8 events on key up!\n", __func__);
+                                       CLOG_ERROR(WM_LOG_EVENTS, "ghost on your platform is misbehaving, utf8 events on key up!");
                                }
                                event.utf8_buf[0] = '\0';
                        }
@@ -3440,8 +3422,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 
                        if (event.utf8_buf[0]) {
                                if (BLI_str_utf8_size(event.utf8_buf) == -1) {
-                                       printf("%s: ghost detected an invalid unicode character '%d'!\n",
-                                              __func__, (int)(unsigned char)event.utf8_buf[0]);
+                                       CLOG_ERROR(WM_LOG_EVENTS,
+                                                  "ghost detected an invalid unicode character '%d'",
+                                                  (int)(unsigned char)event.utf8_buf[0]);
                                        event.utf8_buf[0] = '\0';
                                }
                        }
@@ -3490,8 +3473,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                        /* double click test */
                        /* if previous event was same type, and previous was release, and now it presses... */
                        if (wm_event_is_double_click(&event, evt)) {
-                               if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
-                                       printf("%s Send double click\n", __func__);
+                               CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
                                evt->val = event.val = KM_DBL_CLICK;
                        }
                        
@@ -3561,9 +3543,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                        attach_ndof_data(&event, customdata);
                        wm_event_add(win, &event);
 
-                       if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
-                               printf("%s sending NDOF_MOTION, prev = %d %d\n", __func__, event.x, event.y);
-
+                       CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.x, event.y);
                        break;
                }
 
index 9b4868523dc35dc0ae4f5fd75426016bc41904ad..2743216ee07cfc33a50a94335db5258a1d29c39c 100644 (file)
@@ -40,6 +40,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "DNA_genfile.h"
 #include "DNA_scene_types.h"
 #include "DNA_userdef_types.h"
 #  include "BKE_subsurf.h"
 #endif
 
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_OPERATORS, "wm.operator");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_HANDLERS, "wm.handler");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_EVENTS, "wm.event");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_KEYMAPS, "wm.keymap");
+
 static void wm_init_reports(bContext *C)
 {
        ReportList *reports = CTX_wm_reports(C);
@@ -613,6 +620,8 @@ void WM_exit_ext(bContext *C, const bool do_python)
         * see also T50676. */
        BKE_sound_exit();
 
+       CLG_exit();
+
        BKE_blender_atexit();
 
        if (MEM_get_memory_blocks_in_use() != 0) {
index 45ed44d83d6b88ae7f328f05827eb05e864b8b43..bcfc97a1e235f987bc6e78b4f6d2444be57ab227 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * 
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
@@ -39,6 +40,7 @@
 #include "DNA_windowmanager_types.h"
 
 #include "MEM_guardedalloc.h"
+#include "CLG_log.h"
 
 #include "BLI_blenlib.h"
 #include "BLI_utildefines.h"
@@ -886,11 +888,13 @@ wmKeyMapItem *WM_modalkeymap_find_propvalue(wmKeyMap *km, const int propvalue)
 void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
 {
        wmOperatorType *ot = WM_operatortype_find(opname, 0);
-       
-       if (ot)
+
+       if (ot) {
                ot->modalkeymap = km;
-       else
-               printf("error: modalkeymap_assign, unknown operator %s\n", opname);
+       }
+       else {
+               CLOG_ERROR(WM_LOG_KEYMAPS, "unknown operator '%s'", opname);
+       }
 }
 
 static void wm_user_modal_keymap_set_items(wmWindowManager *wm, wmKeyMap *km)
index 814d88238170c257f0905def76c2187f21aaeedb..b05b25967190d9285e8c693ec67b52c1c61d0f31 100644 (file)
@@ -46,6 +46,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "DNA_ID.h"
 #include "DNA_object_types.h"
 #include "DNA_screen_types.h"
@@ -139,12 +141,12 @@ wmOperatorType *WM_operatortype_find(const char *idname, bool quiet)
                }
 
                if (!quiet) {
-                       printf("search for unknown operator '%s', '%s'\n", idname_bl, idname);
+                       CLOG_INFO(WM_LOG_OPERATORS, 0, "search for unknown operator '%s', '%s'\n", idname_bl, idname);
                }
        }
        else {
                if (!quiet) {
-                       printf("search for empty operator\n");
+                       CLOG_INFO(WM_LOG_OPERATORS, 0, "search for empty operator");
                }
        }
 
@@ -170,8 +172,7 @@ void WM_operatortype_append(void (*opfunc)(wmOperatorType *))
        opfunc(ot);
 
        if (ot->name == NULL) {
-               fprintf(stderr, "ERROR: Operator %s has no name property!\n", ot->idname);
-               ot->name = N_("Dummy Name");
+               CLOG_ERROR(WM_LOG_OPERATORS, "Operator '%s' has no name property", ot->idname);
        }
 
        /* XXX All ops should have a description but for now allow them not to. */
@@ -255,7 +256,7 @@ static int wm_macro_exec(bContext *C, wmOperator *op)
                        }
                }
                else {
-                       printf("%s: '%s' cant exec macro\n", __func__, opm->type->idname);
+                       CLOG_WARN(WM_LOG_OPERATORS, "'%s' cant exec macro", opm->type->idname);
                }
        }
        
@@ -300,8 +301,9 @@ static int wm_macro_modal(bContext *C, wmOperator *op, const wmEvent *event)
        wmOperator *opm = op->opm;
        int retval = OPERATOR_FINISHED;
        
-       if (opm == NULL)
-               printf("%s: macro error, calling NULL modal()\n", __func__);
+       if (opm == NULL) {
+               CLOG_ERROR(WM_LOG_OPERATORS, "macro error, calling NULL modal()");
+       }
        else {
                retval = opm->type->modal(C, opm, event);
                OPERATOR_RETVAL_CHECK(retval);
@@ -375,7 +377,7 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam
        const char *i18n_context;
        
        if (WM_operatortype_find(idname, true)) {
-               printf("%s: macro error: operator %s exists\n", __func__, idname);
+               CLOG_ERROR(WM_LOG_OPERATORS, "operator %s exists, cannot create macro", idname);
                return NULL;
        }
        
@@ -1135,11 +1137,14 @@ int WM_menu_invoke_ex(bContext *C, wmOperator *op, int opcontext)
        uiLayout *layout;
 
        if (prop == NULL) {
-               printf("%s: %s has no enum property set\n", __func__, op->type->idname);
+               CLOG_ERROR(WM_LOG_OPERATORS,
+                          "'%s' has no enum property set",
+                          op->type->idname);
        }
        else if (RNA_property_type(prop) != PROP_ENUM) {
-               printf("%s: %s \"%s\" is not an enum property\n",
-                      __func__, op->type->idname, RNA_property_identifier(prop));
+               CLOG_ERROR(WM_LOG_OPERATORS,
+                          "'%s', '%s' is not an enum property",
+                          op->type->idname, RNA_property_identifier(prop));
        }
        else if (RNA_property_is_set(op->ptr, prop)) {
                const int retval = op->type->exec(C, op);
@@ -1827,8 +1832,9 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
                                        memcpy(ibuf->rect, ibuf_template->rect, ibuf_template->x * ibuf_template->y * sizeof(char[4]));
                                }
                                else {
-                                       printf("Splash expected %dx%d found %dx%d, ignoring: %s\n",
-                                              x_expect, y_expect, ibuf_template->x, ibuf_template->y, splash_filepath);
+                                       CLOG_ERROR(WM_LOG_OPERATORS,
+                                                  "Splash expected %dx%d found %dx%d, ignoring: %s\n",
+                                                  x_expect, y_expect, ibuf_template->x, ibuf_template->y, splash_filepath);
                                }
                                IMB_freeImBuf(ibuf_template);
                        }
index a155f060335b341921005f10483da6080384759c..a71c1bb198465a459d00c2fb655062fdd421f660 100644 (file)
@@ -26,6 +26,7 @@
 setup_libdirs()
 
 blender_include_dirs(
+       ../../intern/clog
        ../../intern/guardedalloc
        ../../intern/glew-mx
        ../blender/blenlib
index a59a45f885c8685f5838c4d74f070f310f2011ef..962d6720760bb6484524247a02e1d6b2ed42fe6d 100644 (file)
@@ -42,6 +42,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "DNA_genfile.h"
 
 #include "BLI_args.h"
@@ -49,6 +51,7 @@
 #include "BLI_utildefines.h"
 #include "BLI_callbacks.h"
 #include "BLI_string.h"
+#include "BLI_system.h"
 
 /* mostly init functions */
 #include "BKE_appdir.h"
@@ -180,6 +183,11 @@ static void callback_main_atexit(void *user_data)
 #endif
 }
 
+static void callback_clg_fatal(void *fp)
+{
+       BLI_system_backtrace(fp);
+}
+
 /** \} */
 
 
@@ -304,6 +312,10 @@ int main(
        sdlewInit();
 #endif
 
+       /* Initialize logging */
+       CLG_init();
+       CLG_fatal_fn_set(callback_clg_fatal);
+
        C = CTX_create();
 
 #ifdef WITH_PYTHON_MODULE
index 25f8d732c5828ef1e62b176e534b2313d0162c11..17fa18916fdecacfb5e2454b7a8cbcb309e3ca8c 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #ifdef WIN32
 #  include "BLI_winstuff.h"
 #endif
@@ -529,6 +531,11 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
        BLI_argsPrintArgDoc(ba, "--python-exit-code");
        BLI_argsPrintArgDoc(ba, "--addons");
 
+       printf("\n");
+       printf("Logging Options:\n");
+       BLI_argsPrintArgDoc(ba, "--log");
+       BLI_argsPrintArgDoc(ba, "--log-level");
+       BLI_argsPrintArgDoc(ba, "--log-file");
 
        printf("\n");
        printf("Debug Options:\n");
@@ -704,6 +711,88 @@ static int arg_handle_background_mode_set(int UNUSED(argc), const char **UNUSED(
        return 0;
 }
 
+static const char arg_handle_log_level_set_doc[] =
+"\n\tSet the logging verbosity level (higher for more details) defaults to 1."
+;
+static int arg_handle_log_level_set(int argc, const char **argv, void *UNUSED(data))
+{
+       const char *arg_id = "--log-level";
+       if (argc > 1) {
+               const char *err_msg = NULL;
+               if (!parse_int_clamp(argv[1], NULL, 0, INT_MAX, &G.log.level, &err_msg)) {
+                       printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
+               }
+               return 1;
+       }
+       else {
+               printf("\nError: '%s' no args given.\n", arg_id);
+               return 0;
+       }
+}
+
+static const char arg_handle_log_file_set_doc[] =
+"\n\tSet a file to output the log to."
+;
+static int arg_handle_log_file_set(int argc, const char **argv, void *UNUSED(data))
+{
+       const char *arg_id = "--log-file";
+       if (argc > 1) {
+               errno = 0;
+               FILE *fp = BLI_fopen(argv[1], "w");
+               if (fp == NULL) {
+                       const char *err_msg = errno ? strerror(errno) : "unknown";
+                       printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
+               }
+               else {
+                       if (UNLIKELY(G.log.file != NULL)) {
+                               fclose(G.log.file);
+                       }
+                       G.log.file = fp;
+                       CLG_output_set(G.log.file);
+               }
+               return 1;
+       }
+       else {
+               printf("\nError: '%s' no args given.\n", arg_id);
+               return 0;
+       }
+}
+
+static const char arg_handle_log_set_doc[] =
+"\n\tEnable logging categories, taking a single comma separated argument.\n"
+"\tMultiple categories can be matched using a '.*' suffix, so '--log \"wm.*\"' logs every kind of window-manager message.\n"
+"\tUse \"*\" to log everything."
+;
+static int arg_handle_log_set(int argc, const char **argv, void *UNUSED(data))
+{
+       const char *arg_id = "--log";
+       if (argc > 1) {
+               const char *str_step = argv[1];
+               while (*str_step) {
+                       const char *str_step_end = strchr(str_step, ',');
+                       int str_step_len = str_step_end ? (str_step_end - str_step) : strlen(str_step);
+
+                       CLG_type_filter(str_step, str_step_len);
+
+                       if (str_step_end) {
+                               /* typically only be one, but don't fail on multiple.*/
+                               while (*str_step_end == ',') {
+                                       str_step_end++;
+                               }
+                               str_step = str_step_end;
+                       }
+                       else {
+                               break;
+                       }
+               }
+               return 1;
+       }
+       else {
+               printf("\nError: '%s' no args given.\n", arg_id);
+               return 0;
+       }
+}
+
 static const char arg_handle_debug_mode_set_doc[] =
 "\n"
 "\tTurn debugging on.\n"
@@ -1827,6 +1916,10 @@ void main_args_setup(bContext *C, bArgs *ba, SYS_SystemHandle *syshandle)
 
        BLI_argsAdd(ba, 1, "-a", NULL, CB(arg_handle_playback_mode), NULL);
 
+       BLI_argsAdd(ba, 1, NULL, "--log-level", CB(arg_handle_log_level_set), ba);
+       BLI_argsAdd(ba, 1, NULL, "--log-file", CB(arg_handle_log_file_set), ba);
+       BLI_argsAdd(ba, 1, NULL, "--log", CB(arg_handle_log_set), ba);
+
        BLI_argsAdd(ba, 1, "-d", "--debug", CB(arg_handle_debug_mode_set), ba);
 
 #ifdef WITH_FFMPEG