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