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
19namespace spdlog {
20namespace cfg {
21namespace helpers {
22
23// inplace convert to lowercase
24inline 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
32inline 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
46inline std::pair<std::string, std::string> extract_kv_(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"),...}
60inline std::unordered_map<std::string, std::string> extract_key_vals_(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
74SPDLOG_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