Coverage Report

Created: 2026-09-14 06:45

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