// // format.cpp // logger // // Created by Sam Jaffe on 8/21/16. // #include "logger/format.h" #include #include #include #include #include #include #include #include #include "common.h" #include "logger/exception.h" #include "logger/logger.h" #include "logger/logpacket.h" namespace logging { std::string fmt_time_with_milis(struct timeval, std::string const &); string_generator parse_date_format_string(char const *); #define is( chr ) *next == chr #define is_string( str ) ! strncmp(next, str, strlen(str)) string_generator date_token(char const * next) { std::string predef_format = "%%Y-%%m-%%d %%H:%%M:%%S,%.03d"; if (is_string("{ISO8601}")) { predef_format = "%%Y-%%m-%%dT%%H:%%M:%%S.%.03dZ"; } else if (is_string("{ABSOLUTE}")) { predef_format = "%%H:%%M:%%S,%.03d"; } else if (is_string("{DATE}")) { predef_format = "%%d %%b %%Y %%H:%%M:%%S,%.03d"; } else if (is('{')) { return parse_date_format_string(next+1); } return [=](logpacket const & lp ){ return fmt_time_with_milis(lp.time, predef_format); }; } string_generator string_token(std::string str) { return [=](logpacket const &){ return str; }; } string_generator handle( char const * & next ) { if (is('c')) { return [](logpacket const & lp){ return lp.logger; }; } else if (is('p')) { return [](logpacket const & lp){ return to_string(lp.level, true); }; } else if (is('m')) { return [](logpacket const & lp){ return lp.message.str(); }; } else if (is('t')) { return [](logpacket const & lp){ return "???"; }; } 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 == '-'; auto align = is_left ? std::left : std::right; if (is_left) ++next; auto bounds = get_bounds(next); string_generator gen = handle(next); return [=](logpacket const & lp, std::ostream & out) { std::string str = gen(lp); if (str.length() > bounds.second) str.erase(str.begin()+bounds.second, str.end()); out << align << std::setw(bounds.first) << str; }; } format::generator convert( string_generator gen ) { return [=](logpacket const & lp, std::ostream & out) { out << gen( lp ); }; } 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) { out.gen.push_back(convert(string_token({curr, next - 1}))); } if (is('d')) { ++next; out.gen.push_back(convert(date_token(next))); if (is('{')) next = std::strchr(next, '}'); } else if (is('n')) { out.gen.push_back(convert(string_token(NEWLINE_TOKEN))); } else if (is('%')) { out.gen.push_back(convert(string_token("%"))); } else if (is('.') || is('-') || isdigit( *next )) { out.gen.push_back(parse_with_bounds(next)); } else { out.gen.push_back(convert(handle(next))); } curr = ++next; } if (curr < end) { out.gen.push_back(convert(string_token({curr, end}))); } return out; } #undef is void format::process(logpacket const & pkt, std::ostream & os) const { for (auto func : gen) { func(pkt, os); } } std::string format::process(logpacket const & pkt) const { std::stringstream ss; process(pkt, ss); return ss.str(); } void format_msg(std::ostream & os, std::string const & interp, size_t pos, std::vector const & objs, size_t idx) { size_t next = interp.find("{}", pos); os << interp.substr(pos, next); if (next == std::string::npos) { return; // throw? } else if (!strncmp(interp.c_str() + next - 1, "{{}}", 4)) { format_msg(os, interp, next+2, objs, idx); } else { format_msg(os << objs[idx], interp, next+2, objs, idx+1); } } std::string message::str() const { std::stringstream ss; format_msg(ss, format_code, 0, objects, 0); return ss.str(); } Json::Value message::json() const { return objects[0].json(); } }