libminifi/include/core/logging/Logger.h (160 lines of code) (raw):

/** * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <string> #include <mutex> #include <memory> #include <optional> #include <sstream> #include <utility> #include <iostream> #include <vector> #include <algorithm> #include "spdlog/common.h" #include "spdlog/logger.h" #include "utils/gsl.h" #include "utils/SmallString.h" #include "utils/meta/detected.h" namespace org::apache::nifi::minifi::core::logging { inline constexpr size_t LOG_BUFFER_SIZE = 1024; class LoggerControl { public: LoggerControl(); [[nodiscard]] bool is_enabled() const; void setEnabled(bool status); protected: std::atomic<bool> is_enabled_; }; template<typename Arg> using has_const_c_str_method = decltype(std::declval<const Arg&>().c_str()); template<typename Arg> inline decltype(auto) conditional_stringify(Arg&& arg) { if constexpr (utils::meta::is_detected_v<has_const_c_str_method, Arg> || std::is_scalar_v<std::decay_t<Arg>>) { return std::forward<Arg>(arg); } else if constexpr (std::is_invocable_v<Arg>) { return std::forward<Arg>(arg)(); } } template<typename Arg> inline decltype(auto) conditional_convert(const Arg& val) { if constexpr (std::is_scalar_v<std::decay_t<Arg>>) { return val; } else if constexpr (std::is_same_v<decltype(std::declval<const Arg&>().c_str()), const char*>) { return val.c_str(); } } template<typename ...Args> std::string format_string(int max_size, char const* format_str, const Args& ...args) { // try to use static buffer char buf[LOG_BUFFER_SIZE + 1]; int result = std::snprintf(buf, LOG_BUFFER_SIZE + 1, format_str, conditional_convert(args)...); if (result < 0) { return "Error while formatting log message"; } const auto buf_size = gsl::narrow<size_t>(result); if (buf_size <= LOG_BUFFER_SIZE) { // static buffer was large enough return {buf, max_size >= 0 ? std::min(buf_size, gsl::narrow<size_t>(max_size)) : buf_size}; } // try to use dynamic buffer size_t dynamic_buffer_size = max_size < 0 ? buf_size : gsl::narrow<size_t>(std::min(result, max_size)); std::vector<char> buffer(dynamic_buffer_size + 1); // extra '\0' character result = std::snprintf(buffer.data(), buffer.size(), format_str, conditional_convert(args)...); if (result < 0) { return "Error while formatting log message"; } return {buffer.cbegin(), buffer.cend() - 1}; // -1 to not include the terminating '\0' } inline std::string format_string(int max_size, char const* format_str) { std::string return_value(format_str); if (max_size >= 0 && return_value.size() > gsl::narrow<size_t>(max_size)) { return return_value.substr(0, max_size); } return return_value; } enum LOG_LEVEL { trace = 0, debug = 1, info = 2, warn = 3, err = 4, critical = 5, off = 6 }; class BaseLogger { public: virtual ~BaseLogger(); virtual void log_string(LOG_LEVEL level, std::string str) = 0; virtual bool should_log(const LOG_LEVEL &level); }; /** * LogBuilder is a class to facilitate using the LOG macros below and an associated put-to operator. * */ class LogBuilder { public: LogBuilder(BaseLogger *l, LOG_LEVEL level); ~LogBuilder(); void setIgnore(); void log_string(LOG_LEVEL level) const; template<typename T> LogBuilder &operator<<(const T &o) { if (!ignore) str << o; return *this; } bool ignore; BaseLogger *ptr; std::stringstream str; LOG_LEVEL level; }; class Logger : public BaseLogger { public: Logger(Logger const&) = delete; Logger& operator=(Logger const&) = delete; /** * @brief Log error message * @param format format string ('man printf' for syntax) * @warning does not check @p log or @p format for null. Caller must ensure parameters and format string lengths match */ template<typename ...Args> void log_error(const char * const format, Args&& ...args) { log(spdlog::level::err, format, std::forward<Args>(args)...); } /** * @brief Log warn message * @param format format string ('man printf' for syntax) * @warning does not check @p log or @p format for null. Caller must ensure parameters and format string lengths match */ template<typename ...Args> void log_warn(const char * const format, Args&& ...args) { log(spdlog::level::warn, format, std::forward<Args>(args)...); } /** * @brief Log info message * @param format format string ('man printf' for syntax) * @warning does not check @p log or @p format for null. Caller must ensure parameters and format string lengths match */ template<typename ...Args> void log_info(const char * const format, Args&& ...args) { log(spdlog::level::info, format, std::forward<Args>(args)...); } /** * @brief Log debug message * @param format format string ('man printf' for syntax) * @warning does not check @p log or @p format for null. Caller must ensure parameters and format string lengths match */ template<typename ...Args> void log_debug(const char * const format, Args&& ...args) { log(spdlog::level::debug, format, std::forward<Args>(args)...); } /** * @brief Log trace message * @param format format string ('man printf' for syntax) * @warning does not check @p log or @p format for null. Caller must ensure parameters and format string lengths match */ template<typename ...Args> void log_trace(const char * const format, Args&& ...args) { log(spdlog::level::trace, format, std::forward<Args>(args)...); } void set_max_log_size(int size) { max_log_size_ = size; } bool should_log(const LOG_LEVEL &level) override; void log_string(LOG_LEVEL level, std::string str) override; virtual std::optional<std::string> get_id() = 0; protected: Logger(std::shared_ptr<spdlog::logger> delegate, std::shared_ptr<LoggerControl> controller); Logger(std::shared_ptr<spdlog::logger> delegate); // NOLINT std::shared_ptr<spdlog::logger> delegate_; std::shared_ptr<LoggerControl> controller_; std::mutex mutex_; private: template<typename ...Args> inline void log(spdlog::level::level_enum level, const char* const format, Args&& ...args) { if (controller_ && !controller_->is_enabled()) return; std::lock_guard<std::mutex> lock(mutex_); if (!delegate_->should_log(level)) { return; } auto str = format_string(max_log_size_.load(), format, conditional_stringify(std::forward<Args>(args))...); if (const auto id = get_id()) { str = str + *id; } delegate_->log(level, str); } std::atomic<int> max_log_size_{LOG_BUFFER_SIZE}; }; #define LOG_DEBUG(x) LogBuilder((x).get(), org::apache::nifi::minifi::core::logging::LOG_LEVEL::debug) #define LOG_INFO(x) LogBuilder((x).get(), org::apache::nifi::minifi::core::logging::LOG_LEVEL::info) #define LOG_TRACE(x) LogBuilder((x).get(), org::apache::nifi::minifi::core::logging::LOG_LEVEL::trace) #define LOG_ERROR(x) LogBuilder((x).get(), org::apache::nifi::minifi::core::logging::LOG_LEVEL::err) #define LOG_WARN(x) LogBuilder((x).get(), org::apache::nifi::minifi::core::logging::LOG_LEVEL::warn) } // namespace org::apache::nifi::minifi::core::logging