proxygen
folly::CustomLogFormatter Class Reference

#include <CustomLogFormatter.h>

Inheritance diagram for folly::CustomLogFormatter:
folly::LogFormatter

Public Member Functions

 CustomLogFormatter (StringPiece format, bool colored)
 
std::string formatMessage (const LogMessage &message, const LogCategory *handlerCategory) override
 
- Public Member Functions inherited from folly::LogFormatter
virtual ~LogFormatter ()
 

Private Member Functions

void parseFormatString (StringPiece format)
 

Private Attributes

std::string logFormat_
 
std::string singleLineLogFormat_
 
std::size_t staticEstimatedWidth_ {0}
 
std::size_t fileNameCount_ {0}
 
std::size_t functionNameCount_ {0}
 
const bool colored_
 

Detailed Description

A LogFormatter implementation that produces messages in a format specified using a config.

The glog message format is:

{L}{m:02d}{D:02d} {H:2d}:{M:02d}:{S:02d}.{USECS:06d} {THREAD:5d} {FILE}:{LINE}]

L: A 1-character code describing the log level (e.g., E, W, I, V) m: month D: day H: hour, 24-hour format M: minute S: second USECS: microseconds THREAD: Thread ID FILE: Filename (just the last component) FUN: The function that logged the message LINE: Line number

TODO: enable support for the following 2:

  • THREADNAME: the thread name.
  • THREADCTX: thread-local log context data, if it has been set. (This is a Facebook-specific modification)

Definition at line 50 of file CustomLogFormatter.h.

Constructor & Destructor Documentation

folly::CustomLogFormatter::CustomLogFormatter ( StringPiece  format,
bool  colored 
)
explicit

Definition at line 104 of file CustomLogFormatter.cpp.

References parseFormatString().

105  : colored_(colored) {
107 }
void parseFormatString(StringPiece format)
Formatter< false, Args... > format(StringPiece fmt, Args &&...args)
Definition: Format.h:271

Member Function Documentation

std::string folly::CustomLogFormatter::formatMessage ( const LogMessage message,
const LogCategory handlerCategory 
)
overridevirtual

Serialze a LogMessage object.

Parameters
messageThe LogMessage object to serialze.
handlerCategoryThe LogCategory that is currently handling this message. Note that this is likely different from the LogCategory where the message was originally logged, which can be accessed as message->getCategory()

Implements folly::LogFormatter.

Definition at line 231 of file CustomLogFormatter.cpp.

References buffer(), colored_, folly::LogMessage::containsNewlines(), folly::test::end(), fileNameCount_, folly::format(), functionNameCount_, folly::LogMessage::getFileBaseName(), folly::LogMessage::getFunctionName(), folly::LogMessage::getLevel(), folly::LogMessage::getLineNumber(), folly::LogMessage::getMessage(), folly::LogMessage::getThreadID(), folly::LogMessage::getTimestamp(), logFormat_, folly::Range< const char * >::npos, folly::sformat(), singleLineLogFormat_, folly::Range< Iter >::size(), staticEstimatedWidth_, string, and fizz::toString().

