Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

133 rader
4.5 KiB

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