pattern_layout_format.cxx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. //
  2. // format.cpp
  3. // logger
  4. //
  5. // Created by Sam Jaffe on 8/21/16.
  6. //
  7. #include <cstdint>
  8. #include <cstring>
  9. #include <ctime>
  10. #include <iomanip>
  11. #include <iostream>
  12. #include <map>
  13. #include <stdexcept>
  14. #include <string>
  15. #include "common.h"
  16. #include "logger/exception.h"
  17. #include "logger/logger.h"
  18. #include "logger/logpacket.h"
  19. #include "logger/message.h"
  20. #include "pattern_layout.h"
  21. namespace logging { namespace {
  22. struct log_handle_gen : public format::generator_t {
  23. std::string str(logpacket const & lp) const override {
  24. return lp.logger;
  25. }
  26. void write(logpacket const & lp, std::ostream & os) const override {
  27. os << lp.logger;
  28. }
  29. };
  30. struct level_gen : public format::generator_t {
  31. std::string str(logpacket const & lp) const override {
  32. return to_string(lp.level, true);
  33. }
  34. };
  35. struct location_info_gen : public format::generator_t {
  36. enum { method, line, file } where;
  37. constexpr static const decltype(where) format_codes[] = {
  38. ['F'] = file,
  39. ['M'] = method,
  40. ['L'] = line,
  41. };
  42. location_info_gen(char wh) : where(format_codes[wh]) {}
  43. std::string str(logpacket const & lp) const override {
  44. switch (where) {
  45. case file:
  46. return lp.info.filename;
  47. case line:
  48. return std::to_string(lp.info.line);
  49. case method:
  50. return lp.info.function;
  51. }
  52. }
  53. };
  54. struct message_gen : public format::generator_t {
  55. std::string str(logpacket const & lp) const override {
  56. return lp.message.str();
  57. }
  58. };
  59. struct literal_gen : public format::generator_t {
  60. literal_gen(std::string const & value) : value(value) {}
  61. std::string str(logpacket const & lp) const override {
  62. return value;
  63. }
  64. void write(logpacket const & lp, std::ostream & os) const override {
  65. os << value;
  66. }
  67. std::string value;
  68. };
  69. struct bounds_gen : public format::generator_t {
  70. bounds_gen(format::generator impl, bool is_left, int min, size_t max)
  71. : impl(impl), is_left(is_left), min(min), max(max) {}
  72. std::string str(logpacket const & lp) const override {
  73. return impl->str(lp);
  74. }
  75. void write(logpacket const & lp, std::ostream & os) const override {
  76. auto align = is_left ? std::left : std::right;
  77. std::string str = this->str(lp);
  78. if (str.length() > max) {
  79. str.erase(str.begin()+max, str.end());
  80. }
  81. os << align << std::setw(min) << str;
  82. }
  83. format::generator impl;
  84. bool is_left;
  85. int min;
  86. size_t max;
  87. };
  88. } }
  89. namespace logging {
  90. #define is( chr ) *next == chr
  91. #define is_string( str ) ! strncmp(next, str, strlen(str))
  92. format::generator date_token(char const * next);
  93. format::generator handle( char const * & next ) {
  94. if (is('c')) {
  95. return std::make_shared<log_handle_gen>();
  96. } else if (is('p')) {
  97. return std::make_shared<level_gen>();
  98. } else if (is('m')) {
  99. return std::make_shared<message_gen>();
  100. } else if (is('t')) {
  101. return std::make_shared<literal_gen>("???");
  102. } else if (is('F') || is('M') || is('L')) {
  103. return std::make_shared<location_info_gen>(*next);
  104. } else {
  105. std::string error_msg{"unknown format character: '"};
  106. throw unknown_format_specifier{error_msg + *next + "'"};
  107. }
  108. }
  109. int eat(char const * & token) {
  110. size_t chars = 0;
  111. int rval = std::stoi(token, &chars);
  112. token += chars;
  113. return rval;
  114. }
  115. std::pair<int, size_t> get_bounds(char const * & next) {
  116. int min = *next == '.' ? 0 : eat(next);
  117. size_t max = *next == '.' ? eat(++next) : std::string::npos;
  118. return std::make_pair(min, max);
  119. }
  120. format::generator parse_with_bounds(char const * & next) {
  121. bool const is_left = *next == '-';
  122. if (is_left) ++next;
  123. auto bounds = get_bounds(next);
  124. bounds_gen gen{handle(next), is_left, bounds.first, bounds.second};
  125. return std::make_shared<bounds_gen>(gen);
  126. }
  127. format format::parse_format_string( std::string const & fmt ) {
  128. format out;
  129. char const * curr = fmt.c_str();
  130. char const * next = nullptr;
  131. char const * const end = curr + fmt.size();
  132. while ((next = std::strchr(curr, '%')) != nullptr) {
  133. ++next;
  134. if (end == next) {
  135. std::string error_msg{"expected format specifier, got end of string"};
  136. throw format_parsing_exception{error_msg}; // TODO
  137. }
  138. if (curr < next-1) {
  139. std::string lit{curr, next - 1};
  140. out.gen.push_back(std::make_shared<literal_gen>(lit));
  141. }
  142. if (is('d')) {
  143. ++next;
  144. out.gen.push_back(date_token(next));
  145. if (is('{')) next = std::strchr(next, '}');
  146. } else if (is('n')) {
  147. out.gen.push_back(std::make_shared<literal_gen>(NEWLINE_TOKEN));
  148. } else if (is('%')) {
  149. out.gen.push_back(std::make_shared<literal_gen>("%"));
  150. } else if (is('.') || is('-') || isdigit( *next )) {
  151. out.gen.push_back(parse_with_bounds(next));
  152. } else {
  153. out.gen.push_back(handle(next));
  154. }
  155. curr = ++next;
  156. }
  157. if (curr < end) {
  158. std::string lit{curr, end};
  159. out.gen.push_back(std::make_shared<literal_gen>(lit));
  160. }
  161. return out;
  162. }
  163. #undef is
  164. void format::process(logpacket const & pkt, std::ostream & os) const {
  165. for (auto func : gen) { func->write(pkt, os); }
  166. }
  167. std::string format::process(logpacket const & pkt) const {
  168. std::stringstream ss;
  169. process(pkt, ss);
  170. return ss.str();
  171. }
  172. }