| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- //
- // format.cpp
- // logger
- //
- // Created by Sam Jaffe on 8/21/16.
- //
- #include <cstdint>
- #include <cstring>
- #include <ctime>
- #include <iomanip>
- #include <iostream>
- #include <map>
- #include <stdexcept>
- #include <string>
- #include "common.h"
- #include "logger/exception.h"
- #include "logger/logger.h"
- #include "logger/logpacket.h"
- #include "logger/message.h"
- #include "pattern_layout.h"
- namespace logging { namespace {
- struct log_handle_gen : public format::generator_t {
- std::string str(logpacket const & lp) const override {
- return lp.logger;
- }
- void write(logpacket const & lp, std::ostream & os) const override {
- os << lp.logger;
- }
- };
- struct level_gen : public format::generator_t {
- std::string str(logpacket const & lp) const override {
- return to_string(lp.level, true);
- }
- };
- struct location_info_gen : public format::generator_t {
- enum { method, line, file } where;
- constexpr static const decltype(where) format_codes[] = {
- ['F'] = file,
- ['M'] = method,
- ['L'] = line,
- };
- location_info_gen(char wh) : where(format_codes[wh]) {}
- std::string str(logpacket const & lp) const override {
- switch (where) {
- case file:
- return lp.info.filename;
- case line:
- return std::to_string(lp.info.line);
- case method:
- return lp.info.function;
- }
- }
- };
- struct message_gen : public format::generator_t {
- std::string str(logpacket const & lp) const override {
- return lp.message.str();
- }
- };
-
- struct literal_gen : public format::generator_t {
- literal_gen(std::string const & value) : value(value) {}
- std::string str(logpacket const & lp) const override {
- return value;
- }
- void write(logpacket const & lp, std::ostream & os) const override {
- os << value;
- }
- std::string value;
- };
-
- struct bounds_gen : public format::generator_t {
- bounds_gen(format::generator impl, bool is_left, int min, size_t max)
- : impl(impl), is_left(is_left), min(min), max(max) {}
- std::string str(logpacket const & lp) const override {
- return impl->str(lp);
- }
- void write(logpacket const & lp, std::ostream & os) const override {
- auto align = is_left ? std::left : std::right;
- std::string str = this->str(lp);
- if (str.length() > max) {
- str.erase(str.begin()+max, str.end());
- }
- os << align << std::setw(min) << str;
- }
- format::generator impl;
- bool is_left;
- int min;
- size_t max;
- };
- } }
- namespace logging {
- #define is( chr ) *next == chr
- #define is_string( str ) ! strncmp(next, str, strlen(str))
- format::generator date_token(char const * next);
- format::generator handle( char const * & next ) {
- if (is('c')) {
- return std::make_shared<log_handle_gen>();
- } else if (is('p')) {
- return std::make_shared<level_gen>();
- } else if (is('m')) {
- return std::make_shared<message_gen>();
- } else if (is('t')) {
- return std::make_shared<literal_gen>("???");
- } else if (is('F') || is('M') || is('L')) {
- return std::make_shared<location_info_gen>(*next);
- } else {
- std::string error_msg{"unknown format character: '"};
- throw unknown_format_specifier{error_msg + *next + "'"};
- }
- }
-
- int eat(char const * & token) {
- size_t chars = 0;
- int rval = std::stoi(token, &chars);
- token += chars;
- return rval;
- }
-
- std::pair<int, size_t> get_bounds(char const * & next) {
- int min = *next == '.' ? 0 : eat(next);
- size_t max = *next == '.' ? eat(++next) : std::string::npos;
- return std::make_pair(min, max);
- }
-
- format::generator parse_with_bounds(char const * & next) {
- bool const is_left = *next == '-';
- if (is_left) ++next;
-
- auto bounds = get_bounds(next);
-
- bounds_gen gen{handle(next), is_left, bounds.first, bounds.second};
- return std::make_shared<bounds_gen>(gen);
- }
-
- format format::parse_format_string( std::string const & fmt ) {
- format out;
- char const * curr = fmt.c_str();
- char const * next = nullptr;
- char const * const end = curr + fmt.size();
-
- while ((next = std::strchr(curr, '%')) != nullptr) {
- ++next;
- if (end == next) {
- std::string error_msg{"expected format specifier, got end of string"};
- throw format_parsing_exception{error_msg}; // TODO
- }
-
- if (curr < next-1) {
- std::string lit{curr, next - 1};
- out.gen.push_back(std::make_shared<literal_gen>(lit));
- }
- if (is('d')) {
- ++next;
- out.gen.push_back(date_token(next));
- if (is('{')) next = std::strchr(next, '}');
- } else if (is('n')) {
- out.gen.push_back(std::make_shared<literal_gen>(NEWLINE_TOKEN));
- } else if (is('%')) {
- out.gen.push_back(std::make_shared<literal_gen>("%"));
- } else if (is('.') || is('-') || isdigit( *next )) {
- out.gen.push_back(parse_with_bounds(next));
- } else {
- out.gen.push_back(handle(next));
- }
- curr = ++next;
- }
- if (curr < end) {
- std::string lit{curr, end};
- out.gen.push_back(std::make_shared<literal_gen>(lit));
- }
- return out;
- }
- #undef is
-
- void format::process(logpacket const & pkt, std::ostream & os) const {
- for (auto func : gen) { func->write(pkt, os); }
- }
-
- std::string format::process(logpacket const & pkt) const {
- std::stringstream ss;
- process(pkt, ss);
- return ss.str();
- }
- }
|