/** Logging and debugging routines * * @author Steffen Vogel * @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC * @license GNU General Public License (version 3) * * VILLAScommon * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . *********************************************************************************/ #include #include #include #include #include #include using namespace villas; /** The global log instance */ Log villas::logging; Log::Log(Level lvl) : level(lvl), pattern("%H:%M:%S %^%l%$ %n: %v") { char *p = getenv("VILLAS_LOG_PREFIX"); if (p) prefix = p; sinks = std::make_shared(); setLevel(level); setPattern(pattern); /* Default sink */ sink = std::make_shared(); sinks->add_sink(sink); } int Log::getWidth() { int width = Terminal::getCols() - 50; if (!prefix.empty()) width -= prefix.length(); return width; } Logger Log::get(const std::string &name) { Logger logger = spdlog::get(name); if (not logger) { logger = std::make_shared(name, sink); logger->set_level(level); logger->set_pattern(prefix + pattern); spdlog::register_logger(logger); } return logger; } void Log::parse(json_t *cfg) { const char *level = NULL; const char *path = NULL; const char *pattern = NULL; int syslog; int ret; json_error_t err; json_t *json_expressions = nullptr; ret = json_unpack_ex(cfg, &err, JSON_STRICT, "{ s?: s, s?: s, s?: s, s?: b, s?: s }", "level", &level, "file", &path, "expressions", &json_expressions, "syslog", &syslog, "pattern", &pattern ); if (ret) throw JsonError(err); if (level) setLevel(level); if (path) { auto sink = std::make_shared(path); sinks->add_sink(sink); } if (syslog) { auto sink = std::make_shared("villas", LOG_PID, LOG_DAEMON); sinks->add_sink(sink); } if (json_expressions) { if (!json_is_array(json_expressions)) throw ConfigError(json_expressions, "node-config.html#node-config-logging-expressions", "The 'expressions' setting must be a list of objects."); size_t i; json_t *json_expression; json_array_foreach(json_expressions, i, json_expression) { const char *name; const char *lvl; ret = json_unpack_ex(json_expression, &err, JSON_STRICT, "{ s: s, s: s }", "name", &name, "level", &lvl ); if (ret) throw JsonError(err); Logger logger = get(name); auto level = spdlog::level::from_str(lvl); logger->set_level(level); } } } void Log::setPattern(const std::string &pat) { pattern = pat; spdlog::set_pattern(pattern, spdlog::pattern_time_type::utc); //sinks.set_pattern(pattern); } void Log::setLevel(Level lvl) { level = lvl; spdlog::set_level(lvl); //sinks.set_level(lvl); } void Log::setLevel(const std::string &lvl) { std::list l = SPDLOG_LEVEL_NAMES; auto it = std::find(l.begin(), l.end(), lvl); if (it == l.end()) throw RuntimeError("Invalid log level {}", lvl); setLevel(spdlog::level::from_str(lvl)); } Log::Level Log::getLevel() const { return level; } std::string Log::getLevelName() const { return std::string(spdlog::level::to_c_str(level)); }