Line data Source code
1 : #include "source/server/admin/logs_handler.h" 2 : 3 : #include <string> 4 : 5 : #include "source/common/common/fine_grain_logger.h" 6 : #include "source/common/common/logger.h" 7 : #include "source/server/admin/utils.h" 8 : 9 : #include "absl/strings/str_split.h" 10 : 11 : namespace Envoy { 12 : namespace Server { 13 : 14 : namespace { 15 : // Build the level string to level enum map. 16 134 : absl::flat_hash_map<absl::string_view, spdlog::level::level_enum> buildLevelMap() { 17 134 : absl::flat_hash_map<absl::string_view, spdlog::level::level_enum> levels; 18 : 19 134 : uint32_t i = 0; 20 938 : for (absl::string_view level_string : LogsHandler::levelStrings()) { 21 938 : levels[level_string] = static_cast<spdlog::level::level_enum>(i++); 22 938 : } 23 : 24 134 : return levels; 25 134 : } 26 : } // namespace 27 : 28 : LogsHandler::LogsHandler(Server::Instance& server) 29 134 : : HandlerContextBase(server), log_levels_(buildLevelMap()) {} 30 : 31 268 : std::vector<absl::string_view> LogsHandler::levelStrings() { 32 268 : std::vector<absl::string_view> strings; 33 268 : strings.reserve(ARRAY_SIZE(spdlog::level::level_string_views)); 34 1876 : for (spdlog::string_view_t level : spdlog::level::level_string_views) { 35 1876 : strings.emplace_back(absl::string_view{level.data(), level.size()}); 36 1876 : } 37 268 : return strings; 38 268 : } 39 : 40 : Http::Code LogsHandler::handlerLogging(Http::ResponseHeaderMap&, Buffer::Instance& response, 41 0 : AdminStream& admin_stream) { 42 0 : Http::Utility::QueryParamsMulti query_params = admin_stream.queryParams(); 43 : 44 0 : Http::Code rc = Http::Code::OK; 45 0 : const absl::Status status = changeLogLevel(query_params); 46 0 : if (!status.ok()) { 47 0 : rc = Http::Code::BadRequest; 48 0 : response.add(fmt::format("error: {}\n\n", status.message())); 49 : 50 0 : response.add("usage: /logging?<name>=<level> (change single level)\n"); 51 0 : response.add("usage: /logging?paths=name1:level1,name2:level2,... (change multiple levels)\n"); 52 0 : response.add("usage: /logging?level=<level> (change all levels)\n"); 53 0 : response.add("levels: "); 54 0 : for (auto level_string_view : spdlog::level::level_string_views) { 55 0 : response.add(fmt::format("{} ", level_string_view)); 56 0 : } 57 : 58 0 : response.add("\n"); 59 0 : } 60 : 61 0 : if (!Logger::Context::useFineGrainLogger()) { 62 0 : response.add("active loggers:\n"); 63 0 : for (const Logger::Logger& logger : Logger::Registry::loggers()) { 64 0 : response.add(fmt::format(" {}: {}\n", logger.name(), logger.levelString())); 65 0 : } 66 : 67 0 : response.add("\n"); 68 0 : } else { 69 0 : response.add("active loggers:\n"); 70 0 : std::string logger_info = getFineGrainLogContext().listFineGrainLoggers(); 71 0 : response.add(logger_info); 72 0 : response.add("\n"); 73 0 : } 74 : 75 0 : return rc; 76 0 : } 77 : 78 : Http::Code LogsHandler::handlerReopenLogs(Http::ResponseHeaderMap&, Buffer::Instance& response, 79 0 : AdminStream&) { 80 0 : server_.accessLogManager().reopen(); 81 0 : response.add("OK\n"); 82 0 : return Http::Code::OK; 83 0 : } 84 : 85 0 : absl::Status LogsHandler::changeLogLevel(Http::Utility::QueryParamsMulti& params) { 86 : // "level" and "paths" will be set to the empty string when this is invoked 87 : // from HTML without setting them, so clean out empty values. 88 0 : auto level = params.getFirstValue("level"); 89 0 : if (level.has_value() && level.value().empty()) { 90 0 : params.remove("level"); 91 0 : level = std::nullopt; 92 0 : } 93 0 : auto paths = params.getFirstValue("paths"); 94 0 : if (paths.has_value() && paths.value().empty()) { 95 0 : params.remove("paths"); 96 0 : paths = std::nullopt; 97 0 : } 98 : 99 0 : if (params.data().empty()) { 100 0 : return absl::OkStatus(); 101 0 : } 102 : 103 0 : if (params.data().size() != 1) { 104 0 : return absl::InvalidArgumentError("invalid number of parameters"); 105 0 : } 106 : 107 0 : if (level.has_value()) { 108 : // Change all log levels. 109 0 : const absl::StatusOr<spdlog::level::level_enum> level_to_use = parseLogLevel(level.value()); 110 0 : if (!level_to_use.ok()) { 111 0 : return level_to_use.status(); 112 0 : } 113 : 114 0 : Logger::Context::changeAllLogLevels(*level_to_use); 115 0 : return absl::OkStatus(); 116 0 : } 117 : 118 : // Build a map of name:level pairs, a few allocations is ok here since it's 119 : // not common to call this function at a high rate. 120 0 : absl::flat_hash_map<absl::string_view, spdlog::level::level_enum> name_levels; 121 0 : std::vector<std::pair<absl::string_view, int>> glob_levels; 122 0 : const bool use_fine_grain_logger = Logger::Context::useFineGrainLogger(); 123 : 124 0 : if (paths.has_value()) { 125 : // Bulk change log level by name:level pairs, separated by comma. 126 0 : std::vector<absl::string_view> pairs = 127 0 : absl::StrSplit(paths.value(), ',', absl::SkipWhitespace()); 128 0 : for (const auto& name_level : pairs) { 129 0 : const std::pair<absl::string_view, absl::string_view> name_level_pair = 130 0 : absl::StrSplit(name_level, absl::MaxSplits(':', 1), absl::SkipWhitespace()); 131 0 : auto [name, level] = name_level_pair; 132 0 : if (name.empty() || level.empty()) { 133 0 : return absl::InvalidArgumentError("empty logger name or empty logger level"); 134 0 : } 135 : 136 0 : const absl::StatusOr<spdlog::level::level_enum> level_to_use = parseLogLevel(level); 137 0 : if (!level_to_use.ok()) { 138 0 : return level_to_use.status(); 139 0 : } 140 : 141 0 : if (use_fine_grain_logger) { 142 0 : ENVOY_LOG(info, "adding fine-grain log update, glob='{}' level='{}'", name, 143 0 : spdlog::level::level_string_views[*level_to_use]); 144 0 : glob_levels.emplace_back(name, *level_to_use); 145 0 : } else { 146 0 : name_levels[name] = *level_to_use; 147 0 : } 148 0 : } 149 0 : } else { 150 : // The HTML admin interface will always populate "level" and "paths" though 151 : // they may be empty. There's a legacy non-HTML-accessible mechanism to 152 : // set a single logger to a level, which we'll handle now. In this scenario, 153 : // "level" and "paths" will not be populated. 154 0 : if (params.data().size() != 1) { 155 0 : return absl::InvalidArgumentError("invalid number of parameters"); 156 0 : } 157 : 158 : // Change particular log level by name. 159 0 : const auto it = params.data().begin(); 160 0 : const std::string& key = it->first; 161 0 : const std::string& value = it->second[0]; 162 : 163 0 : const absl::StatusOr<spdlog::level::level_enum> level_to_use = parseLogLevel(value); 164 0 : if (!level_to_use.ok()) { 165 0 : return level_to_use.status(); 166 0 : } 167 : 168 0 : if (use_fine_grain_logger) { 169 0 : ENVOY_LOG(info, "adding fine-grain log update, glob='{}' level='{}'", key, 170 0 : spdlog::level::level_string_views[*level_to_use]); 171 0 : glob_levels.emplace_back(key, *level_to_use); 172 0 : } else { 173 0 : name_levels[key] = *level_to_use; 174 0 : } 175 0 : } 176 : 177 0 : if (!use_fine_grain_logger) { 178 0 : return changeLogLevelsForComponentLoggers(name_levels); 179 0 : } 180 0 : getFineGrainLogContext().updateVerbositySetting(glob_levels); 181 : 182 0 : return absl::OkStatus(); 183 0 : } 184 : 185 : absl::Status LogsHandler::changeLogLevelsForComponentLoggers( 186 0 : const absl::flat_hash_map<absl::string_view, spdlog::level::level_enum>& changes) { 187 0 : std::vector<std::pair<Logger::Logger*, spdlog::level::level_enum>> loggers_to_change; 188 0 : for (Logger::Logger& logger : Logger::Registry::loggers()) { 189 0 : auto name_level_itr = changes.find(logger.name()); 190 0 : if (name_level_itr == changes.end()) { 191 0 : continue; 192 0 : } 193 : 194 0 : loggers_to_change.emplace_back(std::make_pair(&logger, name_level_itr->second)); 195 0 : } 196 : 197 : // Check if we have any invalid logger in changes. 198 0 : if (loggers_to_change.size() != changes.size()) { 199 0 : return absl::InvalidArgumentError("unknown logger name"); 200 0 : } 201 : 202 0 : for (auto& it : loggers_to_change) { 203 0 : Logger::Logger* logger = it.first; 204 0 : spdlog::level::level_enum level = it.second; 205 : 206 0 : ENVOY_LOG(info, "change log level: name='{}' level='{}'", logger->name(), 207 0 : spdlog::level::level_string_views[level]); 208 0 : logger->setLevel(level); 209 0 : } 210 : 211 0 : return absl::OkStatus(); 212 0 : } 213 : 214 : } // namespace Server 215 : } // namespace Envoy