src/core/log.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/core/log.h>
7
8#include <mgba/core/config.h>
9#include <mgba/core/thread.h>
10
11#define MAX_CATEGORY 64
12
13static struct mLogger* _defaultLogger = NULL;
14
15struct mLogger* mLogGetContext(void) {
16 struct mLogger* logger = NULL;
17#ifndef DISABLE_THREADING
18 logger = mCoreThreadLogger();
19#endif
20 if (logger) {
21 return logger;
22 }
23 return _defaultLogger;
24}
25
26void mLogSetDefaultLogger(struct mLogger* logger) {
27 _defaultLogger = logger;
28}
29
30static int _category = 0;
31static const char* _categoryNames[MAX_CATEGORY];
32static const char* _categoryIds[MAX_CATEGORY];
33
34int mLogGenerateCategory(const char* name, const char* id) {
35 if (_category < MAX_CATEGORY) {
36 _categoryNames[_category] = name;
37 _categoryIds[_category] = id;
38 }
39 ++_category;
40 return _category - 1;
41}
42
43const char* mLogCategoryName(int category) {
44 if (category < MAX_CATEGORY) {
45 return _categoryNames[category];
46 }
47 return NULL;
48}
49
50const char* mLogCategoryId(int category) {
51 if (category < MAX_CATEGORY) {
52 return _categoryIds[category];
53 }
54 return NULL;
55}
56
57int mLogCategoryById(const char* id) {
58 int i;
59 for (i = 0; i < _category; ++i) {
60 if (strcmp(_categoryIds[i], id) == 0) {
61 return i;
62 }
63 }
64 return -1;
65}
66
67void mLog(int category, enum mLogLevel level, const char* format, ...) {
68 struct mLogger* context = mLogGetContext();
69 va_list args;
70 va_start(args, format);
71 if (context) {
72 if (!context->filter || mLogFilterTest(context->filter, category, level)) {
73 context->log(context, category, level, format, args);
74 }
75 } else {
76 printf("%s: ", mLogCategoryName(category));
77 vprintf(format, args);
78 printf("\n");
79 }
80 va_end(args);
81}
82
83void mLogFilterInit(struct mLogFilter* filter) {
84 HashTableInit(&filter->categories, 8, NULL);
85 TableInit(&filter->levels, 8, NULL);
86}
87
88void mLogFilterDeinit(struct mLogFilter* filter) {
89 HashTableDeinit(&filter->categories);
90 TableDeinit(&filter->levels);
91}
92
93static void _setFilterLevel(const char* key, const char* value, enum mCoreConfigLevel level, void* user) {
94 UNUSED(level);
95 struct mLogFilter* filter = user;
96 key = strchr(key, '.');
97 if (!key || !key[1]) {
98 return;
99 }
100 if (!value) {
101 return;
102 }
103 ++key;
104 char* end;
105 int ivalue = strtol(value, &end, 10);
106 if (ivalue == 0) {
107 ivalue = 0x80; // Zero is reserved
108 }
109 if (!end) {
110 return;
111 }
112 mLogFilterSet(filter, key, ivalue);
113}
114
115void mLogFilterLoad(struct mLogFilter* filter, const struct mCoreConfig* config) {
116 HashTableClear(&filter->categories);
117 TableClear(&filter->levels);
118
119 mCoreConfigEnumerate(config, "logLevel.", _setFilterLevel, filter);
120 filter->defaultLevels = mLOG_ALL;
121 mCoreConfigGetIntValue(config, "logLevel", &filter->defaultLevels);
122}
123
124void mLogFilterSave(const struct mLogFilter* filter, struct mCoreConfig* config) {
125 mCoreConfigSetIntValue(config, "logLevel", filter->defaultLevels);
126 int i;
127 for (i = 0; i < _category; ++i) {
128 char configName[128] = {0};
129 snprintf(configName, sizeof(configName) - 1, "logLevel.%s", mLogCategoryId(i));
130 int levels = mLogFilterLevels(filter, i);
131 if (levels) {
132 mCoreConfigSetIntValue(config, configName, levels & ~0x80);
133 } else {
134 mCoreConfigSetValue(config, configName, NULL);
135 }
136 }
137}
138
139void mLogFilterSet(struct mLogFilter* filter, const char* category, int levels) {
140 levels |= 0x80;
141 HashTableInsert(&filter->categories, category, (void*)(intptr_t) levels);
142 // Can't do this eagerly because not all categories are initialized immediately
143 int cat = mLogCategoryById(category);
144 if (cat >= 0) {
145 TableInsert(&filter->levels, cat, (void*)(intptr_t) levels);
146 }
147}
148
149void mLogFilterReset(struct mLogFilter* filter, const char* category) {
150 HashTableRemove(&filter->categories, category);
151 // Can't do this eagerly because not all categories are initialized immediately
152 int cat = mLogCategoryById(category);
153 if (cat >= 0) {
154 TableRemove(&filter->levels, cat);
155 }
156}
157
158bool mLogFilterTest(const struct mLogFilter* filter, int category, enum mLogLevel level) {
159 int value = mLogFilterLevels(filter, category);
160 if (value) {
161 return value & level;
162 }
163 return level & filter->defaultLevels;
164}
165
166int mLogFilterLevels(const struct mLogFilter* filter , int category) {
167 int value = (intptr_t) TableLookup(&filter->levels, category);
168 if (value) {
169 return value;
170 }
171 const char* cat = mLogCategoryId(category);
172 if (cat) {
173 value = (intptr_t) HashTableLookup(&filter->categories, cat);
174 }
175 return value;
176}
177
178mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status")