233  {
234  // Get the local time info
235  struct tm ltime;
236  auto timeSinceEpoch = message.getTimestamp().time_since_epoch();
237  auto epochSeconds =
238  std::chrono::duration_cast<std::chrono::seconds>(timeSinceEpoch);
239  std::chrono::microseconds usecs =
240  std::chrono::duration_cast<std::chrono::microseconds>(timeSinceEpoch) -
241  epochSeconds;
242  time_t unixTimestamp = epochSeconds.count();
243  if (!localtime_r(&unixTimestamp, &ltime)) {
244  memset(&ltime, 0, sizeof(ltime));
245  }
246 
247  auto basename = message.getFileBaseName();
248 
249  // Most common logs will be single line logs and so we can format the entire
250  // log string including the message at once.
251  if (!message.containsNewlines()) {
252  return folly::sformat(
254  getGlogLevelName(message.getLevel())[0],
255  ltime.tm_mon + 1,
256  ltime.tm_mday,
257  ltime.tm_hour,
258  ltime.tm_min,
259  ltime.tm_sec,
260  usecs.count(),
261  message.getThreadID(),
262  basename,
263  message.getFunctionName(),
264  message.getLineNumber(),
265  // NOTE: THE FOLLOWING ARGUMENTS ALWAYS NEED TO BE THE LAST 3:
266  message.getMessage(),
267  // If colored logs are enabled, the singleLineLogFormat_ will contain
268  // placeholders for the color and the reset sequences. If not, then
269  // the following params will just be ignored by the folly::sformat().
270  getColorSequence(message.getLevel()),
271  getResetSequence(message.getLevel()));
272  }
273  // If the message contains multiple lines, ensure that the log header is
274  // prepended before each message line.
275  else {
276  const auto headerFormatter = folly::format(
277  logFormat_,
278  getGlogLevelName(message.getLevel())[0],
279  ltime.tm_mon + 1,
280  ltime.tm_mday,
281  ltime.tm_hour,
282  ltime.tm_min,
283  ltime.tm_sec,
284  usecs.count(),
285  message.getThreadID(),
286  basename,
287  message.getFunctionName(),
288  message.getLineNumber());
289 
290  // Estimate header length. If this still isn't long enough the string will
291  // grow as necessary, so the code will still be correct, but just slightly
292  // less efficient than if we had allocated a large enough buffer the first
293  // time around.
294  size_t headerLengthGuess = staticEstimatedWidth_ +
295  (fileNameCount_ * basename.size()) +
296  (functionNameCount_ * message.getFunctionName().size());
297 
298  // Format the data into a buffer.
300  // If colored logging is supported, then process the color based on
301  // the level of the message.
302  if (colored_) {
303  buffer.append(getColorSequence(message.getLevel()).toString());
304  }
305  StringPiece msgData{message.getMessage()};
306 
307  // Make a guess at how many lines will be in the message, just to make an
308  // initial buffer allocation. If the guess is too small then the string
309  // will reallocate and grow as necessary, it will just be slightly less
310  // efficient than if we had guessed enough space.
311  size_t numLinesGuess = 4;
312  buffer.reserve((headerLengthGuess * numLinesGuess) + msgData.size());
313 
314  size_t idx = 0;
315  while (true) {
316  auto end = msgData.find('\n', idx);
317  if (end == StringPiece::npos) {
318  end = msgData.size();
319  }
320 
321  auto line = msgData.subpiece(idx, end - idx);
322  headerFormatter.appendTo(buffer);
323  buffer.append(line.data(), line.size());
324  buffer.push_back('\n');
325 
326  if (end == msgData.size()) {
327  break;
328  }
329  idx = end + 1;
330  }
331  // If colored logging is supported and the current message is a color other
332  // than the default, then RESET colors after printing message.
333  if (colored_) {
334  buffer.append(getResetSequence(message.getLevel()).toString());
335  }
336  return buffer;
337  }
338 }
std::vector< uint8_t > buffer(kBufferSize+16)
Definition: test.c:42
folly::StringPiece toString(StateEnum state)
Definition: State.cpp:16
std::string sformat(StringPiece fmt, Args &&...args)
Definition: Format.h:280
auto end(TestAdlIterable &instance)
Definition: ForeachTest.cpp:62
static const size_type npos
Definition: Range.h:197
const char * string
Definition: Conv.cpp:212
Formatter< false, Args... > format(StringPiece fmt, Args &&...args)
Definition: Format.h:271
Range< const char * > StringPiece
void folly::CustomLogFormatter::parseFormatString ( StringPiece  format)
private

Definition at line 109 of file CustomLogFormatter.cpp.

References a, b, folly::Range< Iter >::begin(), colored_, folly::Range< Iter >::end(), fileNameCount_, functionNameCount_, proxygen::HPACK::LITERAL, logFormat_, gmock_output_test::output, singleLineLogFormat_, folly::Range< Iter >::size(), staticEstimatedWidth_, string, and UNLIKELY.

Referenced by CustomLogFormatter().

