/* Ousía Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel 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 #include #include "Logger.hpp" namespace ousia { /* Class Logger */ void Logger::log(Severity severity, const std::string &msg, const SourceLocation &loc, MessageMode mode) { // Assemble the message and pass it through the filter, then process it Message message{severity, mode, std::move(msg), loc}; if (filterMessage(message)) { processMessage(message); } } LoggerFork Logger::fork() { return LoggerFork(this); } /* Class LoggerFork */ void LoggerFork::processMessage(const Message &msg) { calls.emplace_back(CallType::MESSAGE, messages.size()); messages.push_back(msg); } void LoggerFork::processPushDefaultLocation(const SourceLocation &loc) { calls.emplace_back(CallType::PUSH_LOCATION, locations.size()); locations.push_back(loc); } void LoggerFork::processPopDefaultLocation() { calls.emplace_back(CallType::POP_LOCATION, 0); } void LoggerFork::processSetDefaultLocation(const SourceLocation &loc) { // Check whether setDefaultLocation was called immediately before, if yes, // simply override the data if (!calls.empty() && calls.back().type == CallType::SET_LOCATION) { locations.back() = loc; } else { calls.emplace_back(CallType::SET_LOCATION, locations.size()); locations.emplace_back(loc); } } void LoggerFork::processSetSourceContextCallback( SourceContextCallback sourceContextCallback) { // Check whether setSourceContextCallback was called immediately before, // if yes, simply override the data if (!calls.empty() && calls.back().type == CallType::SET_CONTEXT_CALLBACK) { callbacks.back() = sourceContextCallback; } else { calls.emplace_back(CallType::SET_CONTEXT_CALLBACK, callbacks.size()); callbacks.emplace_back(sourceContextCallback); } } void LoggerFork::purge() { calls.clear(); messages.clear(); locations.clear(); callbacks.clear(); } void LoggerFork::commit() { for (const Call &call : calls) { switch (call.type) { case CallType::MESSAGE: if (parent->filterMessage(messages[call.dataIdx])) { parent->processMessage(messages[call.dataIdx]); } break; case CallType::PUSH_LOCATION: parent->processPushDefaultLocation(locations[call.dataIdx]); break; case CallType::POP_LOCATION: parent->processPopDefaultLocation(); break; case CallType::SET_LOCATION: parent->processSetDefaultLocation(locations[call.dataIdx]); break; case CallType::SET_CONTEXT_CALLBACK: parent->processSetSourceContextCallback( callbacks[call.dataIdx]); break; } } purge(); } /* Class GuardedLogger */ GuardedLogger::GuardedLogger(Logger &parent, SourceLocation loc) : parent(parent), depth(0) { pushDefaultLocation(loc); } GuardedLogger::~GuardedLogger() { while (depth > 0) { popDefaultLocation(); } } void GuardedLogger::processMessage(const Message &msg) { parent.processMessage(msg); } bool GuardedLogger::filterMessage(const Message &msg) { return parent.filterMessage(msg); } void GuardedLogger::processPushDefaultLocation(const SourceLocation &loc) { parent.processPushDefaultLocation(loc); depth++; } void GuardedLogger::processPopDefaultLocation() { depth--; parent.processPopDefaultLocation(); } void GuardedLogger::processSetDefaultLocation(const SourceLocation &loc) { parent.processSetDefaultLocation(loc); } void GuardedLogger::processSetSourceContextCallback( SourceContextCallback sourceContextCallback) { parent.processSetSourceContextCallback(sourceContextCallback); } /* Class ConcreteLogger */ ConcreteLogger::ConcreteLogger(Severity minSeverity) : minSeverity(minSeverity), sourceContextCallback(NullSourceContextCallback) { } bool ConcreteLogger::filterMessage(const Message &msg) { // Increment the message count for this severity uint8_t sev = static_cast(msg.severity); if (sev >= messageCounts.size()) { messageCounts.resize(sev + 1); } messageCounts[sev]++; // Filter messages with too small severity return sev >= static_cast(minSeverity); } void ConcreteLogger::processPushDefaultLocation(const SourceLocation &loc) { locations.emplace_back(loc); } void ConcreteLogger::processPopDefaultLocation() { if (!locations.empty()) { locations.pop_back(); } } void ConcreteLogger::processSetDefaultLocation(const SourceLocation &loc) { if (!locations.empty()) { locations.back() = loc; } else { locations.emplace_back(loc); } } void ConcreteLogger::processSetSourceContextCallback( SourceContextCallback sourceContextCallback) { this->sourceContextCallback = sourceContextCallback; } const SourceLocation &ConcreteLogger::messageLocation(const Message &msg) const { if (msg.loc.isValid()) { return msg.loc; } else if (!locations.empty()) { return locations.back(); } return NullSourceLocation; } SourceContext ConcreteLogger::messageContext(const Message &msg) const { return sourceContextCallback(messageLocation(msg)); } Severity ConcreteLogger::getMaxEncounteredSeverity() { for (ssize_t i = messageCounts.size() - 1; i >= 0; i--) { if (messageCounts[i] > 0) { return static_cast(i); } } return Severity::DEBUG; } size_t ConcreteLogger::getSeverityCount(Severity severity) { uint8_t sev = static_cast(severity); if (sev >= messageCounts.size()) { return 0; } return messageCounts[sev]; } void ConcreteLogger::reset() { locations.clear(); messageCounts.clear(); } bool ConcreteLogger::hasError() { return getSeverityCount(Severity::ERROR) > 0 || getSeverityCount(Severity::FATAL_ERROR) > 0; } bool ConcreteLogger::hasFatalError() { return getSeverityCount(Severity::FATAL_ERROR) > 0; } }