123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- //
- // Copyright 2022 The Abseil Authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // https://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include "absl/log/internal/log_format.h"
- #include <string.h>
- #ifdef _MSC_VER
- #include <winsock2.h> // For timeval
- #else
- #include <sys/time.h>
- #endif
- #include <cstddef>
- #include <cstdint>
- #include <limits>
- #include <string>
- #include <type_traits>
- #include "absl/base/config.h"
- #include "absl/base/log_severity.h"
- #include "absl/base/optimization.h"
- #include "absl/log/internal/append_truncated.h"
- #include "absl/log/internal/config.h"
- #include "absl/log/internal/globals.h"
- #include "absl/strings/numbers.h"
- #include "absl/strings/str_format.h"
- #include "absl/strings/string_view.h"
- #include "absl/time/civil_time.h"
- #include "absl/time/time.h"
- #include "absl/types/span.h"
- namespace absl {
- ABSL_NAMESPACE_BEGIN
- namespace log_internal {
- namespace {
- // This templated function avoids compiler warnings about tautological
- // comparisons when log_internal::Tid is unsigned. It can be replaced with a
- // constexpr if once the minimum C++ version Abseil supports is C++17.
- template <typename T>
- inline std::enable_if_t<!std::is_signed<T>::value>
- PutLeadingWhitespace(T tid, char*& p) {
- if (tid < 10) *p++ = ' ';
- if (tid < 100) *p++ = ' ';
- if (tid < 1000) *p++ = ' ';
- if (tid < 10000) *p++ = ' ';
- if (tid < 100000) *p++ = ' ';
- if (tid < 1000000) *p++ = ' ';
- }
- template <typename T>
- inline std::enable_if_t<std::is_signed<T>::value>
- PutLeadingWhitespace(T tid, char*& p) {
- if (tid >= 0 && tid < 10) *p++ = ' ';
- if (tid > -10 && tid < 100) *p++ = ' ';
- if (tid > -100 && tid < 1000) *p++ = ' ';
- if (tid > -1000 && tid < 10000) *p++ = ' ';
- if (tid > -10000 && tid < 100000) *p++ = ' ';
- if (tid > -100000 && tid < 1000000) *p++ = ' ';
- }
- // The fields before the filename are all fixed-width except for the thread ID,
- // which is of bounded width.
- size_t FormatBoundedFields(absl::LogSeverity severity, absl::Time timestamp,
- log_internal::Tid tid, absl::Span<char>& buf) {
- constexpr size_t kBoundedFieldsMaxLen =
- sizeof("SMMDD HH:MM:SS.NNNNNN ") +
- (1 + std::numeric_limits<log_internal::Tid>::digits10 + 1) - sizeof("");
- if (ABSL_PREDICT_FALSE(buf.size() < kBoundedFieldsMaxLen)) {
- // We don't bother trying to truncate these fields if the buffer is too
- // short (or almost too short) because it would require doing a lot more
- // length checking (slow) and it should never happen. A 15kB buffer should
- // be enough for anyone. Instead we mark `buf` full without writing
- // anything.
- buf.remove_suffix(buf.size());
- return 0;
- }
- // We can't call absl::LocalTime(), localtime_r(), or anything else here that
- // isn't async-signal-safe. We can only use the time zone if it has already
- // been loaded.
- const absl::TimeZone* tz = absl::log_internal::TimeZone();
- if (ABSL_PREDICT_FALSE(tz == nullptr)) {
- // If a time zone hasn't been set yet because we are logging before the
- // logging library has been initialized, we fallback to a simpler, slower
- // method. Just report the raw Unix time in seconds. We cram this into the
- // normal time format for the benefit of parsers.
- auto tv = absl::ToTimeval(timestamp);
- int snprintf_result = absl::SNPrintF(
- buf.data(), buf.size(), "%c0000 00:00:%02d.%06d %7d ",
- absl::LogSeverityName(severity)[0], static_cast<int>(tv.tv_sec),
- static_cast<int>(tv.tv_usec), static_cast<int>(tid));
- if (snprintf_result >= 0) {
- buf.remove_prefix(static_cast<size_t>(snprintf_result));
- return static_cast<size_t>(snprintf_result);
- }
- return 0;
- }
- char* p = buf.data();
- *p++ = absl::LogSeverityName(severity)[0];
- const absl::TimeZone::CivilInfo ci = tz->At(timestamp);
- absl::numbers_internal::PutTwoDigits(static_cast<uint32_t>(ci.cs.month()), p);
- p += 2;
- absl::numbers_internal::PutTwoDigits(static_cast<uint32_t>(ci.cs.day()), p);
- p += 2;
- *p++ = ' ';
- absl::numbers_internal::PutTwoDigits(static_cast<uint32_t>(ci.cs.hour()), p);
- p += 2;
- *p++ = ':';
- absl::numbers_internal::PutTwoDigits(static_cast<uint32_t>(ci.cs.minute()),
- p);
- p += 2;
- *p++ = ':';
- absl::numbers_internal::PutTwoDigits(static_cast<uint32_t>(ci.cs.second()),
- p);
- p += 2;
- *p++ = '.';
- const int64_t usecs = absl::ToInt64Microseconds(ci.subsecond);
- absl::numbers_internal::PutTwoDigits(static_cast<uint32_t>(usecs / 10000), p);
- p += 2;
- absl::numbers_internal::PutTwoDigits(static_cast<uint32_t>(usecs / 100 % 100),
- p);
- p += 2;
- absl::numbers_internal::PutTwoDigits(static_cast<uint32_t>(usecs % 100), p);
- p += 2;
- *p++ = ' ';
- PutLeadingWhitespace(tid, p);
- p = absl::numbers_internal::FastIntToBuffer(tid, p);
- *p++ = ' ';
- const size_t bytes_formatted = static_cast<size_t>(p - buf.data());
- buf.remove_prefix(bytes_formatted);
- return bytes_formatted;
- }
- size_t FormatLineNumber(int line, absl::Span<char>& buf) {
- constexpr size_t kLineFieldMaxLen =
- sizeof(":] ") + (1 + std::numeric_limits<int>::digits10 + 1) - sizeof("");
- if (ABSL_PREDICT_FALSE(buf.size() < kLineFieldMaxLen)) {
- // As above, we don't bother trying to truncate this if the buffer is too
- // short and it should never happen.
- buf.remove_suffix(buf.size());
- return 0;
- }
- char* p = buf.data();
- *p++ = ':';
- p = absl::numbers_internal::FastIntToBuffer(line, p);
- *p++ = ']';
- *p++ = ' ';
- const size_t bytes_formatted = static_cast<size_t>(p - buf.data());
- buf.remove_prefix(bytes_formatted);
- return bytes_formatted;
- }
- } // namespace
- std::string FormatLogMessage(absl::LogSeverity severity,
- absl::CivilSecond civil_second,
- absl::Duration subsecond, log_internal::Tid tid,
- absl::string_view basename, int line,
- PrefixFormat format, absl::string_view message) {
- return absl::StrFormat(
- "%c%02d%02d %02d:%02d:%02d.%06d %7d %s:%d] %s%s",
- absl::LogSeverityName(severity)[0], civil_second.month(),
- civil_second.day(), civil_second.hour(), civil_second.minute(),
- civil_second.second(), absl::ToInt64Microseconds(subsecond), tid,
- basename, line, format == PrefixFormat::kRaw ? "RAW: " : "", message);
- }
- // This method is fairly hot, and the library always passes a huge `buf`, so we
- // save some bounds-checking cycles by not trying to do precise truncation.
- // Truncating at a field boundary is probably a better UX anyway.
- //
- // The prefix is written in three parts, each of which does a single
- // bounds-check and truncation:
- // 1. severity, timestamp, and thread ID
- // 2. filename
- // 3. line number and bracket
- size_t FormatLogPrefix(absl::LogSeverity severity, absl::Time timestamp,
- log_internal::Tid tid, absl::string_view basename,
- int line, PrefixFormat format, absl::Span<char>& buf) {
- auto prefix_size = FormatBoundedFields(severity, timestamp, tid, buf);
- prefix_size += log_internal::AppendTruncated(basename, buf);
- prefix_size += FormatLineNumber(line, buf);
- if (format == PrefixFormat::kRaw)
- prefix_size += log_internal::AppendTruncated("RAW: ", buf);
- return prefix_size;
- }
- } // namespace log_internal
- ABSL_NAMESPACE_END
- } // namespace absl
|