cf8c7a7b47ba865fd1643579f3b1da3f48abf405
[folly.git] / folly / experimental / logging / GlogStyleFormatter.cpp
1 /*
2  * Copyright 2004-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <folly/experimental/logging/GlogStyleFormatter.h>
17
18 #include <folly/Format.h>
19 #include <folly/experimental/logging/LogLevel.h>
20 #include <folly/experimental/logging/LogMessage.h>
21
22 namespace {
23 using folly::StringPiece;
24 using folly::LogLevel;
25
26 StringPiece getGlogLevelName(LogLevel level) {
27   if (level < LogLevel::INFO) {
28     return "VERBOSE";
29   } else if (level < LogLevel::WARN) {
30     return "INFO";
31   } else if (level < LogLevel::ERROR) {
32     return "WARNING";
33   } else if (level < LogLevel::CRITICAL) {
34     return "ERROR";
35   }
36   return "CRITICAL";
37 }
38 }
39
40 namespace folly {
41
42 std::string GlogStyleFormatter::formatMessage(
43     const LogMessage& message,
44     const LogCategory* /* handlerCategory */) {
45   // Get the local time info
46   struct tm ltime;
47   auto timeSinceEpoch = message.getTimestamp().time_since_epoch();
48   auto epochSeconds =
49       std::chrono::duration_cast<std::chrono::seconds>(timeSinceEpoch);
50   std::chrono::microseconds usecs =
51       std::chrono::duration_cast<std::chrono::microseconds>(timeSinceEpoch) -
52       epochSeconds;
53   time_t unixTimestamp = epochSeconds.count();
54   if (!localtime_r(&unixTimestamp, &ltime)) {
55     memset(&ltime, 0, sizeof(ltime));
56   }
57
58   auto basename = message.getFileBaseName();
59   auto headerFormatter = folly::format(
60       "{}{:02d}{:02d} {:02d}:{:02d}:{:02d}.{:06d} {:5d} {}:{}] ",
61       getGlogLevelName(message.getLevel())[0],
62       ltime.tm_mon + 1,
63       ltime.tm_mday,
64       ltime.tm_hour,
65       ltime.tm_min,
66       ltime.tm_sec,
67       usecs.count(),
68       message.getThreadID(),
69       basename,
70       message.getLineNumber());
71
72   // TODO: Support including thread names and thread context info.
73
74   // The fixed portion of the header takes up 31 bytes.
75   //
76   // The variable portions that we can't account for here include the line
77   // number and the thread ID (just in case it is larger than 6 digits long).
78   // Here we guess that 40 bytes will be long enough to include room for this.
79   //
80   // If this still isn't long enough the string will grow as necessary, so the
81   // code will still be correct, but just slightly less efficient than if we
82   // had allocated a large enough buffer the first time around.
83   size_t headerLengthGuess = 40 + basename.size();
84
85   // Format the data into a buffer.
86   std::string buffer;
87   StringPiece msgData{message.getMessage()};
88   if (message.containsNewlines()) {
89     // If there are multiple lines in the log message, add a header
90     // before each one.
91     std::string header;
92     header.reserve(headerLengthGuess);
93     headerFormatter.appendTo(header);
94
95     // Make a guess at how many lines will be in the message, just to make an
96     // initial buffer allocation.  If the guess is too small then the string
97     // will reallocate and grow as necessary, it will just be slightly less
98     // efficient than if we had guessed enough space.
99     size_t numLinesGuess = 4;
100     buffer.reserve(((header.size() + 1) * numLinesGuess) + msgData.size());
101
102     size_t idx = 0;
103     while (true) {
104       auto end = msgData.find('\n', idx);
105       if (end == StringPiece::npos) {
106         end = msgData.size();
107       }
108
109       buffer.append(header);
110       auto line = msgData.subpiece(idx, end - idx);
111       buffer.append(line.data(), line.size());
112       buffer.push_back('\n');
113
114       if (end == msgData.size()) {
115         break;
116       }
117       idx = end + 1;
118     }
119   } else {
120     buffer.reserve(headerLengthGuess + msgData.size());
121     headerFormatter.appendTo(buffer);
122     buffer.append(msgData.data(), msgData.size());
123     buffer.push_back('\n');
124   }
125
126   return buffer;
127 }
128 }