summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/port/win/win_logger.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/rocksdb/port/win/win_logger.cc192
1 files changed, 192 insertions, 0 deletions
diff --git a/src/rocksdb/port/win/win_logger.cc b/src/rocksdb/port/win/win_logger.cc
new file mode 100644
index 000000000..072ea419a
--- /dev/null
+++ b/src/rocksdb/port/win/win_logger.cc
@@ -0,0 +1,192 @@
+// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
+// This source code is licensed under both the GPLv2 (found in the
+// COPYING file in the root directory) and Apache 2.0 License
+// (found in the LICENSE.Apache file in the root directory).
+//
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+//
+// Logger implementation that can be shared by all environments
+// where enough posix functionality is available.
+
+#if defined(OS_WIN)
+
+#include "port/win/win_logger.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <algorithm>
+#include <atomic>
+
+#include "monitoring/iostats_context_imp.h"
+#include "port/sys_time.h"
+#include "port/win/env_win.h"
+#include "port/win/io_win.h"
+#include "rocksdb/env.h"
+#include "rocksdb/system_clock.h"
+
+namespace ROCKSDB_NAMESPACE {
+
+namespace port {
+
+WinLogger::WinLogger(uint64_t (*gettid)(), SystemClock* clock, HANDLE file,
+ const InfoLogLevel log_level)
+ : Logger(log_level),
+ file_(file),
+ gettid_(gettid),
+ log_size_(0),
+ last_flush_micros_(0),
+ clock_(clock),
+ flush_pending_(false) {
+ assert(file_ != NULL);
+ assert(file_ != INVALID_HANDLE_VALUE);
+}
+
+void WinLogger::DebugWriter(const char* str, int len) {
+ assert(file_ != INVALID_HANDLE_VALUE);
+ DWORD bytesWritten = 0;
+ BOOL ret = WriteFile(file_, str, len, &bytesWritten, NULL);
+ if (ret == FALSE) {
+ std::string errSz = GetWindowsErrSz(GetLastError());
+ fprintf(stderr, "%s", errSz.c_str());
+ }
+}
+
+WinLogger::~WinLogger() { CloseInternal().PermitUncheckedError(); }
+
+Status WinLogger::CloseImpl() { return CloseInternal(); }
+
+Status WinLogger::CloseInternal() {
+ Status s;
+ if (INVALID_HANDLE_VALUE != file_) {
+ BOOL ret = FlushFileBuffers(file_);
+ if (ret == 0) {
+ auto lastError = GetLastError();
+ s = IOErrorFromWindowsError("Failed to flush LOG on Close() ", lastError);
+ }
+ ret = CloseHandle(file_);
+ // On error the return value is zero
+ if (ret == 0 && s.ok()) {
+ auto lastError = GetLastError();
+ s = IOErrorFromWindowsError("Failed to flush LOG on Close() ", lastError);
+ }
+ file_ = INVALID_HANDLE_VALUE;
+ closed_ = true;
+ }
+ return s;
+}
+
+void WinLogger::Flush() {
+ assert(file_ != INVALID_HANDLE_VALUE);
+ if (flush_pending_) {
+ flush_pending_ = false;
+ // With Windows API writes go to OS buffers directly so no fflush needed
+ // unlike with C runtime API. We don't flush all the way to disk
+ // for perf reasons.
+ }
+
+ last_flush_micros_ = clock_->NowMicros();
+}
+
+void WinLogger::Logv(const char* format, va_list ap) {
+ IOSTATS_TIMER_GUARD(logger_nanos);
+ assert(file_ != INVALID_HANDLE_VALUE);
+
+ const uint64_t thread_id = (*gettid_)();
+
+ // We try twice: the first time with a fixed-size stack allocated buffer,
+ // and the second time with a much larger dynamically allocated buffer.
+ char buffer[500];
+ std::unique_ptr<char[]> largeBuffer;
+ for (int iter = 0; iter < 2; ++iter) {
+ char* base;
+ int bufsize;
+ if (iter == 0) {
+ bufsize = sizeof(buffer);
+ base = buffer;
+ } else {
+ bufsize = 30000;
+ largeBuffer.reset(new char[bufsize]);
+ base = largeBuffer.get();
+ }
+
+ char* p = base;
+ char* limit = base + bufsize;
+
+ port::TimeVal now_tv;
+ port::GetTimeOfDay(&now_tv, nullptr);
+ const time_t seconds = now_tv.tv_sec;
+ struct tm t;
+ localtime_s(&t, &seconds);
+ p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
+ t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
+ t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec),
+ static_cast<long long unsigned int>(thread_id));
+
+ // Print the message
+ if (p < limit) {
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int done = vsnprintf(p, limit - p, format, backup_ap);
+ if (done > 0) {
+ p += done;
+ } else {
+ continue;
+ }
+ va_end(backup_ap);
+ }
+
+ // Truncate to available space if necessary
+ if (p >= limit) {
+ if (iter == 0) {
+ continue; // Try again with larger buffer
+ } else {
+ p = limit - 1;
+ }
+ }
+
+ // Add newline if necessary
+ if (p == base || p[-1] != '\n') {
+ *p++ = '\n';
+ }
+
+ assert(p <= limit);
+ const size_t write_size = p - base;
+
+ DWORD bytesWritten = 0;
+ BOOL ret = WriteFile(file_, base, static_cast<DWORD>(write_size),
+ &bytesWritten, NULL);
+ if (ret == FALSE) {
+ std::string errSz = GetWindowsErrSz(GetLastError());
+ fprintf(stderr, "%s", errSz.c_str());
+ }
+
+ flush_pending_ = true;
+ assert((bytesWritten == write_size) || (ret == FALSE));
+ if (bytesWritten > 0) {
+ log_size_ += write_size;
+ }
+
+ uint64_t now_micros =
+ static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + now_tv.tv_usec;
+ if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) {
+ flush_pending_ = false;
+ // With Windows API writes go to OS buffers directly so no fflush needed
+ // unlike with C runtime API. We don't flush all the way to disk
+ // for perf reasons.
+ last_flush_micros_ = now_micros;
+ }
+ break;
+ }
+}
+
+size_t WinLogger::GetLogFileSize() const { return log_size_; }
+
+} // namespace port
+
+} // namespace ROCKSDB_NAMESPACE
+
+#endif