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>
21 #include <folly/portability/Time.h>
24 using folly::StringPiece;
25 using folly::LogLevel;
27 StringPiece getGlogLevelName(LogLevel level) {
28 if (level < LogLevel::INFO) {
30 } else if (level < LogLevel::WARN) {
32 } else if (level < LogLevel::ERR) {
34 } else if (level < LogLevel::CRITICAL) {
43 std::string GlogStyleFormatter::formatMessage(
44 const LogMessage& message,
45 const LogCategory* /* handlerCategory */) {
46 // Get the local time info
48 auto timeSinceEpoch = message.getTimestamp().time_since_epoch();
50 std::chrono::duration_cast<std::chrono::seconds>(timeSinceEpoch);
51 std::chrono::microseconds usecs =
52 std::chrono::duration_cast<std::chrono::microseconds>(timeSinceEpoch) -
54 time_t unixTimestamp = epochSeconds.count();
55 if (!localtime_r(&unixTimestamp, <ime)) {
56 memset(<ime, 0, sizeof(ltime));
59 auto basename = message.getFileBaseName();
60 auto headerFormatter = folly::format(
61 "{}{:02d}{:02d} {:02d}:{:02d}:{:02d}.{:06d} {:5d} {}:{}] ",
62 getGlogLevelName(message.getLevel())[0],
69 message.getThreadID(),
71 message.getLineNumber());
73 // TODO: Support including thread names and thread context info.
75 // The fixed portion of the header takes up 31 bytes.
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.
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();
86 // Format the data into a buffer.
88 StringPiece msgData{message.getMessage()};
89 if (message.containsNewlines()) {
90 // If there are multiple lines in the log message, add a header
93 header.reserve(headerLengthGuess);
94 headerFormatter.appendTo(header);
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());
105 auto end = msgData.find('\n', idx);
106 if (end == StringPiece::npos) {
107 end = msgData.size();
110 buffer.append(header);
111 auto line = msgData.subpiece(idx, end - idx);
112 buffer.append(line.data(), line.size());
113 buffer.push_back('\n');
115 if (end == msgData.size()) {
121 buffer.reserve(headerLengthGuess + msgData.size());
122 headerFormatter.appendTo(buffer);
123 buffer.append(msgData.data(), msgData.size());
124 buffer.push_back('\n');