/*
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"
#include "Terminal.hpp"
namespace ousia {
/* Class Logger */
void Logger::log(Severity severity, const std::string &msg,
const SourceLocation &loc,
MessageMode mode = MessageMode::DEFAULT)
{
// Assemble the message and pass it through the filter, then process it
Message message{severity, std::move(msg), loc, mode};
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)
{
calls.emplace_back(CallType : SET_LOCATION, locations.size());
locations.push_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() = loc;
} 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 ScopedLogger */
ScopedLogger::ScopedLogger(Logger &parent, SourceLocation loc = SourceLocation{})
: Logger(), parent(parent), depth(0)
{
pushDefaultLocation(loc);
}
ScopedLogger::~ScopedLogger()
{
while (depth > 0) {
popDefaultLocation();
}
}
void ScopedLogger::processMessage(const Message &msg)
{
parent.processMessage(msg);
}
bool ScopedLogger::filterMessage(const Message &msg)
{
return parent.filterMessage(msg);
}
void ScopedLogger::processPushDefaultLocation(const SourceLocation &loc)
{
parent.processPushDefaultLocation(loc);
depth++;
}
void ScopedLogger::processPopDefaultLocation()
{
depth--;
parent.processPopDefaultLocation();
}
void ScopedLogger::processSetDefaultLocation(const SourceLocation &loc)
{
parent.processSetDefaultLocation(loc);
}
void ScopedLogger::processSetSourceContextCallback(
SourceContextCallback sourceContextCallback)
{
parent.processSetContextCallback(sourceContextCallback);
}
/* Class ConcreteLogger */
ConcreteLogger(Severity minSeverity = Severity::DEFAULT_MIN_SEVERITY)
: 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);
}
}
const SourceLocation &ConcreteLogger::messageLocation(const Message &msg) const
{
if (msg.loc.valid()) {
return msg.loc;
} else if (!locatios.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()
{
files.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;
}
/* Class TerminalLogger */
void TerminalLogger::processMessage(const Message &msg)
{
Terminal t(useColor);
// Fetch filename, position and context
const SourceContext ctx = messageContext(msg);
// Print the file name
if (ctx.hasFile()) {
os << t.bright() << ctx.filename << t.reset();
}
// Print line and column number
if (ctx.hasLine()) {
if (hasFile) {
os << ':';
}
os << t.bright() << ctx.startLine << t.reset();
if (ctx.hasColumn()) {
os << ':' << ctx.startColumn;
}
}
// Print the optional seperator
if (ctx.hasFile() || ctx.hasLine()) {
os << ": ";
}
// Print the severity
switch (msg.severity) {
case Severity::DEBUG:
break;
case Severity::NOTE:
os << t.color(Terminal::CYAN, true) << "note: ";
break;
case Severity::WARNING:
os << t.color(Terminal::MAGENTA, true) << "warning: ";
break;
case Severity::ERROR:
os << t.color(Terminal::RED, true) << "error: ";
break;
case Severity::FATAL_ERROR:
os << t.color(Terminal::RED, true) << "fatal error: ";
break;
}
os << t.reset();
// Print the actual message
os << msg.msg << std::endl;
// Print the error message context if available
/* if (ctx.valid()) {
size_t relPos = ctx.relPos;
if (ctx.truncatedStart) {
os << "[...] ";
}
os << ctx.text;
if (ctx.truncatedEnd) {
os << " [...]";
}
os << std::endl;
if (ctx.truncatedStart) {
os << " ";
}
for (size_t i = 0; i < relPos; i++) {
if (i < ctx.text.size() && ctx.text[i] == '\t') {
os << '\t';
} else {
os << ' ';
}
}
os << t.color(Terminal::GREEN) << '^' << t.reset() << std::endl;
}*/
}
}