/* * This file is part of the Flowee project * Copyright (C) 2017-2022 Tom Zander * * 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 * (at your option) 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 "Logger.h" #include "LogChannels_p.h" #include #include namespace { std::string shortenMethod(const char *methodName) { assert(methodName); const char *start = strchr(methodName, ' '); const char *end = strchr(methodName, '('); if (end) { if (!start || start > end) start = methodName; else ++start; ++end; std::string copy(start, end - start); return copy; } return std::string(); } } Log::Channel::Channel(TimeStampFormat f) : m_timeStampFormat(f), m_logSubSecondPrecision(true) { } Log::Channel::TimeStampFormat Log::Channel::timeStampFormat() const { return m_timeStampFormat; } void Log::Channel::setTimeStampFormat(const TimeStampFormat &timeStampFormat) { m_timeStampFormat = timeStampFormat; } bool Log::Channel::logSubSecondPrecision() const { return m_logSubSecondPrecision; } void Log::Channel::setlogSubSecondPrecision(bool showSubSecondPrecision) { m_logSubSecondPrecision = showSubSecondPrecision; } // ------------------------------------------------------ ConfigurableChannel::ConfigurableChannel(TimeStampFormat f) : Channel(f), m_timeStampFormat(f), m_logSection(true), m_logLineNumber(false), m_logMethodName(true), m_logFilename(false) { } bool ConfigurableChannel::logSection() const { return m_logSection; } void ConfigurableChannel::setLogSection(bool printSection) { m_logSection = printSection; } bool ConfigurableChannel::logLineNumber() const { return m_logLineNumber; } void ConfigurableChannel::setLogLineNumber(bool printLineNumber) { m_logLineNumber = printLineNumber; } bool ConfigurableChannel::logMethodName() const { return m_logMethodName; } void ConfigurableChannel::setLogMethodName(bool printMethodName) { m_logMethodName = printMethodName; } bool ConfigurableChannel::logFilename() const { return m_logFilename; } void ConfigurableChannel::setLogFilename(bool printFilename) { m_logFilename = printFilename; } // ------------------------------------------------------ ConsoleLogChannel::ConsoleLogChannel() : ConfigurableChannel(TimeOnly) { } void ConsoleLogChannel::setPrefix(const char *prefix) { m_prefix = prefix; } void ConsoleLogChannel::pushLog(int64_t, std::string *timestamp, const std::string &line, const char *filename, int lineNumber, const char *methodName, short logSection, short logLevel) { std::ostream &out = (logLevel == Log::WarningLevel || logLevel == Log::FatalLevel) ? std::clog : std::cout; if (timestamp) out << *timestamp << ' '; if (m_logSection && logSection) { out << '['; const std::string section = Log::Manager::sectionString(logSection); if (!section.empty()) out << section; else out << logSection; out << "] "; } if (m_prefix) out << m_prefix << ' '; if (m_logFilename && filename) out << filename << (m_logLineNumber ? ':' : ' '); if (m_logLineNumber && lineNumber) out << lineNumber << ';'; if (m_logMethodName && methodName) { std::string m(shortenMethod(methodName)); if (!m.empty()) out << m << ") "; } out << line; if (line.empty() || line.back() != '\n') out << std::endl; out.flush(); } FileLogChannel::FileLogChannel(const boost::filesystem::path &logFilename) : ConfigurableChannel(DateTime), m_fileout(0), m_logFilename(logFilename) { } FileLogChannel::~FileLogChannel() { if (m_fileout) fclose(m_fileout); } static void FileWriteStr(const std::string &str, FILE *fp) { fwrite(str.data(), 1, str.size(), fp); } void FileLogChannel::pushLog(int64_t, std::string *timestamp, const std::string &line, const char*, int, const char *methodName, short logSection, short) { if (m_fileout) { if (timestamp) { FileWriteStr(*timestamp, m_fileout); FileWriteStr(" ", m_fileout); } if (m_logSection && logSection) { FileWriteStr("[", m_fileout); const std::string section = Log::Manager::sectionString(logSection); if (section.empty()) { std::ostringstream num; num << logSection; FileWriteStr(num.str() , m_fileout); } else { FileWriteStr(section , m_fileout); } FileWriteStr("] ", m_fileout); } if (m_logMethodName && methodName) { std::string m(shortenMethod(methodName)); if (!m.empty()) { FileWriteStr(m , m_fileout); FileWriteStr(") " , m_fileout); } } FileWriteStr(line, m_fileout); if (line.empty() || line.back() != '\n') FileWriteStr("\n", m_fileout); } } void FileLogChannel::reopenLogFiles() { if (m_fileout) fclose(m_fileout); if (m_logFilename.empty()) return; boost::system::error_code error; boost::filesystem::create_directories(m_logFilename.parent_path(), error); m_fileout = fopen(m_logFilename.string().c_str(), "a"); if (!m_fileout) throw std::runtime_error(std::string("Failed to open(append) file: ") + m_logFilename.string()); setbuf(m_fileout, NULL); // unbuffered } void FileLogChannel::setPath(const std::string &path) { if (!m_logFilename.empty() && boost::filesystem::is_directory(path)) // interpret as dir, append previous filename. m_logFilename = boost::filesystem::path(path) / m_logFilename.filename(); else m_logFilename = path; }