1 // Copyright 2014 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <algorithm>
6 #include "common/logging/backend.h"
7 #include "common/logging/filter.h"
8 #include "common/string_util.h"
9
10 namespace Log {
11 namespace {
12 template <typename It>
GetLevelByName(const It begin,const It end)13 Level GetLevelByName(const It begin, const It end) {
14 for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
15 const char* level_name = GetLevelName(static_cast<Level>(i));
16 if (Common::ComparePartialString(begin, end, level_name)) {
17 return static_cast<Level>(i);
18 }
19 }
20 return Level::Count;
21 }
22
23 template <typename It>
GetClassByName(const It begin,const It end)24 Class GetClassByName(const It begin, const It end) {
25 for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
26 const char* level_name = GetLogClassName(static_cast<Class>(i));
27 if (Common::ComparePartialString(begin, end, level_name)) {
28 return static_cast<Class>(i);
29 }
30 }
31 return Class::Count;
32 }
33
34 template <typename Iterator>
ParseFilterRule(Filter & instance,Iterator begin,Iterator end)35 bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
36 auto level_separator = std::find(begin, end, ':');
37 if (level_separator == end) {
38 LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}",
39 std::string(begin, end));
40 return false;
41 }
42
43 const Level level = GetLevelByName(level_separator + 1, end);
44 if (level == Level::Count) {
45 LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
46 return false;
47 }
48
49 if (Common::ComparePartialString(begin, level_separator, "*")) {
50 instance.ResetAll(level);
51 return true;
52 }
53
54 const Class log_class = GetClassByName(begin, level_separator);
55 if (log_class == Class::Count) {
56 LOG_ERROR(Log, "Unknown log class in filter: {}", std::string(begin, end));
57 return false;
58 }
59
60 instance.SetClassLevel(log_class, level);
61 return true;
62 }
63 } // Anonymous namespace
64
Filter(Level default_level)65 Filter::Filter(Level default_level) {
66 ResetAll(default_level);
67 }
68
ResetAll(Level level)69 void Filter::ResetAll(Level level) {
70 class_levels.fill(level);
71 }
72
SetClassLevel(Class log_class,Level level)73 void Filter::SetClassLevel(Class log_class, Level level) {
74 class_levels[static_cast<std::size_t>(log_class)] = level;
75 }
76
ParseFilterString(std::string_view filter_view)77 void Filter::ParseFilterString(std::string_view filter_view) {
78 auto clause_begin = filter_view.cbegin();
79 while (clause_begin != filter_view.cend()) {
80 auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
81
82 // If clause isn't empty
83 if (clause_end != clause_begin) {
84 ParseFilterRule(*this, clause_begin, clause_end);
85 }
86
87 if (clause_end != filter_view.cend()) {
88 // Skip over the whitespace
89 ++clause_end;
90 }
91 clause_begin = clause_end;
92 }
93 }
94
CheckMessage(Class log_class,Level level) const95 bool Filter::CheckMessage(Class log_class, Level level) const {
96 return static_cast<u8>(level) >=
97 static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]);
98 }
99
IsDebug() const100 bool Filter::IsDebug() const {
101 return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) {
102 return static_cast<u8>(l) <= static_cast<u8>(Level::Debug);
103 });
104 }
105
106 } // namespace Log
107