1 /** @file logfilter.cpp Log entry filter.
2 *
3 * @authors Copyright © 2014-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 *
5 * @par License
6 * LGPL: http://www.gnu.org/licenses/lgpl.html
7 *
8 * <small>This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or (at your
11 * option) any later version. This program is distributed in the hope that it
12 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
14 * General Public License for more details. You should have received a copy of
15 * the GNU Lesser General Public License along with this program; if not, see:
16 * http://www.gnu.org/licenses</small>
17 */
18
19 #include "de/LogFilter"
20
21 namespace de {
22
23 namespace internal {
24 enum FilterId {
25 GenericFilter,
26 ResourceFilter,
27 MapFilter,
28 ScriptFilter,
29 GLFilter,
30 AudioFilter,
31 InputFilter,
32 NetworkFilter,
33 NUM_FILTERS
34 };
35 static char const *subRecName[NUM_FILTERS] = { // for Config
36 "generic",
37 "resource",
38 "map",
39 "script",
40 "gl",
41 "audio",
42 "input",
43 "network"
44 };
45 }
46
47 using namespace internal;
48
49 /**
50 * Filter for determining which log entries will be put in the log buffer.
51 */
DENG2_PIMPL_NOREF(LogFilter)52 DENG2_PIMPL_NOREF(LogFilter)
53 {
54 /// Filtering information for a domain.
55 struct Filter {
56 int domainBit;
57 LogEntry::Level minLevel;
58 bool allowDev;
59
60 Filter()
61 : domainBit(LogEntry::GenericBit)
62 , minLevel(LogEntry::Message)
63 , allowDev(false)
64 {}
65
66 inline bool checkContextBit(duint32 md) const
67 {
68 return (md & (1 << domainBit)) != 0;
69 }
70
71 void read(Record const &rec)
72 {
73 minLevel = LogEntry::Level(dint(rec["minLevel"].value().asNumber()));
74 allowDev = rec["allowDev"].value().isTrue();
75 }
76
77 void write(Record &rec) const
78 {
79 rec.set("minLevel", dint(minLevel));
80 rec.set("allowDev", allowDev);
81 }
82 };
83
84 Filter filterByContext[NUM_FILTERS];
85
86 Impl()
87 {
88 for (int i = 0; i < NUM_FILTERS; ++i)
89 {
90 filterByContext[i].domainBit = LogEntry::FirstDomainBit + i;
91 }
92 }
93
94 bool isLogEntryAllowed(duint32 md) const
95 {
96 // Multiple contexts allowed, in which case if any one passes,
97 // the entry is allowed.
98 for (uint i = 0; i < NUM_FILTERS; ++i)
99 {
100 Filter const &ftr = filterByContext[i];
101 if (ftr.checkContextBit(md))
102 {
103 if ((md & LogEntry::Dev) && !ftr.allowDev) continue; // No devs.
104 if (ftr.minLevel <= int(md & LogEntry::LevelMask))
105 {
106 // Pass due to entry level being enabled.
107 return true;
108 }
109 if ((md & LogEntry::Interactive) && i == ScriptFilter)
110 {
111 // Interactive script entries pass.
112 return true;
113 }
114 }
115 }
116 return false;
117 }
118
119 LogEntry::Level minLevel(duint32 md) const
120 {
121 int lev = LogEntry::HighestLogLevel + 1;
122 for (uint i = 0; i < NUM_FILTERS; ++i)
123 {
124 Filter const &ftr = filterByContext[i];
125 if (ftr.checkContextBit(md))
126 {
127 lev = de::min(lev, int(ftr.minLevel));
128 }
129 }
130 return LogEntry::Level(lev);
131 }
132
133 bool allowDev(duint32 md) const
134 {
135 for (uint i = 0; i < NUM_FILTERS; ++i)
136 {
137 Filter const &ftr = filterByContext[i];
138 if (ftr.checkContextBit(md))
139 {
140 if (ftr.allowDev) return true;
141 }
142 }
143 return false;
144 }
145
146 void setAllowDev(duint32 md, bool allow)
147 {
148 for (uint i = 0; i < NUM_FILTERS; ++i)
149 {
150 Filter &ftr = filterByContext[i];
151 if (ftr.checkContextBit(md))
152 {
153 ftr.allowDev = allow;
154 }
155 }
156 }
157
158 void setMinLevel(duint32 md, LogEntry::Level level)
159 {
160 for (uint i = 0; i < NUM_FILTERS; ++i)
161 {
162 Filter &ftr = filterByContext[i];
163 if (ftr.checkContextBit(md))
164 {
165 ftr.minLevel = level;
166 }
167 }
168 }
169
170 void read(Record const &rec)
171 {
172 try
173 {
174 for (uint i = 0; i < NUM_FILTERS; ++i)
175 {
176 filterByContext[i].read(rec.subrecord(subRecName[i]));
177 }
178 }
179 catch (Error const &er)
180 {
181 LOGDEV_WARNING("Failed to read filter from record: %s\nThe record is:\n%s")
182 << er.asText() << rec.asText();
183
184 LOG_WARNING("Log filter reset to defaults");
185 *this = Impl(); // Reset.
186 }
187 }
188
189 void write(Record &rec) const
190 {
191 for (uint i = 0; i < NUM_FILTERS; ++i)
192 {
193 // Reuse existing subrecords.
194 if (!rec.hasSubrecord(subRecName[i]))
195 {
196 rec.add(subRecName[i], new Record);
197 }
198 filterByContext[i].write(rec.subrecord(subRecName[i]));
199 }
200 }
201 };
202
LogFilter()203 LogFilter::LogFilter() : d(new Impl)
204 {}
205
isLogEntryAllowed(duint32 metadata) const206 bool LogFilter::isLogEntryAllowed(duint32 metadata) const
207 {
208 DENG2_ASSERT(metadata & LogEntry::DomainMask); // must have a domain
209 return d->isLogEntryAllowed(metadata);
210 }
211
setAllowDev(duint32 md,bool allow)212 void LogFilter::setAllowDev(duint32 md, bool allow)
213 {
214 d->setAllowDev(md, allow);
215 }
216
setMinLevel(duint32 md,LogEntry::Level level)217 void LogFilter::setMinLevel(duint32 md, LogEntry::Level level)
218 {
219 d->setMinLevel(md, level);
220 }
221
allowDev(duint32 md) const222 bool LogFilter::allowDev(duint32 md) const
223 {
224 return d->allowDev(md);
225 }
226
minLevel(duint32 md) const227 LogEntry::Level LogFilter::minLevel(duint32 md) const
228 {
229 return d->minLevel(md);
230 }
231
read(Record const & rec)232 void LogFilter::read(Record const &rec)
233 {
234 d->read(rec);
235 }
236
write(Record & rec) const237 void LogFilter::write(Record &rec) const
238 {
239 d->write(rec);
240 }
241
domainRecordName(LogEntry::Context domain)242 String LogFilter::domainRecordName(LogEntry::Context domain)
243 {
244 for (int i = LogEntry::FirstDomainBit; i <= LogEntry::LastDomainBit; ++i)
245 {
246 if (domain & (1 << i)) return subRecName[i - LogEntry::FirstDomainBit];
247 }
248 return "";
249 }
250
251 } // namespace de
252