| 1 | /* |
| 2 | * Copyright 2017-present Facebook, Inc. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | #include <folly/logging/LogLevel.h> |
| 17 | |
| 18 | #include <array> |
| 19 | #include <cctype> |
| 20 | #include <ostream> |
| 21 | |
| 22 | #include <folly/Conv.h> |
| 23 | |
| 24 | using std::string; |
| 25 | |
| 26 | namespace folly { |
| 27 | |
| 28 | namespace { |
| 29 | struct NumberedLevelInfo { |
| 30 | LogLevel min; |
| 31 | LogLevel max; |
| 32 | StringPiece lowerPrefix; |
| 33 | StringPiece upperPrefix; |
| 34 | }; |
| 35 | |
| 36 | constexpr std::array<NumberedLevelInfo, 2> numberedLogLevels = {{ |
| 37 | NumberedLevelInfo{LogLevel::DBG, LogLevel::DBG0, "dbg" , "DBG" }, |
| 38 | NumberedLevelInfo{LogLevel::INFO, LogLevel::INFO0, "info" , "INFO" }, |
| 39 | }}; |
| 40 | } // namespace |
| 41 | |
| 42 | LogLevel stringToLogLevel(StringPiece name) { |
| 43 | string lowerNameStr; |
| 44 | lowerNameStr.reserve(name.size()); |
| 45 | for (char c : name) { |
| 46 | lowerNameStr.push_back(static_cast<char>(std::tolower(c))); |
| 47 | } |
| 48 | StringPiece lowerName{lowerNameStr}; |
| 49 | |
| 50 | // If the string is of the form "LogLevel::foo" or "LogLevel(foo)" |
| 51 | // strip it down just to "foo". This makes sure we can process both |
| 52 | // the "LogLevel::WARN" and "LogLevel(1234)" formats produced by |
| 53 | // logLevelToString(). |
| 54 | constexpr StringPiece lowercasePrefix{"loglevel::" }; |
| 55 | constexpr StringPiece wrapperPrefix{"loglevel(" }; |
| 56 | if (lowerName.startsWith(lowercasePrefix)) { |
| 57 | lowerName.advance(lowercasePrefix.size()); |
| 58 | } else if (lowerName.startsWith(wrapperPrefix) && lowerName.endsWith(")" )) { |
| 59 | lowerName.advance(wrapperPrefix.size()); |
| 60 | lowerName.subtract(1); |
| 61 | } |
| 62 | |
| 63 | if (lowerName == "uninitialized" ) { |
| 64 | return LogLevel::UNINITIALIZED; |
| 65 | } else if (lowerName == "none" ) { |
| 66 | return LogLevel::NONE; |
| 67 | } else if (lowerName == "debug" || lowerName == "dbg" ) { |
| 68 | return LogLevel::DBG; |
| 69 | } else if (lowerName == "info" ) { |
| 70 | return LogLevel::INFO; |
| 71 | } else if (lowerName == "warn" || lowerName == "warning" ) { |
| 72 | return LogLevel::WARN; |
| 73 | } else if (lowerName == "error" || lowerName == "err" ) { |
| 74 | return LogLevel::ERR; |
| 75 | } else if (lowerName == "critical" ) { |
| 76 | return LogLevel::CRITICAL; |
| 77 | } else if (lowerName == "dfatal" ) { |
| 78 | return LogLevel::DFATAL; |
| 79 | } else if (lowerName == "fatal" ) { |
| 80 | return LogLevel::FATAL; |
| 81 | } else if (lowerName == "max" || lowerName == "max_level" ) { |
| 82 | return LogLevel::MAX_LEVEL; |
| 83 | } |
| 84 | |
| 85 | for (const auto& info : numberedLogLevels) { |
| 86 | if (!lowerName.startsWith(info.lowerPrefix)) { |
| 87 | continue; |
| 88 | } |
| 89 | auto remainder = lowerName.subpiece(info.lowerPrefix.size()); |
| 90 | auto level = folly::tryTo<int>(remainder).value_or(-1); |
| 91 | if (level < 0 || |
| 92 | static_cast<unsigned int>(level) > (static_cast<uint32_t>(info.max) - |
| 93 | static_cast<uint32_t>(info.min))) { |
| 94 | throw std::range_error(to<string>( |
| 95 | "invalid " , info.lowerPrefix, " logger level: " , name.str())); |
| 96 | } |
| 97 | return info.max - level; |
| 98 | } |
| 99 | |
| 100 | // Try as an plain integer if all else fails |
| 101 | try { |
| 102 | auto level = folly::to<uint32_t>(lowerName); |
| 103 | return static_cast<LogLevel>(level); |
| 104 | } catch (const std::exception&) { |
| 105 | throw std::range_error("invalid logger level: " + name.str()); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | string logLevelToString(LogLevel level) { |
| 110 | if (level == LogLevel::UNINITIALIZED) { |
| 111 | return "UNINITIALIZED" ; |
| 112 | } else if (level == LogLevel::NONE) { |
| 113 | return "NONE" ; |
| 114 | } else if (level == LogLevel::DBG) { |
| 115 | return "DEBUG" ; |
| 116 | } else if (level == LogLevel::INFO) { |
| 117 | return "INFO" ; |
| 118 | } else if (level == LogLevel::WARN) { |
| 119 | return "WARN" ; |
| 120 | } else if (level == LogLevel::ERR) { |
| 121 | return "ERR" ; |
| 122 | } else if (level == LogLevel::CRITICAL) { |
| 123 | return "CRITICAL" ; |
| 124 | } else if (level == LogLevel::DFATAL) { |
| 125 | return "DFATAL" ; |
| 126 | } else if (level == LogLevel::FATAL) { |
| 127 | return "FATAL" ; |
| 128 | } |
| 129 | |
| 130 | for (const auto& info : numberedLogLevels) { |
| 131 | if (static_cast<uint32_t>(level) <= static_cast<uint32_t>(info.max) && |
| 132 | static_cast<uint32_t>(level) > static_cast<uint32_t>(info.min)) { |
| 133 | auto num = static_cast<uint32_t>(info.max) - static_cast<uint32_t>(level); |
| 134 | return folly::to<string>(info.upperPrefix, num); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | return folly::to<string>("LogLevel(" , static_cast<uint32_t>(level), ")" ); |
| 139 | } |
| 140 | |
| 141 | std::ostream& operator<<(std::ostream& os, LogLevel level) { |
| 142 | os << logLevelToString(level); |
| 143 | return os; |
| 144 | } |
| 145 | } // namespace folly |
| 146 | |