/root/bitcoin/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 | 0 | explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); } |
30 | | |
31 | 0 | ~PosixLogger() override { std::fclose(fp_); } |
32 | | |
33 | 0 | void Logv(const char* format, va_list arguments) override { |
34 | | // Record the time as close to the Logv() call as possible. |
35 | 0 | struct ::timeval now_timeval; |
36 | 0 | ::gettimeofday(&now_timeval, nullptr); |
37 | 0 | const std::time_t now_seconds = now_timeval.tv_sec; |
38 | 0 | struct std::tm now_components; |
39 | 0 | ::localtime_r(&now_seconds, &now_components); |
40 | | |
41 | | // Record the thread ID. |
42 | 0 | constexpr const int kMaxThreadIdSize = 32; |
43 | 0 | std::ostringstream thread_stream; |
44 | 0 | thread_stream << std::this_thread::get_id(); |
45 | 0 | std::string thread_id = thread_stream.str(); |
46 | 0 | 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 | 0 | constexpr const int kStackBufferSize = 512; |
53 | 0 | char stack_buffer[kStackBufferSize]; |
54 | 0 | static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize), |
55 | 0 | "sizeof(char) is expected to be 1 in C++"); |
56 | |
|
57 | 0 | int dynamic_buffer_size = 0; // Computed in the first iteration. |
58 | 0 | for (int iteration = 0; iteration < 2; ++iteration) { |
59 | 0 | const int buffer_size = |
60 | 0 | (iteration == 0) ? kStackBufferSize : dynamic_buffer_size; |
61 | 0 | char* const buffer = |
62 | 0 | (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size]; |
63 | | |
64 | | // Print the header into the buffer. |
65 | 0 | int buffer_offset = snprintf( |
66 | 0 | buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", |
67 | 0 | now_components.tm_year + 1900, now_components.tm_mon + 1, |
68 | 0 | now_components.tm_mday, now_components.tm_hour, now_components.tm_min, |
69 | 0 | now_components.tm_sec, static_cast<int>(now_timeval.tv_usec), |
70 | 0 | 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 | 0 | assert(buffer_offset <= 28 + kMaxThreadIdSize); |
76 | 0 | static_assert(28 + kMaxThreadIdSize < kStackBufferSize, |
77 | 0 | "stack-allocated buffer may not fit the message header"); |
78 | 0 | assert(buffer_offset < buffer_size); |
79 | | |
80 | | // Print the message into the buffer. |
81 | 0 | std::va_list arguments_copy; |
82 | 0 | va_copy(arguments_copy, arguments); |
83 | 0 | buffer_offset += |
84 | 0 | std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset, |
85 | 0 | format, arguments_copy); |
86 | 0 | 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 | 0 | if (buffer_offset >= buffer_size - 1) { |
91 | | // The message did not fit into the buffer. |
92 | 0 | 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 | 0 | dynamic_buffer_size = buffer_offset + 2; |
97 | 0 | continue; |
98 | 0 | } |
99 | | |
100 | | // The dynamically-allocated buffer was incorrectly sized. This should |
101 | | // not happen, assuming a correct implementation of (v)snprintf. Fail |
102 | | // 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 | 0 | if (buffer[buffer_offset - 1] != '\n') { |
109 | 0 | buffer[buffer_offset] = '\n'; |
110 | 0 | ++buffer_offset; |
111 | 0 | } |
112 | |
|
113 | 0 | assert(buffer_offset <= buffer_size); |
114 | 0 | std::fwrite(buffer, 1, buffer_offset, fp_); |
115 | 0 | std::fflush(fp_); |
116 | |
|
117 | 0 | if (iteration != 0) { |
118 | 0 | delete[] buffer; |
119 | 0 | } |
120 | 0 | break; |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | private: |
125 | | std::FILE* const fp_; |
126 | | }; |
127 | | |
128 | | } // namespace leveldb |
129 | | |
130 | | #endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ |