// 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 #include #include #include #include #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 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(now_tv.tv_usec), static_cast(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(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(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