/* * Copyright (c) 2013-2015, dennis wang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "color.h" #include "console_appender.h" #include "logger_impl.h" #include "util.h" thread_local static std::unique_ptr buffer_ptr_; ConsoleAppender::ConsoleAppender(LoggerImpl *impl_ptr, const std::string &name, const std::string &attribute) { if (!name.empty()) { _name = "[" + name + "]"; _realName = name; } _impl = impl_ptr; // 获取配置 parseAttribute(attribute); } ConsoleAppender::~ConsoleAppender() {} void ConsoleAppender::write(int level, const char *format, ...) { if (level < _level) { return; } va_list va_ptr; va_start(va_ptr, format); if (level == Logger::WARNING) { std::cout << blue; } else if (level == Logger::EXCEPTION) { std::cout << green; } else if (level == Logger::FAILURE) { std::cout << yellow; } else if (level == Logger::FATAL) { std::cout << red; } else if (level == Logger::DIAGNOSE) { std::cout << blue; } write(getLevelString(level), format, va_ptr); std::cout << white; va_end(va_ptr); } void ConsoleAppender::setLevel(int level) { _level = level; } int ConsoleAppender::getLevel() { return _level; } void ConsoleAppender::destroy() { // 从管理器内删除 if (_impl) { _impl->remove(_realName); } // 销毁自己 delete this; } void ConsoleAppender::write(const std::string &level, const char *format, va_list va_ptr) { if (_format == Format::JSON) { // 写入JSON格式 write_json(level, format, va_ptr); return; } std::string pattern = _pattern.getPrefix(); int len = 0; if (!buffer_ptr_) { buffer_ptr_ = std::make_unique(BUF_LEN); } auto *buffer = buffer_ptr_.get(); if (!pattern.empty()) { // 日志行前缀 std::memcpy(buffer + len, pattern.c_str(), (int)pattern.size()); len += (int)pattern.size(); } if (show_log_level_ && !level.empty()) { // 日志等级 std::memcpy(buffer + len, level.c_str(), (int)level.size()); len += (int)level.size(); } if (show_log_name_ && !_name.empty()) { // 日志名称 std::memcpy(buffer + len, _name.c_str(), (int)_name.size()); len += (int)_name.size(); } decltype(len) prefix_size = len; int bytes = std::vsnprintf(buffer + len, BUF_LEN - len, format, va_ptr); if (0 >= bytes) { return; } len += bytes; ScopeLock guard(&_lock); std::string suffix; if (_same_log_count) { auto it = _log_map.find(buffer + prefix_size); if (it != _log_map.end()) { it->second += 1; suffix.append("{").append(std::to_string(it->second)).append("}"); } else { _log_map.emplace(buffer + prefix_size, 0); } } std::fprintf(stderr, "%s%s%s", buffer, suffix.c_str(), LINE_END); if (_flush) { std::fflush(stderr); } } void ConsoleAppender::write_json(const std::string &level, const char *format, va_list va_ptr) { if (!buffer_ptr_) { buffer_ptr_.reset(new char[BUF_LEN]); } char *buffer = buffer_ptr_.get(); int bytes = std::vsnprintf(buffer, BUF_LEN - 1, format, va_ptr); if (0 >= bytes) { return; } std::string json = "{"; if (show_log_level_) { json.append("\"level\":\"").append(level).append("\","); } json.append("\"time\":\"").append(_pattern.getTime()).append("\","); if (show_log_name_) { json.append("\"logger\":\"").append(_name).append("\","); } json.append("\"message\":").append("\"").append(buffer).append("\""); json += "}"; ScopeLock guard(&_lock); std::string suffix; if (_same_log_count) { auto it = _log_map.find(json); if (it != _log_map.end()) { it->second += 1; suffix.append("{").append(std::to_string(it->second)).append("}"); } else { _log_map.emplace(json, 0); } } std::fprintf(stderr, "%s%s%s", json.c_str(), suffix.c_str(), LINE_END); if (_flush) { std::fflush(stderr); } } void ConsoleAppender::reload(const std::string &attribute) { parseAttribute(attribute); } void ConsoleAppender::show_level(bool) {} void ConsoleAppender::show_name(bool) {} void ConsoleAppender::parseAttribute(const std::string &attribute) { std::vector tokens; klogger::split(attribute, ";", tokens); std::vector::iterator guard = tokens.begin(); // 解析日志属性配置 for (; guard != tokens.end(); guard++) { if (std::string::npos != guard->find("line=")) { _line = getAttribute("line=", *guard); _pattern.parse(_line); } else if (std::string::npos != guard->find("flush=")) { std::string flush = getAttribute("flush=", *guard); _flush = (flush == "true"); } else if (std::string::npos != guard->find("level=")) { std::string level = getAttribute("level=", *guard); _level = atoi(level.c_str()); } else if (std::string::npos != guard->find("logCount=")) { auto log_count = getAttribute("logCount=", *guard); _same_log_count = (log_count == "true"); } else if (std::string::npos != guard->find("showLevel=")) { std::string attr = getAttribute("showLevel=", *guard); show_log_level_ = (attr == "true"); } else if (std::string::npos != guard->find("showName=")) { std::string attr = getAttribute("showName=", *guard); show_log_name_ = (attr == "true"); } else if (std::string::npos != guard->find("format=")) { auto format = getAttribute("format=", *guard); if (format == "json") { _format = Format::JSON; } else { _format = Format::TXT; } } } } std::string ConsoleAppender::getAttribute(const std::string &name, const std::string &attribute) { size_t pos = attribute.find(name); if (pos == std::string::npos) { throw LoggerException("invalid attribute"); } return attribute.substr(pos + name.size()); }