format.cxx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. //
  2. // format.cpp
  3. // logger
  4. //
  5. // Created by Sam Jaffe on 8/21/16.
  6. //
  7. #include "logger/format.h"
  8. #include <cstdint>
  9. #include <cstring>
  10. #include <ctime>
  11. #include <iomanip>
  12. #include <iostream>
  13. #include <map>
  14. #include <stdexcept>
  15. #include <string>
  16. #include "common.h"
  17. #include "logger/exception.h"
  18. #include "logger/logger.h"
  19. #include "logger/logpacket.h"
  20. namespace logging {
  21. std::string fmt_time_with_milis(struct timeval, std::string const &);
  22. string_generator parse_date_format_string(char const *);
  23. #define is( chr ) *next == chr
  24. #define is_string( str ) ! strncmp(next, str, strlen(str))
  25. string_generator date_token(char const * next) {
  26. std::string predef_format = "%%Y-%%m-%%d %%H:%%M:%%S,%.03d";
  27. if (is_string("{ISO8601}")) {
  28. predef_format = "%%Y-%%m-%%dT%%H:%%M:%%S.%.03dZ";
  29. } else if (is_string("{ABSOLUTE}")) {
  30. predef_format = "%%H:%%M:%%S,%.03d";
  31. } else if (is_string("{DATE}")) {
  32. predef_format = "%%d %%b %%Y %%H:%%M:%%S,%.03d";
  33. } else if (is('{')) {
  34. return parse_date_format_string(next+1);
  35. }
  36. return [=](logpacket const & lp ){
  37. return fmt_time_with_milis(lp.time, predef_format);
  38. };
  39. }
  40. string_generator string_token(std::string str) {
  41. return [=](logpacket const &){ return str; };
  42. }
  43. string_generator handle( char const * & next ) {
  44. if (is('c')) {
  45. return [](logpacket const & lp){ return lp.logger; };
  46. } else if (is('p')) {
  47. return [](logpacket const & lp){ return to_string(lp.level, true); };
  48. } else if (is('m')) {
  49. return [](logpacket const & lp){ return lp.message.str(); };
  50. } else if (is('t')) {
  51. return [](logpacket const & lp){ return "???"; };
  52. } else {
  53. std::string error_msg{"unknown format character: '"};
  54. throw unknown_format_specifier{error_msg + *next + "'"};
  55. }
  56. }
  57. int eat(char const * & token) {
  58. size_t chars = 0;
  59. int rval = std::stoi(token, &chars);
  60. token += chars;
  61. return rval;
  62. }
  63. std::pair<int, size_t> get_bounds(char const * & next) {
  64. int min = *next == '.' ? 0 : eat(next);
  65. size_t max = *next == '.' ? eat(++next) : std::string::npos;
  66. return std::make_pair(min, max);
  67. }
  68. format::generator parse_with_bounds(char const * & next) {
  69. bool const is_left = *next == '-';
  70. auto align = is_left ? std::left : std::right;
  71. if (is_left) ++next;
  72. auto bounds = get_bounds(next);
  73. string_generator gen = handle(next);
  74. return [=](logpacket const & lp, std::ostream & out) {
  75. std::string str = gen(lp);
  76. if (str.length() > bounds.second)
  77. str.erase(str.begin()+bounds.second, str.end());
  78. out << align << std::setw(bounds.first) << str;
  79. };
  80. }
  81. format::generator convert( string_generator gen ) {
  82. return [=](logpacket const & lp, std::ostream & out) {
  83. out << gen( lp );
  84. };
  85. }
  86. format format::parse_format_string( std::string const & fmt ) {
  87. format out;
  88. char const * curr = fmt.c_str();
  89. char const * next = nullptr;
  90. char const * const end = curr + fmt.size();
  91. while ((next = std::strchr(curr, '%')) != nullptr) {
  92. ++next;
  93. if (end == next) {
  94. std::string error_msg{"expected format specifier, got end of string"};
  95. throw format_parsing_exception{error_msg}; // TODO
  96. }
  97. if (curr < next-1) {
  98. out.gen.push_back(convert(string_token({curr, next - 1})));
  99. }
  100. if (is('d')) {
  101. ++next;
  102. out.gen.push_back(convert(date_token(next)));
  103. if (is('{')) next = std::strchr(next, '}');
  104. } else if (is('n')) {
  105. out.gen.push_back(convert(string_token(NEWLINE_TOKEN)));
  106. } else if (is('%')) {
  107. out.gen.push_back(convert(string_token("%")));
  108. } else if (is('.') || is('-') || isdigit( *next )) {
  109. out.gen.push_back(parse_with_bounds(next));
  110. } else {
  111. out.gen.push_back(convert(handle(next)));
  112. }
  113. curr = ++next;
  114. }
  115. if (curr < end) {
  116. out.gen.push_back(convert(string_token({curr, end})));
  117. }
  118. return out;
  119. }
  120. #undef is
  121. void format::process(logpacket const & pkt, std::ostream & os) const {
  122. for (auto func : gen) { func(pkt, os); }
  123. }
  124. std::string format::process(logpacket const & pkt) const {
  125. std::stringstream ss;
  126. process(pkt, ss);
  127. return ss.str();
  128. }
  129. void format_msg(std::ostream & os, std::string const & interp, size_t pos,
  130. std::vector<detail::object> const & objs, size_t idx) {
  131. size_t next = interp.find("{}", pos);
  132. os << interp.substr(pos, next);
  133. if (next == std::string::npos) {
  134. return; // throw?
  135. } else if (!strncmp(interp.c_str() + next - 1, "{{}}", 4)) {
  136. format_msg(os, interp, next+2, objs, idx);
  137. } else {
  138. format_msg(os << objs[idx], interp, next+2, objs, idx+1);
  139. }
  140. }
  141. std::string message::str() const {
  142. std::stringstream ss;
  143. format_msg(ss, format_code, 0, objects, 0);
  144. return ss.str();
  145. }
  146. Json::Value message::json() const {
  147. return objects[0].json();
  148. }
  149. }