1 //
2 // Copyright 2022 The Abseil Authors.
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 // https://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 "absl/log/internal/log_format.h"
17
18 #include <string.h>
19
20 #ifdef _MSC_VER
21 #include <winsock2.h> // For timeval
22 #else
23 #include <sys/time.h>
24 #endif
25
26 #include <cstddef>
27 #include <cstdint>
28 #include <limits>
29 #include <string>
30 #include <type_traits>
31
32 #include "absl/base/config.h"
33 #include "absl/base/log_severity.h"
34 #include "absl/base/optimization.h"
35 #include "absl/log/internal/config.h"
36 #include "absl/log/internal/globals.h"
37 #include "absl/strings/numbers.h"
38 #include "absl/strings/str_format.h"
39 #include "absl/strings/string_view.h"
40 #include "absl/time/civil_time.h"
41 #include "absl/time/time.h"
42 #include "absl/types/span.h"
43
44 namespace absl {
45 ABSL_NAMESPACE_BEGIN
46 namespace log_internal {
47 namespace {
48
49 // This templated function avoids compiler warnings about tautological
50 // comparisons when log_internal::Tid is unsigned. It can be replaced with a
51 // constexpr if once the minimum C++ version Abseil suppports is C++17.
52 template <typename T>
53 inline std::enable_if_t<!std::is_signed<T>::value>
PutLeadingWhitespace(T tid,char * & p)54 PutLeadingWhitespace(T tid, char*& p) {
55 if (tid < 10) *p++ = ' ';
56 if (tid < 100) *p++ = ' ';
57 if (tid < 1000) *p++ = ' ';
58 if (tid < 10000) *p++ = ' ';
59 if (tid < 100000) *p++ = ' ';
60 if (tid < 1000000) *p++ = ' ';
61 }
62
63 template <typename T>
64 inline std::enable_if_t<std::is_signed<T>::value>
PutLeadingWhitespace(T tid,char * & p)65 PutLeadingWhitespace(T tid, char*& p) {
66 if (tid >= 0 && tid < 10) *p++ = ' ';
67 if (tid > -10 && tid < 100) *p++ = ' ';
68 if (tid > -100 && tid < 1000) *p++ = ' ';
69 if (tid > -1000 && tid < 10000) *p++ = ' ';
70 if (tid > -10000 && tid < 100000) *p++ = ' ';
71 if (tid > -100000 && tid < 1000000) *p++ = ' ';
72 }
73
74 // The fields before the filename are all fixed-width except for the thread ID,
75 // which is of bounded width.
FormatBoundedFields(absl::LogSeverity severity,absl::Time timestamp,log_internal::Tid tid,absl::Span<char> & buf)76 size_t FormatBoundedFields(absl::LogSeverity severity, absl::Time timestamp,
77 log_internal::Tid tid, absl::Span<char>& buf) {
78 constexpr size_t kBoundedFieldsMaxLen =
79 sizeof("SMMDD HH:MM:SS.NNNNNN ") +
80 (1 + std::numeric_limits<log_internal::Tid>::digits10 + 1) - sizeof("");
81 if (ABSL_PREDICT_FALSE(buf.size() < kBoundedFieldsMaxLen)) {
82 // We don't bother trying to truncate these fields if the buffer is too
83 // short (or almost too short) because it would require doing a lot more
84 // length checking (slow) and it should never happen. A 15kB buffer should
85 // be enough for anyone. Instead we mark `buf` full without writing
86 // anything.
87 buf.remove_suffix(buf.size());
88 return 0;
89 }
90
91 // We can't call absl::LocalTime(), localtime_r(), or anything else here that
92 // isn't async-signal-safe. We can only use the time zone if it has already
93 // been loaded.
94 const absl::TimeZone* tz = absl::log_internal::TimeZone();
95 if (ABSL_PREDICT_FALSE(tz == nullptr)) {
96 // If a time zone hasn't been set yet because we are logging before the
97 // logging library has been initialized, we fallback to a simpler, slower
98 // method. Just report the raw Unix time in seconds. We cram this into the
99 // normal time format for the benefit of parsers.
100 auto tv = absl::ToTimeval(timestamp);
101 int snprintf_result = absl::SNPrintF(
102 buf.data(), buf.size(), "%c0000 00:00:%02d.%06d %7d ",
103 absl::LogSeverityName(severity)[0], static_cast<int>(tv.tv_sec),
104 static_cast<int>(tv.tv_usec), static_cast<int>(tid));
105 if (snprintf_result >= 0) {
106 buf.remove_prefix(static_cast<size_t>(snprintf_result));
107 return static_cast<size_t>(snprintf_result);
108 }
109 return 0;
110 }
111
112 char* p = buf.data();
113 *p++ = absl::LogSeverityName(severity)[0];
114 const absl::TimeZone::CivilInfo ci = tz->At(timestamp);
115 absl::numbers_internal::PutTwoDigits(static_cast<size_t>(ci.cs.month()), p);
116 p += 2;
117 absl::numbers_internal::PutTwoDigits(static_cast<size_t>(ci.cs.day()), p);
118 p += 2;
119 *p++ = ' ';
120 absl::numbers_internal::PutTwoDigits(static_cast<size_t>(ci.cs.hour()), p);
121 p += 2;
122 *p++ = ':';
123 absl::numbers_internal::PutTwoDigits(static_cast<size_t>(ci.cs.minute()), p);
124 p += 2;
125 *p++ = ':';
126 absl::numbers_internal::PutTwoDigits(static_cast<size_t>(ci.cs.second()), p);
127 p += 2;
128 *p++ = '.';
129 const int64_t usecs = absl::ToInt64Microseconds(ci.subsecond);
130 absl::numbers_internal::PutTwoDigits(static_cast<size_t>(usecs / 10000), p);
131 p += 2;
132 absl::numbers_internal::PutTwoDigits(static_cast<size_t>(usecs / 100 % 100),
133 p);
134 p += 2;
135 absl::numbers_internal::PutTwoDigits(static_cast<size_t>(usecs % 100), p);
136 p += 2;
137 *p++ = ' ';
138 PutLeadingWhitespace(tid, p);
139 p = absl::numbers_internal::FastIntToBuffer(tid, p);
140 *p++ = ' ';
141 const size_t bytes_formatted = static_cast<size_t>(p - buf.data());
142 buf.remove_prefix(bytes_formatted);
143 return bytes_formatted;
144 }
145
146 // Copies into `dst` as many bytes of `src` as will fit, then advances `dst`
147 // past the copied bytes and returns the number of bytes written.
AppendTruncated(absl::string_view src,absl::Span<char> & dst)148 size_t AppendTruncated(absl::string_view src, absl::Span<char>& dst) {
149 if (src.size() > dst.size()) src = src.substr(0, dst.size());
150 memcpy(dst.data(), src.data(), src.size());
151 dst.remove_prefix(src.size());
152 return src.size();
153 }
154
FormatLineNumber(int line,absl::Span<char> & buf)155 size_t FormatLineNumber(int line, absl::Span<char>& buf) {
156 constexpr size_t kLineFieldMaxLen =
157 sizeof(":] ") + (1 + std::numeric_limits<int>::digits10 + 1) - sizeof("");
158 if (ABSL_PREDICT_FALSE(buf.size() < kLineFieldMaxLen)) {
159 // As above, we don't bother trying to truncate this if the buffer is too
160 // short and it should never happen.
161 buf.remove_suffix(buf.size());
162 return 0;
163 }
164 char* p = buf.data();
165 *p++ = ':';
166 p = absl::numbers_internal::FastIntToBuffer(line, p);
167 *p++ = ']';
168 *p++ = ' ';
169 const size_t bytes_formatted = static_cast<size_t>(p - buf.data());
170 buf.remove_prefix(bytes_formatted);
171 return bytes_formatted;
172 }
173
174 } // namespace
175
FormatLogMessage(absl::LogSeverity severity,absl::CivilSecond civil_second,absl::Duration subsecond,log_internal::Tid tid,absl::string_view basename,int line,absl::string_view message)176 std::string FormatLogMessage(absl::LogSeverity severity,
177 absl::CivilSecond civil_second,
178 absl::Duration subsecond, log_internal::Tid tid,
179 absl::string_view basename, int line,
180 absl::string_view message) {
181 return absl::StrFormat(
182 "%c%02d%02d %02d:%02d:%02d.%06d %7d %s:%d] %s",
183 absl::LogSeverityName(severity)[0], civil_second.month(),
184 civil_second.day(), civil_second.hour(), civil_second.minute(),
185 civil_second.second(), absl::ToInt64Microseconds(subsecond), tid,
186 basename, line, message);
187 }
188
189 // This method is fairly hot, and the library always passes a huge `buf`, so we
190 // save some bounds-checking cycles by not trying to do precise truncation.
191 // Truncating at a field boundary is probably a better UX anyway.
192 //
193 // The prefix is written in three parts, each of which does a single
194 // bounds-check and truncation:
195 // 1. severity, timestamp, and thread ID
196 // 2. filename
197 // 3. line number and bracket
FormatLogPrefix(absl::LogSeverity severity,absl::Time timestamp,log_internal::Tid tid,absl::string_view basename,int line,absl::Span<char> & buf)198 size_t FormatLogPrefix(absl::LogSeverity severity, absl::Time timestamp,
199 log_internal::Tid tid, absl::string_view basename,
200 int line, absl::Span<char>& buf) {
201 auto prefix_size = FormatBoundedFields(severity, timestamp, tid, buf);
202 prefix_size += AppendTruncated(basename, buf);
203 prefix_size += FormatLineNumber(line, buf);
204 return prefix_size;
205 }
206
207 } // namespace log_internal
208 ABSL_NAMESPACE_END
209 } // namespace absl
210