]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/port/win/win_logger.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / rocksdb / port / win / win_logger.cc
1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9 //
10 // Logger implementation that can be shared by all environments
11 // where enough posix functionality is available.
12
13 #include "port/win/win_logger.h"
14 #include "port/win/io_win.h"
15
16 #include <algorithm>
17 #include <stdio.h>
18 #include <time.h>
19 #include <fcntl.h>
20 #include <atomic>
21
22 #include "rocksdb/env.h"
23
24 #include "monitoring/iostats_context_imp.h"
25 #include "port/sys_time.h"
26
27 namespace rocksdb {
28
29 namespace port {
30
31 WinLogger::WinLogger(uint64_t (*gettid)(), Env* env, HANDLE file,
32 const InfoLogLevel log_level)
33 : Logger(log_level),
34 file_(file),
35 gettid_(gettid),
36 log_size_(0),
37 last_flush_micros_(0),
38 env_(env),
39 flush_pending_(false) {
40 assert(file_ != NULL);
41 assert(file_ != INVALID_HANDLE_VALUE);
42 }
43
44 void WinLogger::DebugWriter(const char* str, int len) {
45 assert(file_ != INVALID_HANDLE_VALUE);
46 DWORD bytesWritten = 0;
47 BOOL ret = WriteFile(file_, str, len, &bytesWritten, NULL);
48 if (ret == FALSE) {
49 std::string errSz = GetWindowsErrSz(GetLastError());
50 fprintf(stderr, errSz.c_str());
51 }
52 }
53
54 WinLogger::~WinLogger() {
55 CloseInternal();
56 }
57
58 Status WinLogger::CloseImpl() {
59 return CloseInternal();
60 }
61
62 Status WinLogger::CloseInternal() {
63 Status s;
64 if (INVALID_HANDLE_VALUE != file_) {
65 BOOL ret = FlushFileBuffers(file_);
66 if (ret == 0) {
67 auto lastError = GetLastError();
68 s = IOErrorFromWindowsError("Failed to flush LOG on Close() ",
69 lastError);
70 }
71 ret = CloseHandle(file_);
72 // On error the return value is zero
73 if (ret == 0 && s.ok()) {
74 auto lastError = GetLastError();
75 s = IOErrorFromWindowsError("Failed to flush LOG on Close() ",
76 lastError);
77 }
78 file_ = INVALID_HANDLE_VALUE;
79 closed_ = true;
80 }
81 return s;
82 }
83
84 void WinLogger::Flush() {
85 assert(file_ != INVALID_HANDLE_VALUE);
86 if (flush_pending_) {
87 flush_pending_ = false;
88 // With Windows API writes go to OS buffers directly so no fflush needed
89 // unlike with C runtime API. We don't flush all the way to disk
90 // for perf reasons.
91 }
92
93 last_flush_micros_ = env_->NowMicros();
94 }
95
96 void WinLogger::Logv(const char* format, va_list ap) {
97 IOSTATS_TIMER_GUARD(logger_nanos);
98 assert(file_ != INVALID_HANDLE_VALUE);
99
100 const uint64_t thread_id = (*gettid_)();
101
102 // We try twice: the first time with a fixed-size stack allocated buffer,
103 // and the second time with a much larger dynamically allocated buffer.
104 char buffer[500];
105 std::unique_ptr<char[]> largeBuffer;
106 for (int iter = 0; iter < 2; ++iter) {
107 char* base;
108 int bufsize;
109 if (iter == 0) {
110 bufsize = sizeof(buffer);
111 base = buffer;
112 } else {
113 bufsize = 30000;
114 largeBuffer.reset(new char[bufsize]);
115 base = largeBuffer.get();
116 }
117
118 char* p = base;
119 char* limit = base + bufsize;
120
121 struct timeval now_tv;
122 gettimeofday(&now_tv, nullptr);
123 const time_t seconds = now_tv.tv_sec;
124 struct tm t;
125 localtime_s(&t, &seconds);
126 p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
127 t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
128 t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec),
129 static_cast<long long unsigned int>(thread_id));
130
131 // Print the message
132 if (p < limit) {
133 va_list backup_ap;
134 va_copy(backup_ap, ap);
135 int done = vsnprintf(p, limit - p, format, backup_ap);
136 if (done > 0) {
137 p += done;
138 } else {
139 continue;
140 }
141 va_end(backup_ap);
142 }
143
144 // Truncate to available space if necessary
145 if (p >= limit) {
146 if (iter == 0) {
147 continue; // Try again with larger buffer
148 } else {
149 p = limit - 1;
150 }
151 }
152
153 // Add newline if necessary
154 if (p == base || p[-1] != '\n') {
155 *p++ = '\n';
156 }
157
158 assert(p <= limit);
159 const size_t write_size = p - base;
160
161 DWORD bytesWritten = 0;
162 BOOL ret = WriteFile(file_, base, static_cast<DWORD>(write_size),
163 &bytesWritten, NULL);
164 if (ret == FALSE) {
165 std::string errSz = GetWindowsErrSz(GetLastError());
166 fprintf(stderr, errSz.c_str());
167 }
168
169 flush_pending_ = true;
170 assert((bytesWritten == write_size) || (ret == FALSE));
171 if (bytesWritten > 0) {
172 log_size_ += write_size;
173 }
174
175 uint64_t now_micros =
176 static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + now_tv.tv_usec;
177 if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) {
178 flush_pending_ = false;
179 // With Windows API writes go to OS buffers directly so no fflush needed
180 // unlike with C runtime API. We don't flush all the way to disk
181 // for perf reasons.
182 last_flush_micros_ = now_micros;
183 }
184 break;
185 }
186 }
187
188 size_t WinLogger::GetLogFileSize() const { return log_size_; }
189
190 }
191
192 } // namespace rocksdb