add a new logging library
[folly.git] / folly / experimental / logging / LogMessage.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/LogMessage.h>
17
18 #include <folly/ThreadId.h>
19
20 using std::chrono::system_clock;
21
22 namespace folly {
23
24 LogMessage::LogMessage(
25     const LogCategory* category,
26     LogLevel level,
27     StringPiece filename,
28     unsigned int lineNumber,
29     std::string&& msg)
30     : category_{category},
31       level_{level},
32       threadID_{getOSThreadID()},
33       timestamp_{system_clock::now()},
34       filename_{filename},
35       lineNumber_{lineNumber},
36       rawMessage_{std::move(msg)} {
37   sanitizeMessage();
38 }
39
40 LogMessage::LogMessage(
41     const LogCategory* category,
42     LogLevel level,
43     system_clock::time_point timestamp,
44     StringPiece filename,
45     unsigned int lineNumber,
46     std::string&& msg)
47     : category_{category},
48       level_{level},
49       threadID_{getOSThreadID()},
50       timestamp_{timestamp},
51       filename_{filename},
52       lineNumber_{lineNumber},
53       rawMessage_{std::move(msg)} {
54   sanitizeMessage();
55 }
56
57 StringPiece LogMessage::getFileBaseName() const {
58 #if _WIN32
59   // Windows allows either backwards or forwards slash as path separator
60   auto idx1 = filename_.rfind('\\');
61   auto idx2 = filename_.rfind('/');
62   StringPiece::size_type idx;
63   if (idx1 == StringPiece::npos) {
64     idx = idx2;
65   } else if (idx2 == StringPiece::npos) {
66     idx = idx1;
67   } else {
68     idx = std::max(idx1, idx2);
69   }
70 #else
71   auto idx = filename_.rfind('/');
72 #endif
73   if (idx == StringPiece::npos) {
74     return filename_;
75   }
76   return filename_.subpiece(idx + 1);
77 }
78
79 void LogMessage::sanitizeMessage() {
80   // Compute how long the sanitized string will be.
81   size_t sanitizedLength = 0;
82   for (const char c : rawMessage_) {
83     if (c == '\\') {
84       // Backslashes are escaped as two backslashes
85       sanitizedLength += 2;
86     } else if (static_cast<unsigned char>(c) < 0x20) {
87       // Newlines and tabs are emitted directly with no escaping.
88       // All other control characters are emitted as \xNN (4 characters)
89       if (c == '\n') {
90         sanitizedLength += 1;
91         containsNewlines_ = true;
92       } else if (c == '\t') {
93         sanitizedLength += 1;
94       } else {
95         sanitizedLength += 4;
96       }
97     } else if (c == 0x7f) {
98       // Bytes above the ASCII range are emitted as \xNN (4 characters)
99       sanitizedLength += 4;
100     } else {
101       // This character will be emitted as-is, with no escaping.
102       ++sanitizedLength;
103     }
104   }
105
106   // If nothing is different, just use rawMessage_ directly,
107   // and don't populate message_.
108   if (sanitizedLength == rawMessage_.size()) {
109     return;
110   }
111
112   message_.reserve(sanitizedLength);
113   for (const char c : rawMessage_) {
114     if (c == '\\') {
115       message_.push_back('\\');
116       message_.push_back('\\');
117     } else if (static_cast<unsigned char>(c) < 0x20) {
118       if (c == '\n' || c == '\t') {
119         message_.push_back(c);
120       } else {
121         static constexpr StringPiece hexdigits{"0123456789abcdef"};
122         std::array<char, 4> data{
123             {'\\', 'x', hexdigits[(c >> 4) & 0xf], hexdigits[c & 0xf]}};
124         message_.append(data.data(), data.size());
125       }
126     } else if (c == 0x7f) {
127       constexpr std::array<char, 4> data{{'\\', 'x', '7', 'f'}};
128       message_.append(data.data(), data.size());
129     } else {
130       message_.push_back(c);
131     }
132   }
133 }
134 }