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.
26 explicit WindowsLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); }
27 28 ~WindowsLogger() override { std::fclose(fp_); }
29 30 void Logv(const char* format, 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 = 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 (v)snprintf. Fail
96 // 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