/work/svt-av1/Source/Lib/Codec/svt_log.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright(c) 2019 Intel Corporation |
3 | | * |
4 | | * This source code is subject to the terms of the BSD 2 Clause License and |
5 | | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
6 | | * was not distributed with this source code in the LICENSE file, you can |
7 | | * obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open |
8 | | * Media Patent License 1.0 was not distributed with this source code in the |
9 | | * PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license. |
10 | | */ |
11 | | //for getenv on windows |
12 | | #if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS) |
13 | | #define _CRT_SECURE_NO_WARNINGS |
14 | | #endif |
15 | | #include "svt_log.h" |
16 | | #include "svt_threads.h" |
17 | | #include <stdio.h> |
18 | | #include <stdlib.h> |
19 | | #include <stdarg.h> |
20 | | |
21 | | #if !CONFIG_LOG_QUIET |
22 | | |
23 | 11.8k | static const char* log_level_str(SvtAv1LogLevel level) { |
24 | 11.8k | switch (level) { |
25 | 0 | case SVT_AV1_LOG_FATAL: |
26 | 0 | return "fatal"; |
27 | 0 | case SVT_AV1_LOG_ERROR: |
28 | 0 | return "error"; |
29 | 1.89k | case SVT_AV1_LOG_WARN: |
30 | 1.89k | return "warn"; |
31 | 9.95k | case SVT_AV1_LOG_INFO: |
32 | 9.95k | return "info"; |
33 | 0 | case SVT_AV1_LOG_DEBUG: |
34 | 0 | return "debug"; |
35 | 0 | default: |
36 | 0 | return "unknown"; |
37 | 11.8k | } |
38 | 11.8k | } |
39 | | |
40 | | struct DefaultCtx { |
41 | | SvtAv1LogLevel level; |
42 | | FILE* file; |
43 | | }; |
44 | | |
45 | | /** |
46 | | * @brief Global logger structure |
47 | | * |
48 | | * Handles both default cases and custom loggers. |
49 | | */ |
50 | | static struct Logger { |
51 | | SvtAv1LogCallback fn; |
52 | | // If fn == default_logger, ctx is DefaultCtx* |
53 | | void* ctx; |
54 | | }* g_logger; |
55 | | |
56 | 11.8k | static void default_logger(void* context, SvtAv1LogLevel level, const char* tag, const char* fmt, va_list args) { |
57 | 11.8k | struct DefaultCtx* logger = context; |
58 | 11.8k | if (level > logger->level) { |
59 | 0 | return; |
60 | 0 | } |
61 | 11.8k | if (tag) { |
62 | 11.8k | fprintf(logger->file, "%s[%s]: ", tag, log_level_str(level)); |
63 | 11.8k | } |
64 | 11.8k | vfprintf(logger->file, fmt, args); |
65 | 11.8k | fflush(logger->file); |
66 | 11.8k | } |
67 | | |
68 | 1 | static void logger_cleanup(void) { |
69 | 1 | if (g_logger->fn == default_logger) { |
70 | 1 | struct DefaultCtx* ctx = g_logger->ctx; |
71 | 1 | if (ctx->file && ctx->file != stderr) { |
72 | 0 | fclose(ctx->file); |
73 | 0 | } |
74 | 1 | free(ctx); |
75 | 1 | } |
76 | 1 | free(g_logger); |
77 | 1 | g_logger = NULL; |
78 | 1 | } |
79 | | |
80 | | // file scoped variable just to handle the init for a custom logger. |
81 | | // It's only ever read once. |
82 | | // Any writes after being read are ignored. |
83 | | static struct Logger custom_logger_input; |
84 | | |
85 | 1 | static ONCE_ROUTINE(logger_create) { |
86 | 1 | g_logger = calloc(1, sizeof(*g_logger)); |
87 | 1 | if (!g_logger) { |
88 | | // Not sure what else to do here... |
89 | 0 | ONCE_ROUTINE_EPILOG; |
90 | 0 | } |
91 | 1 | struct Logger custom = custom_logger_input; |
92 | | |
93 | 1 | if (custom.fn && custom.fn != default_logger) { |
94 | | // A custom logger was requested before initialization |
95 | 0 | *g_logger = custom; |
96 | 0 | atexit(logger_cleanup); |
97 | 0 | ONCE_ROUTINE_EPILOG; |
98 | 0 | } |
99 | | |
100 | | // Default logger |
101 | 1 | struct DefaultCtx* ctx = calloc(1, sizeof(*ctx)); |
102 | 1 | if (!ctx) { |
103 | 0 | free(g_logger); |
104 | 0 | g_logger = NULL; |
105 | 0 | ONCE_ROUTINE_EPILOG; |
106 | 0 | } |
107 | | |
108 | 1 | const char* log = getenv("SVT_LOG"); |
109 | 1 | ctx->level = SVT_AV1_LOG_INFO; |
110 | 1 | if (log) { |
111 | 0 | const int new_level = atoi(log); |
112 | 0 | if (new_level >= SVT_AV1_LOG_ALL && new_level <= SVT_AV1_LOG_DEBUG) { |
113 | 0 | ctx->level = (SvtAv1LogLevel)new_level; |
114 | 0 | } |
115 | 0 | } |
116 | | |
117 | 1 | const char* file = getenv("SVT_LOG_FILE"); |
118 | 1 | if (file) { |
119 | 0 | FOPEN(ctx->file, file, "w+"); |
120 | 0 | } |
121 | | // If the file couldn't be opened, fall back to stderr |
122 | 1 | if (!ctx->file) { |
123 | 1 | ctx->file = stderr; |
124 | 1 | } |
125 | | |
126 | 1 | g_logger->fn = default_logger; |
127 | 1 | g_logger->ctx = ctx; |
128 | 1 | atexit(logger_cleanup); |
129 | 1 | ONCE_ROUTINE_EPILOG; |
130 | 1 | } |
131 | | |
132 | | DEFINE_ONCE(g_logger_once); |
133 | | |
134 | 11.8k | static struct Logger* get_logger() { |
135 | 11.8k | svt_run_once(&g_logger_once, logger_create); |
136 | 11.8k | return g_logger; |
137 | 11.8k | } |
138 | | |
139 | 0 | void svt_aom_log_set_callback(SvtAv1LogCallback callback, void* context) { |
140 | | // We only want to allow using a custom context if a custom callback is provided. |
141 | | // Otherwise the context for the default logger will be incorrect. |
142 | | // This will still allow passing NULL for context in case the callback doesn't use it. |
143 | | // Due to how the rest of the code is structured, this only takes effect once, |
144 | | // and only if done before any logging happens. |
145 | 0 | if (callback) { |
146 | 0 | custom_logger_input.fn = callback; |
147 | 0 | custom_logger_input.ctx = context; |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | 11.8k | void svt_aom_log(SvtAv1LogLevel level, const char* tag, const char* format, ...) { |
152 | 11.8k | struct Logger* logger = get_logger(); |
153 | 11.8k | if (!logger) { |
154 | 0 | return; |
155 | 0 | } |
156 | 11.8k | va_list args; |
157 | 11.8k | va_start(args, format); |
158 | 11.8k | logger->fn(logger->ctx, level, tag, format, args); |
159 | | va_end(args); |
160 | 11.8k | } |
161 | | |
162 | | #endif //CONFIG_LOG_QUIET |