Coverage Report

Created: 2026-10-28 06:49

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