| 1 | /* |
| 2 | * Copyright 2017-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/logging/LogStreamProcessor.h> |
| 17 | |
| 18 | #include <folly/logging/LogStream.h> |
| 19 | #include <folly/logging/xlog.h> |
| 20 | |
| 21 | namespace folly { |
| 22 | |
| 23 | LogStreamProcessor::LogStreamProcessor( |
| 24 | const LogCategory* category, |
| 25 | LogLevel level, |
| 26 | folly::StringPiece filename, |
| 27 | unsigned int lineNumber, |
| 28 | folly::StringPiece functionName, |
| 29 | AppendType) noexcept |
| 30 | : LogStreamProcessor( |
| 31 | category, |
| 32 | level, |
| 33 | filename, |
| 34 | lineNumber, |
| 35 | functionName, |
| 36 | INTERNAL, |
| 37 | std::string()) {} |
| 38 | |
| 39 | LogStreamProcessor::LogStreamProcessor( |
| 40 | XlogCategoryInfo<true>* categoryInfo, |
| 41 | LogLevel level, |
| 42 | folly::StringPiece categoryName, |
| 43 | bool isCategoryNameOverridden, |
| 44 | folly::StringPiece filename, |
| 45 | unsigned int lineNumber, |
| 46 | folly::StringPiece functionName, |
| 47 | AppendType) noexcept |
| 48 | : LogStreamProcessor( |
| 49 | categoryInfo, |
| 50 | level, |
| 51 | categoryName, |
| 52 | isCategoryNameOverridden, |
| 53 | filename, |
| 54 | lineNumber, |
| 55 | functionName, |
| 56 | INTERNAL, |
| 57 | std::string()) {} |
| 58 | |
| 59 | LogStreamProcessor::LogStreamProcessor( |
| 60 | const LogCategory* category, |
| 61 | LogLevel level, |
| 62 | folly::StringPiece filename, |
| 63 | unsigned int lineNumber, |
| 64 | folly::StringPiece functionName, |
| 65 | InternalType, |
| 66 | std::string&& msg) noexcept |
| 67 | : category_{category}, |
| 68 | level_{level}, |
| 69 | filename_{filename}, |
| 70 | lineNumber_{lineNumber}, |
| 71 | functionName_{functionName}, |
| 72 | message_{std::move(msg)}, |
| 73 | stream_{this} {} |
| 74 | |
| 75 | namespace { |
| 76 | LogCategory* getXlogCategory( |
| 77 | XlogCategoryInfo<true>* categoryInfo, |
| 78 | folly::StringPiece categoryName, |
| 79 | bool isCategoryNameOverridden) { |
| 80 | if (!categoryInfo->isInitialized()) { |
| 81 | return categoryInfo->init(categoryName, isCategoryNameOverridden); |
| 82 | } |
| 83 | return categoryInfo->getCategory(&xlog_detail::xlogFileScopeInfo); |
| 84 | } |
| 85 | } // namespace |
| 86 | |
| 87 | /** |
| 88 | * Construct a LogStreamProcessor from an XlogCategoryInfo. |
| 89 | * |
| 90 | * We intentionally define this in LogStreamProcessor.cpp instead of |
| 91 | * LogStreamProcessor.h to avoid having it inlined at every XLOG() call site, |
| 92 | * to reduce the emitted code size. |
| 93 | */ |
| 94 | LogStreamProcessor::LogStreamProcessor( |
| 95 | XlogCategoryInfo<true>* categoryInfo, |
| 96 | LogLevel level, |
| 97 | folly::StringPiece categoryName, |
| 98 | bool isCategoryNameOverridden, |
| 99 | folly::StringPiece filename, |
| 100 | unsigned int lineNumber, |
| 101 | folly::StringPiece functionName, |
| 102 | InternalType, |
| 103 | std::string&& msg) noexcept |
| 104 | : category_{getXlogCategory( |
| 105 | categoryInfo, |
| 106 | categoryName, |
| 107 | isCategoryNameOverridden)}, |
| 108 | level_{level}, |
| 109 | filename_{filename}, |
| 110 | lineNumber_{lineNumber}, |
| 111 | functionName_{functionName}, |
| 112 | message_{std::move(msg)}, |
| 113 | stream_{this} {} |
| 114 | |
| 115 | #ifdef __INCLUDE_LEVEL__ |
| 116 | namespace { |
| 117 | LogCategory* getXlogCategory(XlogFileScopeInfo* fileScopeInfo) { |
| 118 | // By the time a LogStreamProcessor is created, the XlogFileScopeInfo object |
| 119 | // should have already been initialized to perform the log level check. |
| 120 | // Therefore we never need to check if it is initialized here. |
| 121 | return fileScopeInfo->category; |
| 122 | } |
| 123 | } // namespace |
| 124 | |
| 125 | /** |
| 126 | * Construct a LogStreamProcessor from an XlogFileScopeInfo. |
| 127 | * |
| 128 | * We intentionally define this in LogStreamProcessor.cpp instead of |
| 129 | * LogStreamProcessor.h to avoid having it inlined at every XLOG() call site, |
| 130 | * to reduce the emitted code size. |
| 131 | * |
| 132 | * This is only defined if __INCLUDE_LEVEL__ is available. The |
| 133 | * XlogFileScopeInfo APIs are only invoked if we can use __INCLUDE_LEVEL__ to |
| 134 | * tell that an XLOG() statement occurs in a non-header file. For compilers |
| 135 | * that do not support __INCLUDE_LEVEL__, the category information is always |
| 136 | * passed in as XlogCategoryInfo<true> rather than as XlogFileScopeInfo. |
| 137 | */ |
| 138 | LogStreamProcessor::LogStreamProcessor( |
| 139 | XlogFileScopeInfo* fileScopeInfo, |
| 140 | LogLevel level, |
| 141 | folly::StringPiece filename, |
| 142 | unsigned int lineNumber, |
| 143 | folly::StringPiece functionName, |
| 144 | InternalType, |
| 145 | std::string&& msg) noexcept |
| 146 | : category_{getXlogCategory(fileScopeInfo)}, |
| 147 | level_{level}, |
| 148 | filename_{filename}, |
| 149 | lineNumber_{lineNumber}, |
| 150 | functionName_{functionName}, |
| 151 | message_{std::move(msg)}, |
| 152 | stream_{this} {} |
| 153 | |
| 154 | LogStreamProcessor::LogStreamProcessor( |
| 155 | XlogFileScopeInfo* fileScopeInfo, |
| 156 | LogLevel level, |
| 157 | folly::StringPiece filename, |
| 158 | unsigned int lineNumber, |
| 159 | folly::StringPiece functionName, |
| 160 | AppendType) noexcept |
| 161 | : LogStreamProcessor( |
| 162 | fileScopeInfo, |
| 163 | level, |
| 164 | filename, |
| 165 | lineNumber, |
| 166 | functionName, |
| 167 | INTERNAL, |
| 168 | std::string()) {} |
| 169 | #endif |
| 170 | |
| 171 | /* |
| 172 | * We intentionally define the LogStreamProcessor destructor in |
| 173 | * LogStreamProcessor.cpp instead of LogStreamProcessor.h to avoid having it |
| 174 | * emitted inline at every log statement site. This helps reduce the emitted |
| 175 | * code size for each log statement. |
| 176 | */ |
| 177 | LogStreamProcessor::~LogStreamProcessor() noexcept { |
| 178 | // The LogStreamProcessor destructor is responsible for logging the message. |
| 179 | // Doing this in the destructor avoids an separate function call to log the |
| 180 | // message being emitted inline at every log statement site. |
| 181 | logNow(); |
| 182 | } |
| 183 | |
| 184 | void LogStreamProcessor::logNow() noexcept { |
| 185 | // Note that admitMessage() is not noexcept and theoretically may throw. |
| 186 | // However, the only exception that should be possible is std::bad_alloc if |
| 187 | // we fail to allocate memory. We intentionally let our noexcept specifier |
| 188 | // crash in that case, since the program likely won't be able to continue |
| 189 | // anyway. |
| 190 | // |
| 191 | // Any other error here is unexpected and we also want to fail hard |
| 192 | // in that situation too. |
| 193 | category_->admitMessage(LogMessage{category_, |
| 194 | level_, |
| 195 | filename_, |
| 196 | lineNumber_, |
| 197 | functionName_, |
| 198 | extractMessageString(stream_)}); |
| 199 | } |
| 200 | |
| 201 | std::string LogStreamProcessor::( |
| 202 | LogStream& stream) noexcept { |
| 203 | if (stream.empty()) { |
| 204 | return std::move(message_); |
| 205 | } |
| 206 | |
| 207 | if (message_.empty()) { |
| 208 | return stream.extractString(); |
| 209 | } |
| 210 | message_.append(stream.extractString()); |
| 211 | return std::move(message_); |
| 212 | } |
| 213 | |
| 214 | void LogStreamVoidify<true>::operator&(std::ostream& stream) { |
| 215 | // Non-fatal log messages wait until the LogStreamProcessor destructor to log |
| 216 | // the message. However for fatal messages we log immediately in the & |
| 217 | // operator, since it is marked noreturn. |
| 218 | // |
| 219 | // This does result in slightly larger emitted code for fatal log messages |
| 220 | // (since the operator & call cannot be completely omitted). However, fatal |
| 221 | // log messages should typically be much more rare than non-fatal messages, |
| 222 | // so the small amount of extra overhead shouldn't be a big deal. |
| 223 | auto& logStream = static_cast<LogStream&>(stream); |
| 224 | logStream.getProcessor()->logNow(); |
| 225 | abort(); |
| 226 | } |
| 227 | |
| 228 | void logDisabledHelper(std::true_type) noexcept { |
| 229 | // This function can only be reached if we had a disabled fatal log message. |
| 230 | // This should never happen: LogCategory::setLevelLocked() does not allow |
| 231 | // setting the threshold for a category lower than FATAL (in production |
| 232 | // builds) or DFATAL (in debug builds). |
| 233 | abort(); |
| 234 | } |
| 235 | } // namespace folly |
| 236 | |