//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Achim Brandt /// @author Dr. Frank Celler //////////////////////////////////////////////////////////////////////////////// #include "LogAppender.h" #include "ApplicationFeatures/ShellColorsFeature.h" #include "Basics/MutexLocker.h" #include "Basics/StringUtils.h" #include "Logger/LogAppenderFile.h" #include "Logger/LogAppenderSyslog.h" #include "Logger/Logger.h" using namespace arangodb; using namespace arangodb::basics; arangodb::Mutex LogAppender::_appendersLock; std::map>> LogAppender::_topics2appenders; std::map, std::shared_ptr> LogAppender::_definition2appenders; std::vector> LogAppender::_loggers; bool LogAppender::_allowStdLogging = true; void LogAppender::addLogger(std::function func) { MUTEX_LOCKER(guard, _appendersLock); // to silence TSan _loggers.emplace_back(func); } void LogAppender::addAppender(std::string const& definition, std::string const& filter) { MUTEX_LOCKER(guard, _appendersLock); std::pair, LogTopic*> appender( buildAppender(definition, filter)); if (appender.first == nullptr) { return; } LogTopic* topic = appender.second; size_t n = (topic == nullptr) ? LogTopic::MAX_LOG_TOPICS : topic->id(); if (std::find(_topics2appenders[n].begin(), _topics2appenders[n].end(), appender.first) == _topics2appenders[n].end()) { _topics2appenders[n].emplace_back(appender.first); } } std::pair, LogTopic*> LogAppender::buildAppender( std::string const& definition, std::string const& filter) { std::vector v = StringUtils::split(definition, '=', '\0'); std::string topicName; std::string output; std::string contentFilter; if (v.size() == 1) { output = v[0]; contentFilter = filter; } else if (v.size() == 2) { topicName = StringUtils::tolower(v[0]); if (topicName.empty()) { output = v[0]; contentFilter = filter; } else { output = v[1]; } } else { LOG_TOPIC("32068", ERR, arangodb::Logger::FIXME) << "strange output definition '" << definition << "' ignored"; return {nullptr, nullptr}; } LogTopic* topic = nullptr; if (!topicName.empty()) { topic = LogTopic::lookup(topicName); if (topic == nullptr) { LOG_TOPIC("a1098", ERR, arangodb::Logger::FIXME) << "strange topic '" << topicName << "', ignoring whole defintion"; return {nullptr, nullptr}; } } auto key = std::make_pair(output, contentFilter); #ifdef ARANGODB_ENABLE_SYSLOG if (StringUtils::isPrefix(output, "syslog://")) { key = std::make_pair("syslog://", ""); } #endif auto it = _definition2appenders.find(key); if (it != _definition2appenders.end()) { return {it->second, topic}; } #ifdef ARANGODB_ENABLE_SYSLOG // first handle syslog-logging if (StringUtils::isPrefix(output, "syslog://")) { auto s = StringUtils::split(output.substr(9), '/'); if (s.size() < 1 || s.size() > 2) { LOG_TOPIC("70194", ERR, arangodb::Logger::FIXME) << "unknown syslog definition '" << output << "', expecting " << "'syslog://facility/identifier'"; return {nullptr, nullptr}; } std::string identifier = ""; if (s.size() == 2) { identifier = s[1]; } auto result = std::make_shared(s[0], identifier, contentFilter); _definition2appenders[key] = result; return {result, topic}; } #endif if (output == "+" || output == "-") { for (auto const& it : _definition2appenders) { if (it.first.first == "+" || it.first.first == "-") { // alreay got a logger for stderr/stdout return {nullptr, nullptr}; } } } // everything else must be file-/stream-based logging std::shared_ptr result; if (output == "+") { result.reset(new LogAppenderStderr(contentFilter)); } else if (output == "-") { result.reset(new LogAppenderStdout(contentFilter)); } else if (StringUtils::isPrefix(output, "file://")) { result.reset(new LogAppenderFile(output.substr(7), contentFilter)); } else { LOG_TOPIC("ca950", ERR, arangodb::Logger::FIXME) << "unknown output definition '" << output << "'"; return {nullptr, nullptr}; } try { _definition2appenders[key] = result; return {result, topic}; } catch (...) { // cannot open file for logging return {nullptr, nullptr}; } } void LogAppender::log(LogMessage* message) { LogLevel level = message->_level; size_t topicId = message->_topicId; std::string const& m = message->_message; size_t offset = message->_offset; MUTEX_LOCKER(guard, _appendersLock); // output to appender auto output = [&level, &m, &offset](size_t n) -> bool { auto const& it = _topics2appenders.find(n); bool shown = false; if (it != _topics2appenders.end()) { auto const& appenders = it->second; for (auto const& appender : appenders) { if (appender->checkContent(m)) { appender->logMessage(level, m, offset); } shown = true; } } return shown; }; bool shown = false; // try to find a specific topic if (topicId < LogTopic::MAX_LOG_TOPICS) { shown = output(topicId); } // otherwise use the general topic if (!shown) { output(LogTopic::MAX_LOG_TOPICS); } for (auto const& logger : _loggers) { logger(message); } } void LogAppender::shutdown() { MUTEX_LOCKER(guard, _appendersLock); LogAppenderSyslog::close(); LogAppenderFile::closeAll(); _topics2appenders.clear(); _definition2appenders.clear(); } void LogAppender::reopen() { MUTEX_LOCKER(guard, _appendersLock); LogAppenderFile::reopenAll(); }