xref: /aosp_15_r20/external/leveldb/util/windows_logger.h (revision 9507f98c5f32dee4b5f9e4a38cd499f3ff5c4490)
1 // Copyright (c) 2018 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 for the Windows platform.
6 
7 #ifndef STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
8 #define STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
9 
10 #include <cassert>
11 #include <cstdarg>
12 #include <cstdio>
13 #include <ctime>
14 #include <sstream>
15 #include <thread>
16 
17 #include "leveldb/env.h"
18 
19 namespace leveldb {
20 
21 class WindowsLogger final : public Logger {
22  public:
23   // Creates a logger that writes to the given file.
24   //
25   // The PosixLogger instance takes ownership of the file handle.
WindowsLogger(std::FILE * fp)26   explicit WindowsLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); }
27 
~WindowsLogger()28   ~WindowsLogger() override { std::fclose(fp_); }
29 
Logv(const char * format,std::va_list arguments)30   void Logv(const char* format, std::va_list arguments) override {
31     // Record the time as close to the Logv() call as possible.
32     SYSTEMTIME now_components;
33     ::GetLocalTime(&now_components);
34 
35     // Record the thread ID.
36     constexpr const int kMaxThreadIdSize = 32;
37     std::ostringstream thread_stream;
38     thread_stream << std::this_thread::get_id();
39     std::string thread_id = thread_stream.str();
40     if (thread_id.size() > kMaxThreadIdSize) {
41       thread_id.resize(kMaxThreadIdSize);
42     }
43 
44     // We first attempt to print into a stack-allocated buffer. If this attempt
45     // fails, we make a second attempt with a dynamically allocated buffer.
46     constexpr const int kStackBufferSize = 512;
47     char stack_buffer[kStackBufferSize];
48     static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),
49                   "sizeof(char) is expected to be 1 in C++");
50 
51     int dynamic_buffer_size = 0;  // Computed in the first iteration.
52     for (int iteration = 0; iteration < 2; ++iteration) {
53       const int buffer_size =
54           (iteration == 0) ? kStackBufferSize : dynamic_buffer_size;
55       char* const buffer =
56           (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
57 
58       // Print the header into the buffer.
59       int buffer_offset = std::snprintf(
60           buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",
61           now_components.wYear, now_components.wMonth, now_components.wDay,
62           now_components.wHour, now_components.wMinute, now_components.wSecond,
63           static_cast<int>(now_components.wMilliseconds * 1000),
64           thread_id.c_str());
65 
66       // The header can be at most 28 characters (10 date + 15 time +
67       // 3 delimiters) plus the thread ID, which should fit comfortably into the
68       // static buffer.
69       assert(buffer_offset <= 28 + kMaxThreadIdSize);
70       static_assert(28 + kMaxThreadIdSize < kStackBufferSize,
71                     "stack-allocated buffer may not fit the message header");
72       assert(buffer_offset < buffer_size);
73 
74       // Print the message into the buffer.
75       std::va_list arguments_copy;
76       va_copy(arguments_copy, arguments);
77       buffer_offset +=
78           std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset,
79                          format, arguments_copy);
80       va_end(arguments_copy);
81 
82       // The code below may append a newline at the end of the buffer, which
83       // requires an extra character.
84       if (buffer_offset >= buffer_size - 1) {
85         // The message did not fit into the buffer.
86         if (iteration == 0) {
87           // Re-run the loop and use a dynamically-allocated buffer. The buffer
88           // will be large enough for the log message, an extra newline and a
89           // null terminator.
90           dynamic_buffer_size = buffer_offset + 2;
91           continue;
92         }
93 
94         // The dynamically-allocated buffer was incorrectly sized. This should
95         // not happen, assuming a correct implementation of std::(v)snprintf.
96         // Fail in tests, recover by truncating the log message in production.
97         assert(false);
98         buffer_offset = buffer_size - 1;
99       }
100 
101       // Add a newline if necessary.
102       if (buffer[buffer_offset - 1] != '\n') {
103         buffer[buffer_offset] = '\n';
104         ++buffer_offset;
105       }
106 
107       assert(buffer_offset <= buffer_size);
108       std::fwrite(buffer, 1, buffer_offset, fp_);
109       std::fflush(fp_);
110 
111       if (iteration != 0) {
112         delete[] buffer;
113       }
114       break;
115     }
116   }
117 
118  private:
119   std::FILE* const fp_;
120 };
121 
122 }  // namespace leveldb
123 
124 #endif  // STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
125