2 * Copyright 2004-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <folly/experimental/logging/GlogStyleFormatter.h>
18 #include <folly/Format.h>
19 #include <folly/experimental/logging/LogLevel.h>
20 #include <folly/experimental/logging/LogMessage.h>
23 using folly::StringPiece;
24 using folly::LogLevel;
26 StringPiece getGlogLevelName(LogLevel level) {
27 if (level < LogLevel::INFO) {
29 } else if (level < LogLevel::WARN) {
31 } else if (level < LogLevel::ERROR) {
33 } else if (level < LogLevel::CRITICAL) {
42 std::string GlogStyleFormatter::formatMessage(
43 const LogMessage& message,
44 const LogCategory* /* handlerCategory */) {
45 // Get the local time info
47 auto timeSinceEpoch = message.getTimestamp().time_since_epoch();
49 std::chrono::duration_cast<std::chrono::seconds>(timeSinceEpoch);
50 std::chrono::microseconds usecs =
51 std::chrono::duration_cast<std::chrono::microseconds>(timeSinceEpoch) -
53 time_t unixTimestamp = epochSeconds.count();
54 if (!localtime_r(&unixTimestamp, <ime)) {
55 memset(<ime, 0, sizeof(ltime));
58 auto basename = message.getFileBaseName();
59 auto headerFormatter = folly::format(
60 "{}{:02d}{:02d} {:02d}:{:02d}:{:02d}.{:06d} {:5d} {}:{}] ",
61 getGlogLevelName(message.getLevel())[0],
68 message.getThreadID(),
70 message.getLineNumber());
72 // TODO: Support including thread names and thread context info.
74 // The fixed portion of the header takes up 31 bytes.
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.
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();
85 // Format the data into a buffer.
87 StringPiece msgData{message.getMessage()};
88 if (message.containsNewlines()) {
89 // If there are multiple lines in the log message, add a header
92 header.reserve(headerLengthGuess);
93 headerFormatter.appendTo(header);
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());
104 auto end = msgData.find('\n', idx);
105 if (end == StringPiece::npos) {
106 end = msgData.size();
109 buffer.append(header);
110 auto line = msgData.subpiece(idx, end - idx);
111 buffer.append(line.data(), line.size());
112 buffer.push_back('\n');
114 if (end == msgData.size()) {
120 buffer.reserve(headerLengthGuess + msgData.size());
121 headerFormatter.appendTo(buffer);
122 buffer.append(msgData.data(), msgData.size());
123 buffer.push_back('\n');