1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. |
2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) |
3 | |
4 | #pragma once |
5 | |
6 | #ifndef SPDLOG_HEADER_ONLY |
7 | # include <spdlog/cfg/helpers.h> |
8 | #endif |
9 | |
10 | #include <spdlog/spdlog.h> |
11 | #include <spdlog/details/os.h> |
12 | #include <spdlog/details/registry.h> |
13 | |
14 | #include <algorithm> |
15 | #include <string> |
16 | #include <utility> |
17 | #include <sstream> |
18 | |
19 | namespace spdlog { |
20 | namespace cfg { |
21 | namespace helpers { |
22 | |
23 | // inplace convert to lowercase |
24 | inline std::string &to_lower_(std::string &str) |
25 | { |
26 | std::transform( |
27 | str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); }); |
28 | return str; |
29 | } |
30 | |
31 | // inplace trim spaces |
32 | inline std::string &trim_(std::string &str) |
33 | { |
34 | const char *spaces = " \n\r\t" ; |
35 | str.erase(str.find_last_not_of(spaces) + 1); |
36 | str.erase(0, str.find_first_not_of(spaces)); |
37 | return str; |
38 | } |
39 | |
40 | // return (name,value) trimmed pair from given "name=value" string. |
41 | // return empty string on missing parts |
42 | // "key=val" => ("key", "val") |
43 | // " key = val " => ("key", "val") |
44 | // "key=" => ("key", "") |
45 | // "val" => ("", "val") |
46 | |
47 | inline std::pair<std::string, std::string> (char sep, const std::string &str) |
48 | { |
49 | auto n = str.find(sep); |
50 | std::string k, v; |
51 | if (n == std::string::npos) |
52 | { |
53 | v = str; |
54 | } |
55 | else |
56 | { |
57 | k = str.substr(0, n); |
58 | v = str.substr(n + 1); |
59 | } |
60 | return std::make_pair(trim_(k), trim_(v)); |
61 | } |
62 | |
63 | // return vector of key/value pairs from sequence of "K1=V1,K2=V2,.." |
64 | // "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...} |
65 | inline std::unordered_map<std::string, std::string> (const std::string &str) |
66 | { |
67 | std::string token; |
68 | std::istringstream token_stream(str); |
69 | std::unordered_map<std::string, std::string> rv{}; |
70 | while (std::getline(token_stream, token, ',')) |
71 | { |
72 | if (token.empty()) |
73 | { |
74 | continue; |
75 | } |
76 | auto kv = extract_kv_('=', token); |
77 | rv[kv.first] = kv.second; |
78 | } |
79 | return rv; |
80 | } |
81 | |
82 | SPDLOG_INLINE void load_levels(const std::string &input) |
83 | { |
84 | if (input.empty() || input.size() > 512) |
85 | { |
86 | return; |
87 | } |
88 | |
89 | auto key_vals = extract_key_vals_(input); |
90 | std::unordered_map<std::string, level::level_enum> levels; |
91 | level::level_enum global_level = level::info; |
92 | bool global_level_found = false; |
93 | |
94 | for (auto &name_level : key_vals) |
95 | { |
96 | auto &logger_name = name_level.first; |
97 | auto level_name = to_lower_(name_level.second); |
98 | auto level = level::from_str(level_name); |
99 | // ignore unrecognized level names |
100 | if (level == level::off && level_name != "off" ) |
101 | { |
102 | continue; |
103 | } |
104 | if (logger_name.empty()) // no logger name indicate global level |
105 | { |
106 | global_level_found = true; |
107 | global_level = level; |
108 | } |
109 | else |
110 | { |
111 | levels[logger_name] = level; |
112 | } |
113 | } |
114 | |
115 | details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr); |
116 | } |
117 | |
118 | } // namespace helpers |
119 | } // namespace cfg |
120 | } // namespace spdlog |
121 | |