109  {
110  std::size_t estimatedWidth = 0;
111  functionNameCount_ = 0;
112  fileNameCount_ = 0;
113  // Replace all format keys to numbers to improve performance and to use
114  // varying value types (which is not possible using folly::vformat()).
116  output.reserve(input.size());
117  const char* varNameStart = nullptr;
118 
119  enum StateEnum {
120  LITERAL,
121  FMT_NAME,
122  FMT_MODIFIERS,
123  } state = LITERAL;
124 
125  for (const char* p = input.begin(); p < input.end(); ++p) {
126  switch (state) {
127  case LITERAL:
128  output.append(p, 1);
129  // In case of `{{` or `}}`, copy it as it is and only increment the
130  // estimatedWidth once as it will result to a single character in
131  // output.
132  if ((p + 1) != input.end() /* ensure not last character */ &&
133  (0 == memcmp(p, "}}", 2) || 0 == memcmp(p, "{{", 2))) {
134  output.append(p + 1, 1);
135  estimatedWidth++;
136  p++;
137  }
138  // If we see a single open curly brace, it denotes a start of a format
139  // name and so we change the state to FMT_NAME and do not increment
140  // estimatedWidth as it won't be in the output.
141  else if (*p == '{') {
142  varNameStart = p + 1;
143  state = FMT_NAME;
144  }
145  // In case it is just a regular literal, just increment estimatedWidth
146  // by one and move on to the next character.
147  else {
148  estimatedWidth++;
149  }
150  break;
151  // In case we have started processing a format name/key
152  case FMT_NAME:
153  // Unless it is the end of the format name/key, do nothing and scan over
154  // the name/key. When it is the end of the format name/key, look up
155  // the argIndex for it and replace the name/key with that index.
156  if (*p == ':' || *p == '}') {
157  StringPiece varName(varNameStart, p);
158  auto item = std::lower_bound(
159  formatKeys.begin(),
160  formatKeys.end(),
161  varName,
162  [](const auto& a, const auto& b) { return a.key < b; });
163 
164  if (UNLIKELY(item == formatKeys.end() || item->key != varName)) {
165  throw std::runtime_error(folly::to<std::string>(
166  "unknown format argument \"", varName, "\""));
167  }
168  output.append(folly::to<std::string>(item->argIndex));
169  output.append(p, 1);
170 
171  // Based on the format key, increment estimatedWidth with the
172  // estimate of how many characters long the value of the format key
173  // will be. If it is a FILE or a FUN, the width will be variable
174  // depending on the values of those fields.
175  estimatedWidth += item->width;
176  if (item->key == "FILE") {
177  fileNameCount_++;
178  } else if (item->key == "FUN") {
180  }
181 
182  // Figure out if there are modifiers that follow the key or if we
183  // continue processing literals.
184  if (*p == ':') {
185  state = FMT_MODIFIERS;
186  } else {
187  state = LITERAL;
188  }
189  }
190  break;
191  // In case we have started processing a format modifier (after :)
192  case FMT_MODIFIERS:
193  // Modifiers are just copied as is and are not considered to determine
194  // the estimatedWidth.
195  output.append(p, 1);
196  if (*p == '}') {
197  state = LITERAL;
198  }
199  break;
200  }
201  }
202  if (state != LITERAL) {
203  throw std::runtime_error("unterminated format string");
204  }
205  // Append a single space after the header format if header is not empty.
206  if (!output.empty()) {
207  output.append(" ");
208  estimatedWidth++;
209  }
210  logFormat_ = output;
211  staticEstimatedWidth_ = estimatedWidth;
212 
213  // populate singleLineLogFormat_ with the padded line format.
214  if (colored_) {
215  singleLineLogFormat_ = folly::to<std::string>(
216  "{",
217  messageIndex + 1,
218  "}",
219  logFormat_,
220  "{",
221  messageIndex,
222  "}{",
223  messageIndex + 2,
224  "}\n");
225  } else {
227  folly::to<std::string>(logFormat_, "{", messageIndex, "}\n");
228  }
229 }
char b
StateEnum
char a
const char * string
Definition: Conv.cpp:212
const Instruction LITERAL
Range< const char * > StringPiece
#define UNLIKELY(x)
Definition: Likely.h:48
state
Definition: http_parser.c:272

Member Data Documentation

const bool folly::CustomLogFormatter::colored_
private

Definition at line 65 of file CustomLogFormatter.h.

Referenced by formatMessage(), and parseFormatString().

std::size_t folly::CustomLogFormatter::fileNameCount_ {0}
private

Definition at line 63 of file CustomLogFormatter.h.

Referenced by formatMessage(), and parseFormatString().

std::size_t folly::CustomLogFormatter::functionNameCount_ {0}
private

Definition at line 64 of file CustomLogFormatter.h.

Referenced by formatMessage(), and parseFormatString().

std::string folly::CustomLogFormatter::logFormat_
private

Definition at line 60 of file CustomLogFormatter.h.

Referenced by formatMessage(), and parseFormatString().

std::string folly::CustomLogFormatter::singleLineLogFormat_
private

Definition at line 61 of file CustomLogFormatter.h.

Referenced by formatMessage(), and parseFormatString().

std::size_t folly::CustomLogFormatter::staticEstimatedWidth_ {0}
private

Definition at line 62 of file CustomLogFormatter.h.

Referenced by formatMessage(), and parseFormatString().


The documentation for this class was generated from the following files: