LCOV - code coverage report
Current view: top level - source/server/admin - logs_handler.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 17 148 11.5 %
Date: 2024-01-05 06:35:25 Functions: 3 7 42.9 %

          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

Generated by: LCOV version 1.15