logging: fully convert the ERROR level to ERR
[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 #include <folly/portability/Time.h>
22
23 namespace {
24 using folly::StringPiece;
25 using folly::LogLevel;
26
27 StringPiece getGlogLevelName(LogLevel level) {
28   if (level < LogLevel::INFO) {
29     return "VERBOSE";
30   } else if (level < LogLevel::WARN) {
31     return "INFO";
32   } else if (level < LogLevel::ERR) {
33     return "WARNING";
34   } else if (level < LogLevel::CRITICAL) {
35     return "ERROR";
36   }
37   return "CRITICAL";
38 }
39 }
40
41 namespace folly {
42
43 std::string GlogStyleFormatter::formatMessage(
44     const LogMessage& message,
45     const LogCategory* /* handlerCategory */) {
46   // Get the local time info
47   struct tm ltime;
48   auto timeSinceEpoch = message.getTimestamp().time_since_epoch();
49   auto epochSeconds =
50       std::chrono::duration_cast<std::chrono::seconds>(timeSinceEpoch);
51   std::chrono::microseconds usecs =
52       std::chrono::duration_cast<std::chrono::microseconds>(timeSinceEpoch) -
53       epochSeconds;
54   time_t unixTimestamp = epochSeconds.count();
55   if (!localtime_r(&unixTimestamp, &ltime)) {
56     memset(&ltime, 0, sizeof(ltime));
57   }
58
59   auto basename = message.getFileBaseName();
60   auto headerFormatter = folly::format(
61       "{}{:02d}{:02d} {:02d}:{:02d}:{:02d}.{:06d} {:5d} {}:{}] ",
62       getGlogLevelName(message.getLevel())[0],
63       ltime.tm_mon + 1,
64       ltime.tm_mday,
65       ltime.tm_hour,
66       ltime.tm_min,
67       ltime.tm_sec,
68       usecs.count(),
69       message.getThreadID(),
70       basename,
71       message.getLineNumber());
72
73   // TODO: Support including thread names and thread context info.
74
75   // The fixed portion of the header takes up 31 bytes.
76   //
77   // The variable portions that we can't account for here include the line
78   // number and the thread ID (just in case it is larger than 6 digits long).
79   // Here we guess that 40 bytes will be long enough to include room for this.
80   //
81   // If this still isn't long enough the string will grow as necessary, so the
82   // code will still be correct, but just slightly less efficient than if we
83   // had allocated a large enough buffer the first time around.
84   size_t headerLengthGuess = 40 + basename.size();
85
86   // Format the data into a buffer.
87   std::string buffer;
88   StringPiece msgData{message.getMessage()};
89   if (message.containsNewlines()) {
90     // If there are multiple lines in the log message, add a header
91     // before each one.
92     std::string header;
93     header.reserve(headerLengthGuess);
94     headerFormatter.appendTo(header);
95
96     // Make a guess at how many lines will be in the message, just to make an
97     // initial buffer allocation.  If the guess is too small then the string
98     // will reallocate and grow as necessary, it will just be slightly less
99     // efficient than if we had guessed enough space.
100     size_t numLinesGuess = 4;
101     buffer.reserve(((header.size() + 1) * numLinesGuess) + msgData.size());
102
103     size_t idx = 0;
104     while (true) {
105       auto end = msgData.find('\n', idx);
106       if (end == StringPiece::npos) {
107         end = msgData.size();
108       }
109
110       buffer.append(header);
111       auto line = msgData.subpiece(idx, end - idx);
112       buffer.append(line.data(), line.size());
113       buffer.push_back('\n');
114
115       if (end == msgData.size()) {
116         break;
117       }
118       idx = end + 1;
119     }
120   } else {
121     buffer.reserve(headerLengthGuess + msgData.size());
122     headerFormatter.appendTo(buffer);
123     buffer.append(msgData.data(), msgData.size());
124     buffer.push_back('\n');
125   }
126
127   return buffer;
128 }
129 }