couchbase 4.2.6-dev → 4.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,97 @@
1
+ # Contributing
2
+
3
+ In addition to filing bugs, you may contribute by submitting patches to fix bugs in the library. Contributions may be submitted to <http://review.couchbase.com>. We use Gerrit as our code review system - and thus submitting a change requires an account there. While Github pull requests are not ignored, Gerrit pull requests will be responded to more quickly (and most likely with more detail).
4
+
5
+ For something to be accepted into the codebase, it must be formatted properly and have undergone proper testing.
6
+
7
+ ## Branches and Tags
8
+
9
+ * The `master` branch represents the mainline branch. The master branch typically consists of content going into the next release.
10
+ * For older series of the Couchbase Node.js SDK see the corresponding branches: 2.x = `v2` and 3.x = `v3`.
11
+
12
+ ## Contributing Patches
13
+
14
+ If you wish to contribute a new feature or a bug fix to the library, try to follow the following guidelines to help ensure your change gets merged upstream.
15
+
16
+ ### Before you begin
17
+
18
+ For any code change, ensure the new code you write looks similar to the code surrounding it. We have no strict code style policies, but do request that your code stand out as little as possible from its surrounding neighborhood (unless of course your change is stylistic in nature).
19
+
20
+ If your change is going to involve a substantial amount of time or effort, please attempt to discuss it with the project developers first who will provide assistance and direction where possible.
21
+
22
+ #### For new features
23
+
24
+ Ensure the feature you are adding does not already exist, and think about how this feature may be useful for other users. In general less intrusive changes are more likely to be accepted.
25
+
26
+ #### For fixing bugs
27
+
28
+ Ensure the bug you are fixing is actually a bug (and not a usage) error, and that it has not been fixed in a more recent version. Please read the [release notes](https://docs.couchbase.com/nodejs-sdk/current/project-docs/sdk-release-notes.html) as well as the [issue tracker](https://issues.couchbase.com/projects/JSCBC/issues/) to see a list of open and resolved issues.
29
+
30
+ ### Code Review
31
+
32
+ #### Signing up on Gerrit
33
+
34
+ Everything that is merged into the library goes through a code review process. The code review process is done via [Gerrit](http://review.couchbase.org).
35
+
36
+ To sign up for a Gerrit account, go to http://review.couchbase.org and click on the _Register_ link at the top right. Once you've signed in you will need to agree to the CLA (Contributor License Agreement) by going you your Gerrit account page and selecting the _Agreements_ link on the left. When you've done that, everything should flow through just fine. Be sure that you have registered your email address at http://review.couchbase.org/#/settings/contact as many sign-up methods won't pass emails along. Note that your email address in your code commit and in the Gerrit settings must match.
37
+
38
+ Add your public SSH key to Gerrit before submitting.
39
+
40
+ #### Setting up your fork with Gerrit
41
+
42
+ Assuming you have a repository created like so:
43
+
44
+ ```
45
+ $ git clone https://github.com/couchbase/couchnode.git
46
+ ```
47
+
48
+ you can simply perform two simple steps to get started with Gerrit:
49
+
50
+ ```
51
+ $ git remote add gerrit ssh://${USERNAME}@review.couchbase.org:29418/couchnode
52
+ $ scp -P 29418 ${USERNAME}@review.couchbase.org:hooks/commit-msg .git/hooks
53
+ $ chmod a+x .git/hooks/commit-msg
54
+ ```
55
+
56
+ The last change is required for annotating each commit message with a special header known as `Change-Id`. This allows Gerrit to group together different revisions of the same patch.
57
+
58
+ #### Pushing a changeset
59
+
60
+ Now that you have your change and a Gerrit account to push to, you need to upload the change for review. To do so, invoke the following incantation:
61
+
62
+ ```
63
+ $ git push gerrit HEAD:refs/for/master
64
+ ```
65
+
66
+ Where `gerrit` is the name of the _remote_ added earlier.
67
+
68
+ #### Pushing a new patchset
69
+
70
+ After a change has been pushed to Gerrit, further revisions can be made and then uploaded. These revisions are called patchsets and are associated with the the `Change-Id` created from the initial commit (see above). To push a new revision, simply ammend the commit (can also add the `--no-edit` option if no edits to the commit message are needed):
71
+
72
+ ```
73
+ $ git commit --amend
74
+ ```
75
+
76
+ Then push the revision to Gerrit:
77
+
78
+ ```
79
+ $ git push gerrit HEAD:refs/for/master
80
+ ```
81
+
82
+ Where `gerrit` is the name of the _remote_ added earlier.
83
+
84
+ #### Troubleshooting
85
+
86
+ You may encounter some errors when pushing. The most common are:
87
+
88
+ * "You are not authorized to push to this repository". You will get this if your account has not yet been approved. Please reach out in the [forums](https://www.couchbase.com/forums/c/node-js-sdk/12) if blocked.
89
+ * "Missing Change-Id". You need to install the `commit-msg` hook as described above. Note that even once you do this, you will need to ensure that any prior commits already have this header - this may be done by doing an interactive rebase (e.g. `git rebase -i origin/master` and selecting `reword` for all the commits; which will automatically fill in the Change-Id).
90
+
91
+ #### Reviewers
92
+
93
+ Once you've pushed your changeset you can add people to review. Currently these are:
94
+
95
+ * Jared Casey
96
+ * Matt Wozakowski
97
+ * Brett Lawson
@@ -72,16 +72,25 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
72
72
 
73
73
  void execute(std::chrono::milliseconds total_timeout, std::chrono::milliseconds udp_timeout)
74
74
  {
75
- CB_LOG_TRACE("Query DNS-SRV (UDP) address=\"{}:{}\", udp_timeout={}, total_timeout={}{:a}",
75
+ CB_LOG_TRACE("Query DNS-SRV (UDP) address=\"{}:{}\", udp_timeout={}, total_timeout={}",
76
76
  address_.to_string(),
77
77
  port_,
78
78
  udp_timeout,
79
- total_timeout,
80
- spdlog::to_hex(send_buf_));
79
+ total_timeout);
81
80
  asio::ip::udp::endpoint endpoint(address_, port_);
82
81
  udp_.open(endpoint.protocol());
82
+ CB_LOG_PROTOCOL("[DNS, UDP, OUT] host=\"{}\", port={}, buffer_size={}{:a}",
83
+ address_.to_string(),
84
+ port_,
85
+ send_buf_.size(),
86
+ spdlog::to_hex(send_buf_));
83
87
  udp_.async_send_to(
84
- asio::buffer(send_buf_), endpoint, [self = shared_from_this()](std::error_code ec1, std::size_t /* bytes_transferred */) mutable {
88
+ asio::buffer(send_buf_), endpoint, [self = shared_from_this()](std::error_code ec1, std::size_t bytes_transferred1) mutable {
89
+ CB_LOG_PROTOCOL("[DNS, UDP, OUT] host=\"{}\", port={}, rc={}, bytes_sent={}",
90
+ self->address_.to_string(),
91
+ self->port_,
92
+ ec1 ? ec1.message() : "ok",
93
+ bytes_transferred1);
85
94
  if (ec1) {
86
95
  self->udp_deadline_.cancel();
87
96
  CB_LOG_DEBUG("DNS UDP write operation has got error, retrying with TCP, address=\"{}:{}\", ec={}",
@@ -94,6 +103,13 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
94
103
  self->recv_buf_.resize(512);
95
104
  self->udp_.async_receive_from(
96
105
  asio::buffer(self->recv_buf_), self->udp_sender_, [self](std::error_code ec2, std::size_t bytes_transferred) mutable {
106
+ CB_LOG_PROTOCOL("[DNS, UDP, IN] host=\"{}\", port={}, rc={}, bytes_received={}{:a}",
107
+ self->address_.to_string(),
108
+ self->port_,
109
+ ec2 ? ec2.message() : "ok",
110
+ bytes_transferred,
111
+ spdlog::to_hex(self->recv_buf_.data(), self->recv_buf_.data() + bytes_transferred));
112
+
97
113
  self->udp_deadline_.cancel();
98
114
  if (ec2) {
99
115
  CB_LOG_DEBUG("DNS UDP read operation has got error, retrying with TCP, address=\"{}:{}\", ec={}",
@@ -168,10 +184,18 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
168
184
  auto send_size = static_cast<std::uint16_t>(self->send_buf_.size());
169
185
  self->send_buf_.insert(self->send_buf_.begin(), static_cast<std::uint8_t>(send_size & 0xffU));
170
186
  self->send_buf_.insert(self->send_buf_.begin(), static_cast<std::uint8_t>(send_size >> 8U));
171
- CB_LOG_TRACE(
172
- "Query DNS-SRV (TCP) address=\"{}:{}\"{:a}", self->address_.to_string(), self->port_, spdlog::to_hex(self->send_buf_));
187
+ CB_LOG_PROTOCOL("[DNS, TCP, OUT] host=\"{}\", port={}, buffer_size={}{:a}",
188
+ self->address_.to_string(),
189
+ self->port_,
190
+ self->send_buf_.size(),
191
+ spdlog::to_hex(self->send_buf_));
173
192
  asio::async_write(
174
- self->tcp_, asio::buffer(self->send_buf_), [self](std::error_code ec2, std::size_t /* bytes_transferred */) mutable {
193
+ self->tcp_, asio::buffer(self->send_buf_), [self](std::error_code ec2, std::size_t bytes_transferred2) mutable {
194
+ CB_LOG_PROTOCOL("[DNS, TCP, OUT] host=\"{}\", port={}, rc={}, bytes_sent={}",
195
+ self->address_.to_string(),
196
+ self->port_,
197
+ ec2 ? ec2.message() : "ok",
198
+ bytes_transferred2);
175
199
  if (ec2) {
176
200
  CB_LOG_DEBUG("DNS TCP write operation has been aborted, address=\"{}:{}\", ec={}",
177
201
  self->address_.to_string(),
@@ -186,7 +210,14 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
186
210
  asio::async_read(
187
211
  self->tcp_,
188
212
  asio::buffer(&self->recv_buf_size_, sizeof(std::uint16_t)),
189
- [self](std::error_code ec3, std::size_t /* bytes_transferred */) mutable {
213
+ [self](std::error_code ec3, std::size_t bytes_transferred3) mutable {
214
+ CB_LOG_PROTOCOL("[DNS, TCP, IN] host=\"{}\", port={}, rc={}, bytes_received={}{:a}",
215
+ self->address_.to_string(),
216
+ self->port_,
217
+ ec3 ? ec3.message() : "ok",
218
+ bytes_transferred3,
219
+ spdlog::to_hex(reinterpret_cast<std::uint8_t*>(&self->recv_buf_size_),
220
+ reinterpret_cast<std::uint8_t*>(&self->recv_buf_size_) + bytes_transferred3));
190
221
  if (ec3) {
191
222
  CB_LOG_DEBUG("DNS TCP buf size read operation has been aborted, address=\"{}:{}\", ec={}",
192
223
  self->address_.to_string(),
@@ -199,8 +230,15 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
199
230
  self->recv_buf_.resize(self->recv_buf_size_);
200
231
  CB_LOG_DEBUG("DNS TCP schedule read of {} bytes", self->recv_buf_size_);
201
232
  asio::async_read(
202
- self->tcp_, asio::buffer(self->recv_buf_), [self](std::error_code ec4, std::size_t bytes_transferred) mutable {
233
+ self->tcp_, asio::buffer(self->recv_buf_), [self](std::error_code ec4, std::size_t bytes_transferred4) mutable {
203
234
  self->deadline_.cancel();
235
+ CB_LOG_PROTOCOL("[DNS, TCP, IN] host=\"{}\", port={}, rc={}, bytes_received={}{:a}",
236
+ self->address_.to_string(),
237
+ self->port_,
238
+ ec4 ? ec4.message() : "ok",
239
+ bytes_transferred4,
240
+ spdlog::to_hex(self->recv_buf_.data(), self->recv_buf_.data() + bytes_transferred4));
241
+
204
242
  if (ec4) {
205
243
  CB_LOG_DEBUG("DNS TCP read operation has been aborted, address=\"{}:{}\", ec={}",
206
244
  self->address_.to_string(),
@@ -208,7 +246,7 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
208
246
  ec4.message());
209
247
  return self->handler_({ ec4 });
210
248
  }
211
- self->recv_buf_.resize(bytes_transferred);
249
+ self->recv_buf_.resize(bytes_transferred4);
212
250
  const dns_message message = dns_codec::decode(self->recv_buf_);
213
251
  dns_srv_response resp{ ec4 };
214
252
  resp.targets.reserve(message.answers.size());
@@ -31,7 +31,10 @@
31
31
 
32
32
  #include <couchbase/error_codes.hxx>
33
33
 
34
+ #include <spdlog/fmt/bin_to_hex.h>
35
+
34
36
  #include <asio.hpp>
37
+
35
38
  #include <list>
36
39
  #include <memory>
37
40
  #include <utility>
@@ -448,7 +451,20 @@ class http_session : public std::enable_shared_from_this<http_session>
448
451
  stream_->async_read_some(
449
452
  asio::buffer(input_buffer_), [self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
450
453
  if (ec == asio::error::operation_aborted || self->stopped_) {
454
+ CB_LOG_PROTOCOL("[HTTP, IN] type={}, host=\"{}\", rc={}, bytes_received={}",
455
+ self->type_,
456
+ self->info_.remote_address(),
457
+ ec ? ec.message() : "ok",
458
+ bytes_transferred);
451
459
  return;
460
+ } else {
461
+ CB_LOG_PROTOCOL("[HTTP, IN] type={}, host=\"{}\", rc={}, bytes_received={}{:a}",
462
+ self->type_,
463
+ self->info_.remote_address(),
464
+ ec ? ec.message() : "ok",
465
+ bytes_transferred,
466
+ spdlog::to_hex(self->input_buffer_.data(),
467
+ self->input_buffer_.data() + static_cast<std::ptrdiff_t>(bytes_transferred)));
452
468
  }
453
469
  self->last_active_ = std::chrono::steady_clock::now();
454
470
  if (ec) {
@@ -495,9 +511,16 @@ class http_session : public std::enable_shared_from_this<http_session>
495
511
  std::vector<asio::const_buffer> buffers;
496
512
  buffers.reserve(writing_buffer_.size());
497
513
  for (auto& buf : writing_buffer_) {
514
+ CB_LOG_PROTOCOL(
515
+ "[HTTP, OUT] type={}, host=\"{}\", buffer_size={}{:a}", type_, info_.remote_address(), buf.size(), spdlog::to_hex(buf));
498
516
  buffers.emplace_back(asio::buffer(buf));
499
517
  }
500
- stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t /* bytes_transferred */) {
518
+ stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
519
+ CB_LOG_PROTOCOL("[HTTP, OUT] type={}, host=\"{}\", rc={}, bytes_sent={}",
520
+ self->type_,
521
+ self->info_.remote_address(),
522
+ ec ? ec.message() : "ok",
523
+ bytes_transferred);
501
524
  if (ec == asio::error::operation_aborted || self->stopped_) {
502
525
  return;
503
526
  }
@@ -1365,7 +1365,20 @@ class mcbp_session_impl
1365
1365
  asio::buffer(input_buffer_),
1366
1366
  [self = shared_from_this(), stream_id = stream_->id()](std::error_code ec, std::size_t bytes_transferred) {
1367
1367
  if (ec == asio::error::operation_aborted || self->stopped_) {
1368
+ CB_LOG_PROTOCOL("[MCBP, IN] host=\"{}\", port={}, rc={}, bytes_received={}",
1369
+ self->endpoint_address_,
1370
+ self->endpoint_.port(),
1371
+ ec ? ec.message() : "ok",
1372
+ bytes_transferred);
1368
1373
  return;
1374
+ } else {
1375
+ CB_LOG_PROTOCOL("[MCBP, IN] host=\"{}\", port={}, rc={}, bytes_received={}{:a}",
1376
+ self->endpoint_address_,
1377
+ self->endpoint_.port(),
1378
+ ec ? ec.message() : "ok",
1379
+ bytes_transferred,
1380
+ spdlog::to_hex(self->input_buffer_.data(),
1381
+ self->input_buffer_.data() + static_cast<std::ptrdiff_t>(bytes_transferred)));
1369
1382
  }
1370
1383
  self->last_active_ = std::chrono::steady_clock::now();
1371
1384
  if (ec) {
@@ -1433,13 +1446,21 @@ class mcbp_session_impl
1433
1446
  std::vector<asio::const_buffer> buffers;
1434
1447
  buffers.reserve(writing_buffer_.size());
1435
1448
  for (auto& buf : writing_buffer_) {
1449
+ CB_LOG_PROTOCOL(
1450
+ "[MCBP, OUT] host=\"{}\", port={}, buffer_size={}{:a}", endpoint_address_, endpoint_.port(), buf.size(), spdlog::to_hex(buf));
1436
1451
  buffers.emplace_back(asio::buffer(buf));
1437
1452
  }
1438
- stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t /*unused*/) {
1453
+ stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
1454
+ CB_LOG_PROTOCOL("[MCBP, OUT] host=\"{}\", port={}, rc={}, bytes_sent={}",
1455
+ self->endpoint_address_,
1456
+ self->endpoint_.port(),
1457
+ ec ? ec.message() : "ok",
1458
+ bytes_transferred);
1439
1459
  if (ec == asio::error::operation_aborted || self->stopped_) {
1440
1460
  return;
1441
1461
  }
1442
1462
  self->last_active_ = std::chrono::steady_clock::now();
1463
+
1443
1464
  if (ec) {
1444
1465
  CB_LOG_ERROR(R"({} IO error while writing to the socket("{}"): {} ({}))",
1445
1466
  self->log_prefix_,
@@ -127,7 +127,7 @@ custom_rotating_file_sink<Mutex>::add_hook(const std::string& hook)
127
127
 
128
128
  // Payload shouldn't contain anything yet, overwrite it
129
129
  Expects(msg.payload.size() == 0);
130
- msg.payload = hook;
130
+ msg.payload = hookToAdd;
131
131
 
132
132
  spdlog::memory_buf_t formatted;
133
133
  formatter->format(msg, formatted);
@@ -22,7 +22,8 @@
22
22
  #include <spdlog/sinks/stdout_color_sinks.h>
23
23
  #include <spdlog/spdlog.h>
24
24
 
25
- static const std::string logger_name{ "couchbase_cxx_client_file_logger" };
25
+ static const std::string file_logger_name{ "couchbase_cxx_client_file_logger" };
26
+ static const std::string protocol_logger_name{ "couchbase_cxx_client_protocol_logger" };
26
27
 
27
28
  /**
28
29
  * Custom log pattern which the loggers will use.
@@ -39,7 +40,12 @@ static const std::string log_pattern{ "[%Y-%m-%d %T.%e] [%P,%t] [%^%l%$] %oms, %
39
40
  * messages and send them to the sinks, which do the actual writing (to file,
40
41
  * to stream etc.) or further processing.
41
42
  */
42
- static std::shared_ptr<spdlog::logger> file_logger;
43
+ static std::shared_ptr<spdlog::logger> file_logger{};
44
+
45
+ /**
46
+ * Instance of the protocol logger.
47
+ */
48
+ static std::shared_ptr<spdlog::logger> protocol_logger{};
43
49
 
44
50
  namespace couchbase::core::logger
45
51
  {
@@ -146,13 +152,11 @@ is_initialized()
146
152
  return file_logger != nullptr;
147
153
  }
148
154
 
149
- /**
150
- * Initialises the loggers. Called if the logger configuration is
151
- * specified in a separate settings object.
152
- */
153
- std::optional<std::string>
154
- create_file_logger(const configuration& logger_settings)
155
+ std::pair<std::optional<std::string>, std::shared_ptr<spdlog::logger>>
156
+ create_file_logger_impl(const std::string logger_name, const configuration& logger_settings)
155
157
  {
158
+ std::shared_ptr<spdlog::logger> logger{};
159
+
156
160
  auto fname = logger_settings.filename;
157
161
  auto buffersz = logger_settings.buffer_size;
158
162
  auto cyclesz = logger_settings.cycle_size;
@@ -218,7 +222,7 @@ create_file_logger(const configuration& logger_settings)
218
222
  spdlog::drop(logger_name);
219
223
 
220
224
  if (logger_settings.unit_test) {
221
- file_logger = std::make_shared<spdlog::logger>(logger_name, sink);
225
+ logger = std::make_shared<spdlog::logger>(logger_name, sink);
222
226
  } else {
223
227
  // Create the default thread pool for async logging
224
228
  spdlog::init_thread_pool(buffersz, 1);
@@ -226,23 +230,71 @@ create_file_logger(const configuration& logger_settings)
226
230
  // Get the thread pool so that we can actually construct the
227
231
  // object with already created sinks...
228
232
  auto tp = spdlog::thread_pool();
229
- file_logger = std::make_shared<spdlog::async_logger>(logger_name, sink, tp, spdlog::async_overflow_policy::block);
233
+ logger = std::make_shared<spdlog::async_logger>(logger_name, sink, tp, spdlog::async_overflow_policy::block);
230
234
  }
231
235
 
232
- file_logger->set_pattern(log_pattern);
233
- file_logger->set_level(translate_level(logger_settings.log_level));
236
+ logger->set_pattern(log_pattern);
237
+ logger->set_level(translate_level(logger_settings.log_level));
234
238
 
235
239
  // Set the flushing interval policy
236
240
  spdlog::flush_every(std::chrono::seconds(1));
237
241
 
238
- spdlog::register_logger(file_logger);
242
+ spdlog::register_logger(logger);
239
243
  } catch (const spdlog::spdlog_ex& ex) {
240
244
  std::string msg = std::string{ "Log initialization failed: " } + ex.what();
241
- return std::optional<std::string>{ msg };
245
+ return { msg, {} };
246
+ }
247
+ return { {}, logger };
248
+ }
249
+
250
+ /**
251
+ * Initialises the loggers. Called if the logger configuration is
252
+ * specified in a separate settings object.
253
+ */
254
+ std::optional<std::string>
255
+ create_file_logger(const configuration& logger_settings)
256
+ {
257
+ auto [error, logger] = create_file_logger_impl(file_logger_name, logger_settings);
258
+ if (error) {
259
+ return error;
260
+ }
261
+ file_logger = std::move(logger);
262
+ return {};
263
+ }
264
+
265
+ std::optional<std::string>
266
+ create_protocol_logger(const configuration& logger_settings)
267
+ {
268
+ if (logger_settings.filename.empty()) {
269
+ return "File name is missing";
270
+ }
271
+ auto config = logger_settings;
272
+ config.log_level = couchbase::core::logger::level::trace;
273
+ auto [error, logger] = create_file_logger_impl(protocol_logger_name, config);
274
+ if (error) {
275
+ return error;
242
276
  }
277
+ protocol_logger = std::move(logger);
243
278
  return {};
244
279
  }
245
280
 
281
+ bool
282
+ should_log_protocol()
283
+ {
284
+ return protocol_logger != nullptr;
285
+ }
286
+
287
+ namespace detail
288
+ {
289
+ void
290
+ log_protocol(const char* file, int line, const char* function, std::string_view msg)
291
+ {
292
+ if (should_log_protocol()) {
293
+ return protocol_logger->log(spdlog::source_loc{ file, line, function }, spdlog::level::level_enum::trace, msg);
294
+ }
295
+ }
296
+ } // namespace detail
297
+
246
298
  spdlog::logger*
247
299
  get()
248
300
  {
@@ -252,17 +304,20 @@ get()
252
304
  void
253
305
  reset()
254
306
  {
255
- spdlog::drop(logger_name);
307
+ spdlog::drop(file_logger_name);
256
308
  file_logger.reset();
309
+
310
+ spdlog::drop(protocol_logger_name);
311
+ protocol_logger.reset();
257
312
  }
258
313
 
259
314
  void
260
315
  create_blackhole_logger()
261
316
  {
262
317
  // delete if already exists
263
- spdlog::drop(logger_name);
318
+ spdlog::drop(file_logger_name);
264
319
 
265
- file_logger = std::make_shared<spdlog::logger>(logger_name, std::make_shared<spdlog::sinks::null_sink_mt>());
320
+ file_logger = std::make_shared<spdlog::logger>(file_logger_name, std::make_shared<spdlog::sinks::null_sink_mt>());
266
321
 
267
322
  file_logger->set_level(spdlog::level::off);
268
323
  file_logger->set_pattern(log_pattern);
@@ -274,11 +329,11 @@ void
274
329
  create_console_logger()
275
330
  {
276
331
  // delete if already exists
277
- spdlog::drop(logger_name);
332
+ spdlog::drop(file_logger_name);
278
333
 
279
334
  auto stderrsink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
280
335
 
281
- file_logger = std::make_shared<spdlog::logger>(logger_name, stderrsink);
336
+ file_logger = std::make_shared<spdlog::logger>(file_logger_name, stderrsink);
282
337
  file_logger->set_level(spdlog::level::info);
283
338
  file_logger->set_pattern(log_pattern);
284
339
 
@@ -322,8 +377,13 @@ void
322
377
  set_log_levels(level lvl)
323
378
  {
324
379
  auto level = translate_level(lvl);
325
- // Apply the function to each registered spdlog::logger
380
+ // Apply the function to each registered spdlog::logger except protocol logger
326
381
  spdlog::apply_all([level](std::shared_ptr<spdlog::logger> l) {
382
+ if (l->name() == protocol_logger_name) {
383
+ l->set_level(spdlog::level::trace);
384
+ return;
385
+ }
386
+
327
387
  try {
328
388
  l->set_level(level);
329
389
  } catch (const spdlog::spdlog_ex& e) {
@@ -54,6 +54,17 @@ level_from_str(const std::string& str);
54
54
  std::optional<std::string>
55
55
  create_file_logger(const configuration& logger_settings);
56
56
 
57
+ /**
58
+ * Protocol logger writes only communication logs with the nodes.
59
+ *
60
+ * It accepts the same settings as a file logger, and writes only to the filesystem.
61
+ *
62
+ * @param logger_settings
63
+ * @return
64
+ */
65
+ std::optional<std::string>
66
+ create_protocol_logger(const configuration& logger_settings);
67
+
57
68
  /**
58
69
  * Initialize the logger with the blackhole logger object
59
70
  *
@@ -144,6 +155,9 @@ set_log_levels(level lvl);
144
155
  bool
145
156
  should_log(level lvl);
146
157
 
158
+ bool
159
+ should_log_protocol();
160
+
147
161
  namespace detail
148
162
  {
149
163
  /**
@@ -153,6 +167,9 @@ namespace detail
153
167
  */
154
168
  void
155
169
  log(const char* file, int line, const char* function, level lvl, std::string_view msg);
170
+
171
+ void
172
+ log_protocol(const char* file, int line, const char* function, std::string_view msg);
156
173
  } // namespace detail
157
174
 
158
175
  /**
@@ -168,6 +185,13 @@ log(const char* file, int line, const char* function, level lvl, const String& m
168
185
  detail::log(file, line, function, lvl, fmt::format(msg, std::forward<Args>(args)...));
169
186
  }
170
187
 
188
+ template<typename String, typename... Args>
189
+ inline void
190
+ log_protocol(const char* file, int line, const char* function, const String& msg, Args&&... args)
191
+ {
192
+ detail::log_protocol(file, line, function, fmt::format(msg, std::forward<Args>(args)...));
193
+ }
194
+
171
195
  /**
172
196
  * Tell the logger to flush its buffers
173
197
  */
@@ -214,6 +238,13 @@ is_initialized();
214
238
  #define CB_LOG_CRITICAL(...) \
215
239
  COUCHBASE_LOG(__FILE__, __LINE__, COUCHBASE_LOGGER_FUNCTION, couchbase::core::logger::level::critical, __VA_ARGS__)
216
240
 
241
+ #define CB_LOG_PROTOCOL(...) \
242
+ do { \
243
+ if (couchbase::core::logger::should_log_protocol()) { \
244
+ couchbase::core::logger::log_protocol(__FILE__, __LINE__, COUCHBASE_LOGGER_FUNCTION, __VA_ARGS__); \
245
+ } \
246
+ } while (false)
247
+
217
248
  /**
218
249
  * Convenience macros which log with the given level, and message, if the given
219
250
  * level is currently enabled.
@@ -35,3 +35,9 @@
35
35
  * passing TLS trust certificate by value
36
36
  */
37
37
  #define COUCHBASE_CXX_CLIENT_CAN_PASS_TLS_TRUST_CERTIFICATE_BY_VALUE 1
38
+
39
+ /**
40
+ * Range scan is available in the core
41
+ * couchbase::core::range_scan_orchestrator and relevant options in the core API
42
+ */
43
+ #define COUCHBASE_CXX_CLIENT_CORE_HAS_RANGE_SCAN 1
@@ -412,13 +412,33 @@ class range_scan_orchestrator_impl
412
412
  return tl::unexpected(errc::common::invalid_argument);
413
413
  }
414
414
 
415
+ // Get the collection ID before starting any of the streams
416
+ {
417
+ auto barrier = std::make_shared<std::promise<tl::expected<get_collection_id_result, std::error_code>>>();
418
+ auto f = barrier->get_future();
419
+ get_collection_id_options const get_cid_options{ options_.retry_strategy, options_.timeout, options_.parent_span };
420
+ agent_.get_collection_id(scope_name_, collection_name_, get_cid_options, [barrier](auto result, auto ec) {
421
+ if (ec) {
422
+ return barrier->set_value(tl::unexpected(ec));
423
+ }
424
+ barrier->set_value(result);
425
+ });
426
+ auto get_cid_res = f.get();
427
+ if (!get_cid_res.has_value()) {
428
+ return tl::unexpected(get_cid_res.error());
429
+ }
430
+ collection_id_ = get_cid_res->collection_id;
431
+ }
432
+
415
433
  auto batch_time_limit = std::chrono::duration_cast<std::chrono::milliseconds>(0.9 * options_.timeout);
416
434
  range_scan_continue_options const continue_options{
417
435
  options_.batch_item_limit, options_.batch_byte_limit, batch_time_limit, options_.timeout, options_.retry_strategy,
418
436
  };
419
437
  for (std::uint16_t vbucket = 0; vbucket < gsl::narrow_cast<std::uint16_t>(vbucket_map_.size()); ++vbucket) {
420
438
  const range_scan_create_options create_options{
421
- scope_name_, collection_name_, scan_type_, options_.timeout, {}, vbucket_to_snapshot_requirements_[vbucket],
439
+ scope_name_, {},
440
+ scan_type_, options_.timeout,
441
+ collection_id_, vbucket_to_snapshot_requirements_[vbucket],
422
442
  options_.ids_only, options_.retry_strategy,
423
443
  };
424
444
 
@@ -653,6 +673,7 @@ class range_scan_orchestrator_impl
653
673
  topology::configuration::vbucket_map vbucket_map_;
654
674
  std::string scope_name_;
655
675
  std::string collection_name_;
676
+ std::uint32_t collection_id_;
656
677
  std::variant<std::monostate, range_scan, prefix_scan, sampling_scan> scan_type_;
657
678
  range_scan_orchestrator_options options_;
658
679
  std::map<std::size_t, std::optional<range_snapshot_requirements>> vbucket_to_snapshot_requirements_;
@@ -31,6 +31,7 @@ enum class bucket_capability {
31
31
  collections,
32
32
  durable_write,
33
33
  tombstoned_user_xattrs,
34
+ range_scan,
34
35
  };
35
36
 
36
37
  enum class cluster_capability {
@@ -67,6 +67,9 @@ struct fmt::formatter<couchbase::core::bucket_capability> {
67
67
  case couchbase::core::bucket_capability::tombstoned_user_xattrs:
68
68
  name = "tombstoned_user_xattrs";
69
69
  break;
70
+ case couchbase::core::bucket_capability::range_scan:
71
+ name = "range_scan";
72
+ break;
70
73
  }
71
74
  return format_to(ctx.out(), "{}", name);
72
75
  }
@@ -117,6 +117,11 @@ struct configuration {
117
117
  return cluster_capabilities.find(cluster_capability::n1ql_read_from_replica) != cluster_capabilities.end();
118
118
  }
119
119
 
120
+ [[nodiscard]] bool supports_range_scan() const
121
+ {
122
+ return bucket_capabilities.find(bucket_capability::range_scan) != bucket_capabilities.end();
123
+ }
124
+
120
125
  [[nodiscard]] bool ephemeral() const
121
126
  {
122
127
  // Use bucket capabilities to identify if couchapi is missing (then its ephemeral). If its null then
@@ -233,6 +233,8 @@ struct traits<couchbase::core::topology::configuration> {
233
233
  result.bucket_capabilities.insert(couchbase::core::bucket_capability::nodes_ext);
234
234
  } else if (name == "xattr") {
235
235
  result.bucket_capabilities.insert(couchbase::core::bucket_capability::xattr);
236
+ } else if (name == "rangeScan") {
237
+ result.bucket_capabilities.insert(couchbase::core::bucket_capability::range_scan);
236
238
  }
237
239
  }
238
240
  }
@@ -34,8 +34,9 @@ Execute one or more Analytics queries and print results to standard output.
34
34
  ### LOGGER OPTIONS
35
35
 
36
36
  <dl>
37
- <dt>`--log-level=LEVEL`</dt><dd>Log level (allowed values are: `trace`, `debug`, `info`, `warning`, `error`, `critical`, `off`). [default: `off`]
38
- <dt>`--log-output=PATH`</dt><dd>File to send logs (when is not set, logs will be written to STDERR).
37
+ <dt>`--log-level=LEVEL`</dt><dd>Log level (allowed values are: `trace`, `debug`, `info`, `warning`, `error`, `critical`, `off`). [default: `off`]</dd>
38
+ <dt>`--log-output=PATH`</dt><dd>File to send logs (when is not set, logs will be written to STDERR).</dd>
39
+ <dt>`--log-protocol=PATH`</dt><dd>File to send protocol logs.</dd>
39
40
  </dl>
40
41
 
41
42
  ### CONNECTION OPTIONS
@@ -34,8 +34,9 @@ Retrieve one or more documents from the server and print them to standard output
34
34
  ### LOGGER OPTIONS
35
35
 
36
36
  <dl>
37
- <dt>`--log-level=LEVEL`</dt><dd>Log level (allowed values are: `trace`, `debug`, `info`, `warning`, `error`, `critical`, `off`). [default: `off`]
38
- <dt>`--log-output=PATH`</dt><dd>File to send logs (when is not set, logs will be written to STDERR).
37
+ <dt>`--log-level=LEVEL`</dt><dd>Log level (allowed values are: `trace`, `debug`, `info`, `warning`, `error`, `critical`, `off`). [default: `off`]</dd>
38
+ <dt>`--log-output=PATH`</dt><dd>File to send logs (when is not set, logs will be written to STDERR).</dd>
39
+ <dt>`--log-protocol=PATH`</dt><dd>File to send protocol logs.</dd>
39
40
  </dl>
40
41
 
41
42
  ### CONNECTION OPTIONS
@@ -41,8 +41,9 @@ Run simple workload generator that sends GET/UPSERT requests with optional N1QL
41
41
  ### LOGGER OPTIONS
42
42
 
43
43
  <dl>
44
- <dt>`--log-level=LEVEL`</dt><dd>Log level (allowed values are: `trace`, `debug`, `info`, `warning`, `error`, `critical`, `off`). [default: `off`]
45
- <dt>`--log-output=PATH`</dt><dd>File to send logs (when is not set, logs will be written to STDERR).
44
+ <dt>`--log-level=LEVEL`</dt><dd>Log level (allowed values are: `trace`, `debug`, `info`, `warning`, `error`, `critical`, `off`). [default: `off`]</dd>
45
+ <dt>`--log-output=PATH`</dt><dd>File to send logs (when is not set, logs will be written to STDERR).</dd>
46
+ <dt>`--log-protocol=PATH`</dt><dd>File to send protocol logs.</dd>
46
47
  </dl>
47
48
 
48
49
  ### CONNECTION OPTIONS
@@ -42,8 +42,9 @@ Execute one or more N1QL queries and print results to standard output.
42
42
  ### LOGGER OPTIONS
43
43
 
44
44
  <dl>
45
- <dt>`--log-level=LEVEL`</dt><dd>Log level (allowed values are: `trace`, `debug`, `info`, `warning`, `error`, `critical`, `off`). [default: `off`]
46
- <dt>`--log-output=PATH`</dt><dd>File to send logs (when is not set, logs will be written to STDERR).
45
+ <dt>`--log-level=LEVEL`</dt><dd>Log level (allowed values are: `trace`, `debug`, `info`, `warning`, `error`, `critical`, `off`). [default: `off`]</dd>
46
+ <dt>`--log-output=PATH`</dt><dd>File to send logs (when is not set, logs will be written to STDERR).</dd>
47
+ <dt>`--log-protocol=PATH`</dt><dd>File to send protocol logs.</dd>
47
48
  </dl>
48
49
 
49
50
  ### CONNECTION OPTIONS
@@ -16,6 +16,7 @@
16
16
  */
17
17
 
18
18
  #include "core/logger/logger.hxx"
19
+ #include "core/logger/configuration.hxx"
19
20
 
20
21
  #include <spdlog/details/os.h>
21
22
  #include <spdlog/spdlog.h>
@@ -32,9 +33,15 @@ init_logger()
32
33
  if (auto env_val = spdlog::details::os::getenv("TEST_LOG_LEVEL"); !env_val.empty()) {
33
34
  couchbase::core::logger::set_log_levels(couchbase::core::logger::level_from_str(env_val));
34
35
  }
36
+ if (auto env_val = spdlog::details::os::getenv("TEST_LOG_PROTOCOL"); !env_val.empty()) {
37
+ couchbase::core::logger::configuration configuration{};
38
+ configuration.filename = env_val;
39
+ couchbase::core::logger::create_protocol_logger(configuration);
40
+ }
35
41
  if (auto env_val = spdlog::details::os::getenv("TEST_LOG_INCLUDE_LOCATION"); !env_val.empty()) {
36
42
  spdlog::set_pattern("[%Y-%m-%d %T.%e] [%P,%t] [%^%l%$] %oms, %v at %@ %!");
37
43
  }
44
+
38
45
  initialized = true;
39
46
  }
40
47
  }
@@ -367,8 +367,9 @@ usage_block_for_logger() -> std::string
367
367
 
368
368
  return fmt::format(R"(
369
369
  Logger options:
370
- --log-level=LEVEL Log level (allowed values are: {allowed_levels}). CBC_LOG_LEVEL. [default: {log_level}]
371
- --log-output=PATH File to send logs (when is not set, logs will be written to STDERR).
370
+ --log-level=LEVEL Log level (allowed values are: {allowed_levels}). CBC_LOG_LEVEL. [default: {log_level}]
371
+ --log-output=PATH File to send logs (when is not set, logs will be written to STDERR).
372
+ --log-protocol=PATH File to send protocol logs.
372
373
  )",
373
374
  fmt::arg("allowed_levels", fmt::join(allowed_log_levels, ", ")),
374
375
  fmt::arg("log_level", default_log_level()));
@@ -393,6 +394,12 @@ apply_logger_options(const docopt::Options& options)
393
394
  couchbase::core::logger::create_file_logger(configuration);
394
395
  }
395
396
 
397
+ if (options.find("--log-protocol") != options.end() && options.at("--log-protocol")) {
398
+ couchbase::core::logger::configuration configuration{};
399
+ configuration.filename = options.at("--log-protocol").asString();
400
+ couchbase::core::logger::create_protocol_logger(configuration);
401
+ }
402
+
396
403
  spdlog::set_level(spdlog::level::from_str(log_level));
397
404
  couchbase::core::logger::set_log_levels(level);
398
405
  }
package/dist/binding.d.ts CHANGED
@@ -2671,6 +2671,8 @@ export interface CppTransaction {
2671
2671
  export interface CppBinding extends CppBindingAutogen {
2672
2672
  cbppVersion: string;
2673
2673
  cbppMetadata: string;
2674
+ enableProtocolLogger: (filename: string) => void;
2675
+ shutdownLogger: () => void;
2674
2676
  Connection: {
2675
2677
  new (): CppConnection;
2676
2678
  };
@@ -23,6 +23,21 @@ export declare const lcbVersion: string;
23
23
  */
24
24
  export declare const cbppVersion: string;
25
25
  export declare const cbppMetadata: string;
26
+ /**
27
+ * Volatile: This API is subject to change at any time.
28
+ *
29
+ * Exposes the underlying couchbase++ library protocol logger. This method is for
30
+ * logging/debugging purposes and must be used with caution as network details will
31
+ * be logged to the provided file.
32
+ */
33
+ export declare function enableProtocolLoggerToSaveNetworkTrafficToFile(filename: string): void;
34
+ /**
35
+ * Volatile: This API is subject to change at any time.
36
+ *
37
+ * Shutdowns the underlying couchbase++ logger.
38
+ *
39
+ */
40
+ export declare function shutdownLogger(): void;
26
41
  export * from './analyticsindexmanager';
27
42
  export * from './analyticstypes';
28
43
  export * from './authenticators';
package/dist/couchbase.js CHANGED
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.cbppMetadata = exports.cbppVersion = exports.lcbVersion = exports.connect = void 0;
20
+ exports.shutdownLogger = exports.enableProtocolLoggerToSaveNetworkTrafficToFile = exports.cbppMetadata = exports.cbppVersion = exports.lcbVersion = exports.connect = void 0;
21
21
  const binding_1 = __importDefault(require("./binding"));
22
22
  const cluster_1 = require("./cluster");
23
23
  /**
@@ -46,6 +46,27 @@ exports.lcbVersion = binding_1.default.cbppVersion;
46
46
  */
47
47
  exports.cbppVersion = binding_1.default.cbppVersion;
48
48
  exports.cbppMetadata = binding_1.default.cbppMetadata;
49
+ /**
50
+ * Volatile: This API is subject to change at any time.
51
+ *
52
+ * Exposes the underlying couchbase++ library protocol logger. This method is for
53
+ * logging/debugging purposes and must be used with caution as network details will
54
+ * be logged to the provided file.
55
+ */
56
+ function enableProtocolLoggerToSaveNetworkTrafficToFile(filename) {
57
+ binding_1.default.enableProtocolLogger(filename);
58
+ }
59
+ exports.enableProtocolLoggerToSaveNetworkTrafficToFile = enableProtocolLoggerToSaveNetworkTrafficToFile;
60
+ /**
61
+ * Volatile: This API is subject to change at any time.
62
+ *
63
+ * Shutdowns the underlying couchbase++ logger.
64
+ *
65
+ */
66
+ function shutdownLogger() {
67
+ binding_1.default.shutdownLogger();
68
+ }
69
+ exports.shutdownLogger = shutdownLogger;
49
70
  __exportStar(require("./analyticsindexmanager"), exports);
50
71
  __exportStar(require("./analyticstypes"), exports);
51
72
  __exportStar(require("./authenticators"), exports);
package/package.json CHANGED
@@ -55,7 +55,7 @@
55
55
  "type": "git",
56
56
  "url": "http://github.com/couchbase/couchnode.git"
57
57
  },
58
- "version": "4.2.6-dev",
58
+ "version": "4.2.6",
59
59
  "config": {
60
60
  "native": false
61
61
  },
@@ -80,16 +80,17 @@
80
80
  ]
81
81
  },
82
82
  "optionalDependencies": {
83
- "@couchbase/couchbase-darwin-arm64-openssl1": "4.2.6-dev",
84
- "@couchbase/couchbase-darwin-arm64-openssl3": "4.2.6-dev",
85
- "@couchbase/couchbase-darwin-x64-openssl1": "4.2.6-dev",
86
- "@couchbase/couchbase-darwin-x64-openssl3": "4.2.6-dev",
87
- "@couchbase/couchbase-linux-arm64-openssl1": "4.2.6-dev",
88
- "@couchbase/couchbase-linux-x64-openssl1": "4.2.6-dev",
89
- "@couchbase/couchbase-linux-x64-openssl3": "4.2.6-dev",
90
- "@couchbase/couchbase-linuxmusl-x64-openssl1": "4.2.6-dev",
91
- "@couchbase/couchbase-linuxmusl-x64-openssl3": "4.2.6-dev",
92
- "@couchbase/couchbase-win32-x64-openssl1": "4.2.6-dev",
93
- "@couchbase/couchbase-win32-x64-openssl3": "4.2.6-dev"
83
+ "@couchbase/couchbase-darwin-arm64-openssl1": "4.2.6",
84
+ "@couchbase/couchbase-darwin-arm64-openssl3": "4.2.6",
85
+ "@couchbase/couchbase-darwin-x64-openssl1": "4.2.6",
86
+ "@couchbase/couchbase-darwin-x64-openssl3": "4.2.6",
87
+ "@couchbase/couchbase-linux-arm64-openssl1": "4.2.6",
88
+ "@couchbase/couchbase-linux-arm64-openssl3": "4.2.6",
89
+ "@couchbase/couchbase-linux-x64-openssl1": "4.2.6",
90
+ "@couchbase/couchbase-linux-x64-openssl3": "4.2.6",
91
+ "@couchbase/couchbase-linuxmusl-x64-openssl1": "4.2.6",
92
+ "@couchbase/couchbase-linuxmusl-x64-openssl3": "4.2.6",
93
+ "@couchbase/couchbase-win32-x64-openssl1": "4.2.6",
94
+ "@couchbase/couchbase-win32-x64-openssl3": "4.2.6"
94
95
  }
95
96
  }
package/src/binding.cpp CHANGED
@@ -6,12 +6,36 @@
6
6
  #include "scan_iterator.hpp"
7
7
  #include "transaction.hpp"
8
8
  #include "transactions.hpp"
9
+ #include <core/logger/configuration.hxx>
9
10
  #include <napi.h>
10
11
  #include <spdlog/spdlog.h>
11
12
 
12
13
  namespace couchnode
13
14
  {
14
15
 
16
+ Napi::Value enable_protocol_logger(const Napi::CallbackInfo &info)
17
+ {
18
+ try {
19
+ auto filename = info[0].ToString().Utf8Value();
20
+ couchbase::core::logger::configuration configuration{};
21
+ configuration.filename = filename;
22
+ couchbase::core::logger::create_protocol_logger(configuration);
23
+ } catch (...) {
24
+ return Napi::Error::New(info.Env(), "Unexpected C++ error").Value();
25
+ }
26
+ return info.Env().Null();
27
+ }
28
+
29
+ Napi::Value shutdown_logger(const Napi::CallbackInfo &info)
30
+ {
31
+ try {
32
+ couchbase::core::logger::shutdown();
33
+ } catch (...) {
34
+ return Napi::Error::New(info.Env(), "Unexpected C++ error").Value();
35
+ }
36
+ return info.Env().Null();
37
+ }
38
+
15
39
  Napi::Object Init(Napi::Env env, Napi::Object exports)
16
40
  {
17
41
  spdlog::set_pattern("[%Y-%m-%d %T.%e] [%P,%t] [%^%l%$] %oms, %v");
@@ -63,6 +87,10 @@ Napi::Object Init(Napi::Env env, Napi::Object exports)
63
87
  exports.Set(
64
88
  Napi::String::New(env, "cbppMetadata"),
65
89
  Napi::String::New(env, couchbase::core::meta::sdk_build_info_json()));
90
+ exports.Set(Napi::String::New(env, "enableProtocolLogger"),
91
+ Napi::Function::New<enable_protocol_logger>(env));
92
+ exports.Set(Napi::String::New(env, "shutdownLogger"),
93
+ Napi::Function::New<shutdown_logger>(env));
66
94
  return exports;
67
95
  }
68
96