| 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/details/os.h> |
| 11 | #include <spdlog/details/registry.h> |
| 12 | #include <spdlog/spdlog.h> |
| 13 | |
| 14 | #include <algorithm> |
| 15 | #include <sstream> |
| 16 | #include <string> |
| 17 | #include <utility> |
| 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 | std::transform(first: str.begin(), last: str.end(), result: str.begin(), unary_op: [](char ch) { |
| 26 | return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); |
| 27 | }); |
| 28 | return str; |
| 29 | } |
| 30 | |
| 31 | // inplace trim spaces |
| 32 | inline std::string &trim_(std::string &str) { |
| 33 | const char *spaces = " \n\r\t" ; |
| 34 | str.erase(pos: str.find_last_not_of(s: spaces) + 1); |
| 35 | str.erase(pos: 0, n: str.find_first_not_of(s: spaces)); |
| 36 | return str; |
| 37 | } |
| 38 | |
| 39 | // return (name,value) trimmed pair from given "name=value" string. |
| 40 | // return empty string on missing parts |
| 41 | // "key=val" => ("key", "val") |
| 42 | // " key = val " => ("key", "val") |
| 43 | // "key=" => ("key", "") |
| 44 | // "val" => ("", "val") |
| 45 | |
| 46 | inline std::pair<std::string, std::string> (char sep, const std::string &str) { |
| 47 | auto n = str.find(c: sep); |
| 48 | std::string k, v; |
| 49 | if (n == std::string::npos) { |
| 50 | v = str; |
| 51 | } else { |
| 52 | k = str.substr(pos: 0, n: n); |
| 53 | v = str.substr(pos: n + 1); |
| 54 | } |
| 55 | return std::make_pair(x&: trim_(str&: k), y&: trim_(str&: v)); |
| 56 | } |
| 57 | |
| 58 | // return vector of key/value pairs from sequence of "K1=V1,K2=V2,.." |
| 59 | // "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...} |
| 60 | inline std::unordered_map<std::string, std::string> (const std::string &str) { |
| 61 | std::string token; |
| 62 | std::istringstream token_stream(str); |
| 63 | std::unordered_map<std::string, std::string> rv{}; |
| 64 | while (std::getline(in&: token_stream, str&: token, delim: ',')) { |
| 65 | if (token.empty()) { |
| 66 | continue; |
| 67 | } |
| 68 | auto kv = extract_kv_(sep: '=', str: token); |
| 69 | rv[kv.first] = kv.second; |
| 70 | } |
| 71 | return rv; |
| 72 | } |
| 73 | |
| 74 | SPDLOG_INLINE void load_levels(const std::string &input) { |
| 75 | if (input.empty() || input.size() > 512) { |
| 76 | return; |
| 77 | } |
| 78 | |
| 79 | auto key_vals = extract_key_vals_(str: input); |
| 80 | std::unordered_map<std::string, level::level_enum> levels; |
| 81 | level::level_enum global_level = level::info; |
| 82 | bool global_level_found = false; |
| 83 | |
| 84 | for (auto &name_level : key_vals) { |
| 85 | auto &logger_name = name_level.first; |
| 86 | auto level_name = to_lower_(str&: name_level.second); |
| 87 | auto level = level::from_str(name: level_name); |
| 88 | // ignore unrecognized level names |
| 89 | if (level == level::off && level_name != "off" ) { |
| 90 | continue; |
| 91 | } |
| 92 | if (logger_name.empty()) // no logger name indicate global level |
| 93 | { |
| 94 | global_level_found = true; |
| 95 | global_level = level; |
| 96 | } else { |
| 97 | levels[logger_name] = level; |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | details::registry::instance().set_levels(levels: std::move(levels), |
| 102 | global_level: global_level_found ? &global_level : nullptr); |
| 103 | } |
| 104 | |
| 105 | } // namespace helpers |
| 106 | } // namespace cfg |
| 107 | } // namespace spdlog |
| 108 | |