/* * This file is part of PowerDNS or dnsdist. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include #include #include #include #include #include #include "config.h" #if !defined(RECURSOR) #include #else #include "logger.hh" #endif // RECURSOR /* This file is intended not to be metronome specific, and is simple example of C++2011 variadic templates in action. The goal is rapid easy to use logging to console & syslog. Usage: string address="localhost"; vinfolog("Got TCP connection from %s", remote); infolog("Bound to %s port %d", address, port); warnlog("Query took %d milliseconds", 1232.4); // yes, %d errlog("Unable to bind to %s: %s", ca.toStringWithPort(), strerr(errno)); Will log to stdout. Will syslog in any case with LOG_INFO, LOG_WARNING, LOG_ERR respectively. If g_verbose=false, vinfolog is a noop. More generically, dolog(someiostream, "Hello %s", stream) will log to someiostream This will happily print a string to %d! Doesn't do further format processing. */ template inline void dolog(O& outputStream, const char* str) { outputStream << str; } template void dolog(O& outputStream, const char* formatStr, T value, const Args&... args) { while (*formatStr) { if (*formatStr == '%') { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) if (*(formatStr + 1) == '%') { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) ++formatStr; } else { outputStream << value; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) formatStr += 2; dolog(outputStream, formatStr, args...); return; } } // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) outputStream << *formatStr++; } } #if !defined(RECURSOR) // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern bool g_verbose; #ifdef DNSDIST namespace dnsdist::logging { class LoggingConfiguration { public: enum class TimeFormat { Numeric, ISO8601 }; static void setVerbose(bool value = true) { g_verbose = value; } static void setSyslog(bool value = true) { s_syslog = value; } static void setStructuredLogging(bool value = true, std::string levelPrefix = "") { s_structuredLogging = value; if (value) { s_structuredLevelPrefix = levelPrefix.empty() ? "prio" : std::move(levelPrefix); } } static void setLogTimestamps(bool value = true) { s_logTimestamps = value; } static void setStructuredTimeFormat(TimeFormat format) { s_structuredTimeFormat = format; } static void setVerboseStream(std::ofstream&& stream) { s_verboseStream = std::move(stream); } static bool getVerbose() { return g_verbose; } static bool getSyslog() { return s_syslog; } static bool getLogTimestamps() { return s_logTimestamps; } static std::optional& getVerboseStream() { return s_verboseStream; } static bool getStructuredLogging() { return s_structuredLogging; } static const std::string& getStructuredLoggingLevelPrefix() { return s_structuredLevelPrefix; } static TimeFormat getStructuredLoggingTimeFormat() { return s_structuredTimeFormat; } private: static std::optional s_verboseStream; static std::string s_structuredLevelPrefix; static TimeFormat s_structuredTimeFormat; static bool s_structuredLogging; static bool s_logTimestamps; static bool s_syslog; }; extern void logTime(std::ostream& stream); } #endif inline void setSyslogFacility(int facility) { /* we always call openlog() right away at startup */ closelog(); openlog("dnsdist", LOG_PID | LOG_NDELAY, facility); } namespace { inline const char* syslogLevelToStr(int level) { static constexpr std::array levelStrs{ "Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Info", "Debug"}; return levelStrs.at(level); } } template void genlog(std::ostream& stream, [[maybe_unused]] int level, [[maybe_unused]] bool skipSyslog, const char* formatStr, const Args&... args) { std::ostringstream str; dolog(str, formatStr, args...); auto output = str.str(); #ifdef DNSDIST if (!skipSyslog && dnsdist::logging::LoggingConfiguration::getSyslog()) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): syslog is what it is syslog(level, "%s", output.c_str()); } if (dnsdist::logging::LoggingConfiguration::getLogTimestamps()) { dnsdist::logging::logTime(stream); } if (dnsdist::logging::LoggingConfiguration::getStructuredLogging()) { stream << dnsdist::logging::LoggingConfiguration::getStructuredLoggingLevelPrefix() << "=\"" << syslogLevelToStr(level) << "\" "; stream << "msg=" << std::quoted(output) << std::endl; } else { stream << output << std::endl; } #else stream << output << std::endl; #endif } template void verboselog(const char* formatStr, const Args&... args) { #ifdef DNSDIST if (auto& stream = dnsdist::logging::LoggingConfiguration::getVerboseStream()) { genlog(*stream, LOG_DEBUG, true, formatStr, args...); } else { #endif /* DNSDIST */ genlog(std::cout, LOG_DEBUG, false, formatStr, args...); #ifdef DNSDIST } #endif /* DNSDIST */ } #define vinfolog \ if (g_verbose) \ verboselog template void infolog(const char* formatStr, const Args&... args) { genlog(std::cout, LOG_INFO, false, formatStr, args...); } template void warnlog(const char* formatStr, const Args&... args) { genlog(std::cout, LOG_WARNING, false, formatStr, args...); } template void errlog(const char* formatStr, const Args&... args) { genlog(std::cout, LOG_ERR, false, formatStr, args...); } #else // RECURSOR #define g_verbose 0 #define vinfolog \ if (g_verbose) \ infolog template void infolog(const char* formatStr, const Args&... args) { g_log << Logger::Info; dolog(g_log, formatStr, args...); } template void warnlog(const char* formatStr, const Args&... args) { g_log << Logger::Warning; dolog(g_log, formatStr, args...); } template void errlog(const char* formatStr, const Args&... args) { g_log << Logger::Error; dolog(g_log, formatStr, args...); } #endif