Coverage Report

Created: 2025-08-25 06:55

/src/leveldb/util/posix_logger.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file. See the AUTHORS file for names of contributors.
4
//
5
// Logger implementation that can be shared by all environments
6
// where enough posix functionality is available.
7
8
#ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
9
#define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
10
11
#include <sys/time.h>
12
13
#include <cassert>
14
#include <cstdarg>
15
#include <cstdio>
16
#include <ctime>
17
#include <sstream>
18
#include <thread>
19
20
#include "leveldb/env.h"
21
22
namespace leveldb {
23
24
class PosixLogger final : public Logger {
25
 public:
26
  // Creates a logger that writes to the given file.
27
  //
28
  // The PosixLogger instance takes ownership of the file handle.
29
118k
  explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); }
30
31
118k
  ~PosixLogger() override { std::fclose(fp_); }
32
33
769k
  void Logv(const char* format, std::va_list arguments) override {
34
    // Record the time as close to the Logv() call as possible.
35
769k
    struct ::timeval now_timeval;
36
769k
    ::gettimeofday(&now_timeval, nullptr);
37
769k
    const std::time_t now_seconds = now_timeval.tv_sec;
38
769k
    struct std::tm now_components;
39
769k
    ::localtime_r(&now_seconds, &now_components);
40
41
    // Record the thread ID.
42
769k
    constexpr const int kMaxThreadIdSize = 32;
43
769k
    std::ostringstream thread_stream;
44
769k
    thread_stream << std::this_thread::get_id();
45
769k
    std::string thread_id = thread_stream.str();
46
769k
    if (thread_id.size() > kMaxThreadIdSize) {
47
0
      thread_id.resize(kMaxThreadIdSize);
48
0
    }
49
50
    // We first attempt to print into a stack-allocated buffer. If this attempt
51
    // fails, we make a second attempt with a dynamically allocated buffer.
52
769k
    constexpr const int kStackBufferSize = 512;
53
769k
    char stack_buffer[kStackBufferSize];
54
769k
    static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),
55
769k
                  "sizeof(char) is expected to be 1 in C++");
56
57
769k
    int dynamic_buffer_size = 0;  // Computed in the first iteration.
58
775k
    for (int iteration = 0; iteration < 2; ++iteration) {
59
775k
      const int buffer_size =
60
775k
          (iteration == 0) ? kStackBufferSize : dynamic_buffer_size;
61
775k
      char* const buffer =
62
775k
          (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
63
64
      // Print the header into the buffer.
65
775k
      int buffer_offset = std::snprintf(
66
775k
          buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",
67
775k
          now_components.tm_year + 1900, now_components.tm_mon + 1,
68
775k
          now_components.tm_mday, now_components.tm_hour, now_components.tm_min,
69
775k
          now_components.tm_sec, static_cast<int>(now_timeval.tv_usec),
70
775k
          thread_id.c_str());
71
72
      // The header can be at most 28 characters (10 date + 15 time +
73
      // 3 delimiters) plus the thread ID, which should fit comfortably into the
74
      // static buffer.
75
775k
      assert(buffer_offset <= 28 + kMaxThreadIdSize);
76
775k
      static_assert(28 + kMaxThreadIdSize < kStackBufferSize,
77
775k
                    "stack-allocated buffer may not fit the message header");
78
775k
      assert(buffer_offset < buffer_size);
79
80
      // Print the message into the buffer.
81
775k
      std::va_list arguments_copy;
82
775k
      va_copy(arguments_copy, arguments);
83
775k
      buffer_offset +=
84
775k
          std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset,
85
775k
                         format, arguments_copy);
86
775k
      va_end(arguments_copy);
87
88
      // The code below may append a newline at the end of the buffer, which
89
      // requires an extra character.
90
775k
      if (buffer_offset >= buffer_size - 1) {
91
        // The message did not fit into the buffer.
92
5.13k
        if (iteration == 0) {
93
          // Re-run the loop and use a dynamically-allocated buffer. The buffer
94
          // will be large enough for the log message, an extra newline and a
95
          // null terminator.
96
5.13k
          dynamic_buffer_size = buffer_offset + 2;
97
5.13k
          continue;
98
5.13k
        }
99
100
        // The dynamically-allocated buffer was incorrectly sized. This should
101
        // not happen, assuming a correct implementation of std::(v)snprintf.
102
        // Fail in tests, recover by truncating the log message in production.
103
0
        assert(false);
104
0
        buffer_offset = buffer_size - 1;
105
0
      }
106
107
      // Add a newline if necessary.
108
769k
      if (buffer[buffer_offset - 1] != '\n') {
109
364k
        buffer[buffer_offset] = '\n';
110
364k
        ++buffer_offset;
111
364k
      }
112
113
769k
      assert(buffer_offset <= buffer_size);
114
769k
      std::fwrite(buffer, 1, buffer_offset, fp_);
115
769k
      std::fflush(fp_);
116
117
769k
      if (iteration != 0) {
118
5.13k
        delete[] buffer;
119
5.13k
      }
120
769k
      break;
121
775k
    }
122
769k
  }
123
124
 private:
125
  std::FILE* const fp_;
126
};
127
128
}  // namespace leveldb
129
130
#endif  // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_