/Users/deen/code/yugabyte-db/src/yb/rocksdb/util/posix_logger.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under the BSD-style license found in the |
3 | | // LICENSE file in the root directory of this source tree. An additional grant |
4 | | // of patent rights can be found in the PATENTS file in the same directory. |
5 | | // |
6 | | // The following only applies to changes made to this file as part of YugaByte development. |
7 | | // |
8 | | // Portions Copyright (c) YugaByte, Inc. |
9 | | // |
10 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
11 | | // in compliance with the License. You may obtain a copy of the License at |
12 | | // |
13 | | // http://www.apache.org/licenses/LICENSE-2.0 |
14 | | // |
15 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
16 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
17 | | // or implied. See the License for the specific language governing permissions and limitations |
18 | | // under the License. |
19 | | // |
20 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
21 | | // Use of this source code is governed by a BSD-style license that can be |
22 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
23 | | // |
24 | | // Logger implementation that can be shared by all environments |
25 | | // where enough posix functionality is available. |
26 | | |
27 | | #ifndef YB_ROCKSDB_UTIL_POSIX_LOGGER_H |
28 | | #define YB_ROCKSDB_UTIL_POSIX_LOGGER_H |
29 | | |
30 | | #include <stdio.h> |
31 | | #include <time.h> |
32 | | #include <fcntl.h> |
33 | | |
34 | | #include <algorithm> |
35 | | #include <atomic> |
36 | | |
37 | | #ifdef __linux__ |
38 | | #ifndef FALLOC_FL_KEEP_SIZE |
39 | | #include <linux/falloc.h> |
40 | | #endif |
41 | | #endif |
42 | | |
43 | | #include "yb/rocksdb/port/sys_time.h" |
44 | | |
45 | | #include "yb/rocksdb/env.h" |
46 | | #include "yb/util/stats/iostats_context_imp.h" |
47 | | #include "yb/rocksdb/util/sync_point.h" |
48 | | |
49 | | namespace rocksdb { |
50 | | |
51 | | const int kDebugLogChunkSize = 128 * 1024; |
52 | | |
53 | | class PosixLogger : public Logger { |
54 | | private: |
55 | | FILE* file_; |
56 | | uint64_t (*gettid_)(); // Return the thread id for the current thread |
57 | | std::atomic_size_t log_size_; |
58 | | int fd_; |
59 | | static const uint64_t flush_every_seconds_ = 5; |
60 | | std::atomic_uint_fast64_t last_flush_micros_; |
61 | | Env* env_; |
62 | | std::atomic<bool> flush_pending_; |
63 | | public: |
64 | | PosixLogger(FILE* f, uint64_t (*gettid)(), Env* env, |
65 | | const InfoLogLevel log_level = InfoLogLevel::ERROR_LEVEL) |
66 | | : Logger(log_level), |
67 | | file_(f), |
68 | | gettid_(gettid), |
69 | | log_size_(0), |
70 | | fd_(fileno(f)), |
71 | | last_flush_micros_(0), |
72 | | env_(env), |
73 | 21.0k | flush_pending_(false) {} |
74 | 21.0k | virtual ~PosixLogger() { |
75 | 21.0k | fclose(file_); |
76 | 21.0k | } |
77 | 336k | virtual void Flush() override { |
78 | 336k | TEST_SYNC_POINT_CALLBACK("PosixLogger::Flush:BeginCallback", nullptr); |
79 | 336k | { |
80 | 336k | bool expected_flush_pending = true; |
81 | | // TODO: use a weaker memory order? |
82 | 336k | if (flush_pending_.compare_exchange_strong(expected_flush_pending, false)) { |
83 | 257k | fflush(file_); |
84 | 257k | } |
85 | 336k | } |
86 | 336k | last_flush_micros_ = env_->NowMicros(); |
87 | 336k | } |
88 | | |
89 | | using Logger::Logv; |
90 | 1.95M | virtual void Logv(const char* format, va_list ap) override { |
91 | 1.95M | IOSTATS_TIMER_GUARD(logger_nanos); |
92 | | |
93 | 1.95M | const uint64_t thread_id = (*gettid_)(); |
94 | | |
95 | | // We try twice: the first time with a fixed-size stack allocated buffer, |
96 | | // and the second time with a much larger dynamically allocated buffer. |
97 | 1.95M | char buffer[500]; |
98 | 2.02M | for (int iter = 0; iter < 22.02M ; iter++62.9k ) { |
99 | 2.02M | char* base; |
100 | 2.02M | int bufsize; |
101 | 2.02M | if (iter == 0) { |
102 | 1.95M | bufsize = sizeof(buffer); |
103 | 1.95M | base = buffer; |
104 | 1.95M | } else { |
105 | 62.9k | bufsize = 30000; |
106 | 62.9k | base = new char[bufsize]; |
107 | 62.9k | } |
108 | 2.02M | char* p = base; |
109 | 2.02M | char* limit = base + bufsize; |
110 | | |
111 | 2.02M | struct timeval now_tv; |
112 | 2.02M | gettimeofday(&now_tv, nullptr); |
113 | 2.02M | const time_t seconds = now_tv.tv_sec; |
114 | 2.02M | struct tm t; |
115 | 2.02M | localtime_r(&seconds, &t); |
116 | 2.02M | p += snprintf(p, limit - p, |
117 | 2.02M | "%04d/%02d/%02d-%02d:%02d:%02d.%06d %" PRIx64 " ", |
118 | 2.02M | t.tm_year + 1900, |
119 | 2.02M | t.tm_mon + 1, |
120 | 2.02M | t.tm_mday, |
121 | 2.02M | t.tm_hour, |
122 | 2.02M | t.tm_min, |
123 | 2.02M | t.tm_sec, |
124 | 2.02M | static_cast<int>(now_tv.tv_usec), |
125 | 2.02M | thread_id); |
126 | | |
127 | | // Print the message |
128 | 2.02M | if (p < limit2.02M ) { |
129 | 2.02M | va_list backup_ap; |
130 | 2.02M | va_copy(backup_ap, ap); |
131 | 2.02M | p += vsnprintf(p, limit - p, format, backup_ap); |
132 | 2.02M | va_end(backup_ap); |
133 | 2.02M | } |
134 | | |
135 | | // Truncate to available space if necessary |
136 | 2.02M | if (p >= limit) { |
137 | 62.9k | if (iter == 0) { |
138 | 62.9k | continue; // Try again with larger buffer |
139 | 62.9k | } else { |
140 | 1 | p = limit - 1; |
141 | 1 | } |
142 | 62.9k | } |
143 | | |
144 | | // Add newline if necessary |
145 | 1.95M | if (1.95M p == base1.95M || p[-1] != '\n') { |
146 | 1.62M | *p++ = '\n'; |
147 | 1.62M | } |
148 | | |
149 | 1.95M | assert(p <= limit); |
150 | 0 | const size_t write_size = p - base; |
151 | | |
152 | | #ifdef ROCKSDB_FALLOCATE_PRESENT |
153 | | // If this write would cross a boundary of kDebugLogChunkSize |
154 | | // space, pre-allocate more space to avoid overly large |
155 | | // allocations from filesystem allocsize options. |
156 | | const size_t log_size = log_size_; |
157 | | const size_t last_allocation_chunk = |
158 | | ((kDebugLogChunkSize - 1 + log_size) / kDebugLogChunkSize); |
159 | | const size_t desired_allocation_chunk = |
160 | | ((kDebugLogChunkSize - 1 + log_size + write_size) / |
161 | | kDebugLogChunkSize); |
162 | | if (last_allocation_chunk != desired_allocation_chunk) { |
163 | | fallocate( |
164 | | fd_, FALLOC_FL_KEEP_SIZE, 0, |
165 | | static_cast<off_t>(desired_allocation_chunk * kDebugLogChunkSize)); |
166 | | } |
167 | | #endif |
168 | | |
169 | 1.95M | size_t sz = fwrite(base, 1, write_size, file_); |
170 | | // TODO: use a weaker memory order? |
171 | 1.95M | flush_pending_.store(true); |
172 | 1.95M | assert(sz == write_size); |
173 | 1.95M | if (sz > 01.95M ) { |
174 | 1.95M | log_size_ += write_size; |
175 | 1.95M | } |
176 | 1.95M | uint64_t now_micros = static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + |
177 | 1.95M | now_tv.tv_usec; |
178 | 1.95M | if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) { |
179 | 25.9k | Flush(); |
180 | 25.9k | } |
181 | 1.95M | if (base != buffer) { |
182 | 62.9k | delete[] base; |
183 | 62.9k | } |
184 | 1.95M | break; |
185 | 2.02M | } |
186 | 1.95M | } |
187 | 897 | size_t GetLogFileSize() const override { return log_size_; } |
188 | | }; |
189 | | |
190 | | } // namespace rocksdb |
191 | | |
192 | | #endif // YB_ROCKSDB_UTIL_POSIX_LOGGER_H |