| /** |
| * @file Logger.cpp |
| * Logger class implementation |
| * |
| * 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. |
| */ |
| |
| #include "core/logging/LoggerConfiguration.h" |
| |
| #include <sys/stat.h> |
| #include <algorithm> |
| #include <vector> |
| #include <queue> |
| #include <memory> |
| #include <map> |
| #include <string> |
| |
| #include "core/Core.h" |
| #include "utils/StringUtils.h" |
| #include "utils/ClassUtils.h" |
| #include "utils/file/FileUtils.h" |
| #include "utils/Environment.h" |
| |
| #include "spdlog/spdlog.h" |
| #include "spdlog/sinks/stdout_sinks.h" |
| #include "spdlog/sinks/null_sink.h" |
| |
| #ifdef WIN32 |
| #include "core/logging/WindowsEventLogSink.h" |
| #else |
| #include "spdlog/sinks/syslog_sink.h" |
| #endif |
| |
| #ifdef WIN32 |
| #include <direct.h> |
| #define _WINSOCKAPI_ |
| #include <windows.h> |
| #include <tchar.h> |
| #endif |
| |
| namespace org { |
| namespace apache { |
| namespace nifi { |
| namespace minifi { |
| namespace core { |
| namespace logging { |
| |
| const char* LoggerConfiguration::spdlog_default_pattern = "[%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v"; |
| |
| std::vector<std::string> LoggerProperties::get_keys_of_type(const std::string &type) { |
| std::vector<std::string> appenders; |
| std::string prefix = type + "."; |
| for (auto const & entry : properties_) { |
| if (entry.first.rfind(prefix, 0) == 0 && entry.first.find(".", prefix.length() + 1) == std::string::npos) { |
| appenders.push_back(entry.first); |
| } |
| } |
| return appenders; |
| } |
| |
| LoggerConfiguration::LoggerConfiguration() |
| : root_namespace_(create_default_root()), |
| loggers(std::vector<std::shared_ptr<LoggerImpl>>()), |
| shorten_names_(false), |
| formatter_(std::make_shared<spdlog::pattern_formatter>(spdlog_default_pattern)) { |
| controller_ = std::make_shared<LoggerControl>(); |
| logger_ = std::shared_ptr<LoggerImpl>( |
| new LoggerImpl(core::getClassName<LoggerConfiguration>(), controller_, get_logger(nullptr, root_namespace_, core::getClassName<LoggerConfiguration>(), formatter_))); |
| loggers.push_back(logger_); |
| } |
| |
| void LoggerConfiguration::initialize(const std::shared_ptr<LoggerProperties> &logger_properties) { |
| std::lock_guard<std::mutex> lock(mutex); |
| root_namespace_ = initialize_namespaces(logger_properties); |
| std::string spdlog_pattern; |
| if (!logger_properties->get("spdlog.pattern", spdlog_pattern)) { |
| spdlog_pattern = spdlog_default_pattern; |
| } |
| |
| /** |
| * There is no need to shorten names per spdlog sink as this is a per log instance. |
| */ |
| std::string shorten_names_str; |
| if (logger_properties->get("spdlog.shorten_names", shorten_names_str)) { |
| utils::StringUtils::StringToBool(shorten_names_str, shorten_names_); |
| } |
| |
| formatter_ = std::make_shared<spdlog::pattern_formatter>(spdlog_pattern); |
| std::map<std::string, std::shared_ptr<spdlog::logger>> spdloggers; |
| for (auto const & logger_impl : loggers) { |
| std::shared_ptr<spdlog::logger> spdlogger; |
| auto it = spdloggers.find(logger_impl->name); |
| if (it == spdloggers.end()) { |
| spdlogger = get_logger(logger_, root_namespace_, logger_impl->name, formatter_, true); |
| spdloggers[logger_impl->name] = spdlogger; |
| } else { |
| spdlogger = it->second; |
| } |
| logger_impl->set_delegate(spdlogger); |
| } |
| logger_->log_debug("Set following pattern on loggers: %s", spdlog_pattern); |
| } |
| |
| std::shared_ptr<Logger> LoggerConfiguration::getLogger(const std::string &name) { |
| std::lock_guard<std::mutex> lock(mutex); |
| std::string adjusted_name = name; |
| const std::string clazz = "class "; |
| auto haz_clazz = name.find(clazz); |
| if (haz_clazz == 0) |
| adjusted_name = name.substr(clazz.length(), name.length() - clazz.length()); |
| if (shorten_names_) { |
| utils::ClassUtils::shortenClassName(adjusted_name, adjusted_name); |
| } |
| |
| std::shared_ptr<LoggerImpl> result = std::make_shared<LoggerImpl>(adjusted_name, controller_, get_logger(logger_, root_namespace_, adjusted_name, formatter_)); |
| loggers.push_back(result); |
| return result; |
| } |
| |
| std::shared_ptr<internal::LoggerNamespace> LoggerConfiguration::initialize_namespaces(const std::shared_ptr<LoggerProperties> &logger_properties) { |
| std::map<std::string, std::shared_ptr<spdlog::sinks::sink>> sink_map = logger_properties->initial_sinks(); |
| |
| std::string appender_type = "appender"; |
| for (auto const & appender_key : logger_properties->get_keys_of_type(appender_type)) { |
| std::string appender_name = appender_key.substr(appender_type.length() + 1); |
| std::string appender_type; |
| if (!logger_properties->get(appender_key, appender_type)) { |
| appender_type = "stderr"; |
| } |
| std::transform(appender_type.begin(), appender_type.end(), appender_type.begin(), ::tolower); |
| |
| if ("nullappender" == appender_type || "null appender" == appender_type || "null" == appender_type) { |
| sink_map[appender_name] = std::make_shared<spdlog::sinks::null_sink_st>(); |
| } else if ("rollingappender" == appender_type || "rolling appender" == appender_type || "rolling" == appender_type) { |
| std::string file_name; |
| if (!logger_properties->get(appender_key + ".file_name", file_name)) { |
| file_name = "minifi-app.log"; |
| } |
| std::string directory; |
| if (!logger_properties->get(appender_key + ".directory", directory)) { |
| // The below part assumes logger_properties->getHome() is existing |
| // Cause minifiHome must be set at MiNiFiMain.cpp? |
| directory = logger_properties->getHome() + utils::file::FileUtils::get_separator() + "logs"; |
| } |
| |
| if (utils::file::FileUtils::create_dir(directory) == -1) { |
| std::cerr << directory << " cannot be created\n"; |
| exit(1); |
| } |
| file_name = directory + utils::file::FileUtils::get_separator() + file_name; |
| |
| int max_files = 3; |
| std::string max_files_str = ""; |
| if (logger_properties->get(appender_key + ".max_files", max_files_str)) { |
| try { |
| max_files = std::stoi(max_files_str); |
| } catch (const std::invalid_argument &) { |
| } catch (const std::out_of_range &) { |
| } |
| } |
| |
| int max_file_size = 5 * 1024 * 1024; |
| std::string max_file_size_str = ""; |
| if (logger_properties->get(appender_key + ".max_file_size", max_file_size_str)) { |
| try { |
| max_file_size = std::stoi(max_file_size_str); |
| } catch (const std::invalid_argument &) { |
| } catch (const std::out_of_range &) { |
| } |
| } |
| sink_map[appender_name] = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(file_name, max_file_size, max_files); |
| } else if ("stdout" == appender_type) { |
| sink_map[appender_name] = spdlog::sinks::stdout_sink_mt::instance(); |
| } else if ("stderr" == appender_type) { |
| sink_map[appender_name] = spdlog::sinks::stderr_sink_mt::instance(); |
| } else if ("syslog" == appender_type) { |
| sink_map[appender_name] = LoggerConfiguration::create_syslog_sink(); |
| } else { |
| sink_map[appender_name] = LoggerConfiguration::create_fallback_sink(); |
| } |
| } |
| |
| std::shared_ptr<internal::LoggerNamespace> root_namespace = std::make_shared<internal::LoggerNamespace>(); |
| std::string logger_type = "logger"; |
| for (auto const & logger_key : logger_properties->get_keys_of_type(logger_type)) { |
| std::string logger_def; |
| if (!logger_properties->get(logger_key, logger_def)) { |
| continue; |
| } |
| bool first = true; |
| spdlog::level::level_enum level = spdlog::level::info; |
| std::vector<std::shared_ptr<spdlog::sinks::sink>> sinks; |
| for (auto const & segment : utils::StringUtils::split(logger_def, ",")) { |
| std::string level_name = utils::StringUtils::trim(segment); |
| if (first) { |
| first = false; |
| std::transform(level_name.begin(), level_name.end(), level_name.begin(), ::tolower); |
| if ("trace" == level_name) { |
| level = spdlog::level::trace; |
| } else if ("debug" == level_name) { |
| level = spdlog::level::debug; |
| } else if ("warn" == level_name) { |
| level = spdlog::level::warn; |
| } else if ("critical" == level_name) { |
| level = spdlog::level::critical; |
| } else if ("error" == level_name) { |
| level = spdlog::level::err; |
| } else if ("off" == level_name) { |
| level = spdlog::level::off; |
| } |
| } else { |
| sinks.push_back(sink_map[level_name]); |
| } |
| } |
| std::shared_ptr<internal::LoggerNamespace> current_namespace = root_namespace; |
| if (logger_key != "logger.root") { |
| for (auto const & name : utils::StringUtils::split(logger_key.substr(logger_type.length() + 1, logger_key.length() - logger_type.length()), "::")) { |
| auto child_pair = current_namespace->children.find(name); |
| std::shared_ptr<internal::LoggerNamespace> child; |
| if (child_pair == current_namespace->children.end()) { |
| child = std::make_shared<internal::LoggerNamespace>(); |
| current_namespace->children[name] = child; |
| } else { |
| child = child_pair->second; |
| } |
| current_namespace = child; |
| } |
| } |
| current_namespace->level = level; |
| current_namespace->has_level = true; |
| current_namespace->sinks = sinks; |
| } |
| return root_namespace; |
| } |
| |
| std::shared_ptr<spdlog::logger> LoggerConfiguration::get_logger(std::shared_ptr<Logger> logger, const std::shared_ptr<internal::LoggerNamespace> &root_namespace, const std::string &name, |
| std::shared_ptr<spdlog::formatter> formatter, bool remove_if_present) { |
| std::shared_ptr<spdlog::logger> spdlogger = spdlog::get(name); |
| if (spdlogger) { |
| if (remove_if_present) { |
| spdlog::drop(name); |
| } else { |
| return spdlogger; |
| } |
| } |
| std::shared_ptr<internal::LoggerNamespace> current_namespace = root_namespace; |
| std::vector<std::shared_ptr<spdlog::sinks::sink>> sinks = root_namespace->sinks; |
| spdlog::level::level_enum level = root_namespace->level; |
| std::string current_namespace_str = ""; |
| std::string sink_namespace_str = "root"; |
| std::string level_namespace_str = "root"; |
| for (auto const & name_segment : utils::StringUtils::split(name, "::")) { |
| current_namespace_str += name_segment; |
| auto child_pair = current_namespace->children.find(name_segment); |
| if (child_pair == current_namespace->children.end()) { |
| break; |
| } |
| current_namespace = child_pair->second; |
| if (current_namespace->sinks.size() > 0) { |
| sinks = current_namespace->sinks; |
| sink_namespace_str = current_namespace_str; |
| } |
| if (current_namespace->has_level) { |
| level = current_namespace->level; |
| level_namespace_str = current_namespace_str; |
| } |
| current_namespace_str += "::"; |
| } |
| if (logger != nullptr) { |
| logger->log_debug("%s logger got sinks from namespace %s and level %s from namespace %s", name, sink_namespace_str, spdlog::level::level_names[level], level_namespace_str); |
| } |
| spdlogger = std::make_shared<spdlog::logger>(name, begin(sinks), end(sinks)); |
| spdlogger->set_level(level); |
| spdlogger->set_formatter(formatter); |
| spdlogger->flush_on(std::max(spdlog::level::info, current_namespace->level)); |
| try { |
| spdlog::register_logger(spdlogger); |
| } catch (const spdlog::spdlog_ex &) { |
| // Ignore as someone else beat us to registration, we should get the one they made below |
| } |
| return spdlog::get(name); |
| } |
| |
| std::shared_ptr<spdlog::sinks::sink> LoggerConfiguration::create_syslog_sink() { |
| #ifdef WIN32 |
| return std::make_shared<internal::windowseventlog_sink>("ApacheNiFiMiNiFi"); |
| #else |
| return std::make_shared<spdlog::sinks::syslog_sink>("ApacheNiFiMiNiFi"); |
| #endif |
| } |
| |
| std::shared_ptr<spdlog::sinks::sink> LoggerConfiguration::create_fallback_sink() { |
| if (utils::Environment::isRunningAsService()) { |
| return LoggerConfiguration::create_syslog_sink(); |
| } else { |
| return spdlog::sinks::stderr_sink_mt::instance(); |
| } |
| } |
| |
| std::shared_ptr<internal::LoggerNamespace> LoggerConfiguration::create_default_root() { |
| std::shared_ptr<internal::LoggerNamespace> result = std::make_shared<internal::LoggerNamespace>(); |
| result->sinks = std::vector<std::shared_ptr<spdlog::sinks::sink>>(); |
| result->sinks.push_back(spdlog::sinks::stderr_sink_mt::instance()); |
| result->level = spdlog::level::info; |
| return result; |
| } |
| |
| } /* namespace logging */ |
| } /* namespace core */ |
| } /* namespace minifi */ |
| } /* namespace nifi */ |
| } /* namespace apache */ |
| } /* namespace org */ |