// // format.cpp // logger // // Created by Sam Jaffe on 8/21/16. // #include #include #include #include #include #include #include #include #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(); } else if (is('p')) { return std::make_shared(); } else if (is('m')) { return std::make_shared(); } else if (is('t')) { return std::make_shared("???"); } else if (is('F') || is('M') || is('L')) { return std::make_shared(*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 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(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(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(NEWLINE_TOKEN)); } else if (is('%')) { out.gen.push_back(std::make_shared("%")); } 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(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(); } }