2121
2222#include < asio.hpp>
2323#include < spdlog/cfg/env.h>
24+ #include < spdlog/sinks/base_sink.h>
2425#include < spdlog/spdlog.h>
2526
2627#include < snappy.h>
3233
3334#include < core/cluster.hxx>
3435#include < core/design_document_namespace_fmt.hxx>
36+ #include < core/logger/configuration.hxx>
3537#include < core/operations.hxx>
3638#include < core/operations/management/analytics.hxx>
3739#include < core/operations/management/bucket.hxx>
@@ -182,10 +184,11 @@ cb_binary_new(VALUE str)
182184 return couchbase::core::utils::to_binary (static_cast <const char *>(RSTRING_PTR (str)), static_cast <std::size_t >(RSTRING_LEN (str)));
183185}
184186
187+ template <typename StringLike>
185188static inline VALUE
186- cb_str_new (const std::string& str)
189+ cb_str_new (const StringLike str)
187190{
188- return rb_external_str_new (str. data (), static_cast <long >(str. size ()));
191+ return rb_external_str_new (std:: data (str ), static_cast <long >(std:: size (str )));
189192}
190193
191194static inline VALUE
@@ -200,6 +203,12 @@ cb_str_new(const std::byte* data, std::size_t size)
200203 return rb_external_str_new (reinterpret_cast <const char *>(data), static_cast <long >(size));
201204}
202205
206+ static inline VALUE
207+ cb_str_new (const char * data)
208+ {
209+ return rb_external_str_new_cstr (data);
210+ }
211+
203212static inline VALUE
204213cb_str_new (const std::optional<std::string>& str)
205214{
@@ -266,6 +275,132 @@ init_versions(VALUE mCouchbase)
266275 std::string_view (RSTRING_PTR (build_info), static_cast <std::size_t >(RSTRING_LEN (build_info))));
267276}
268277
278+ template <typename Mutex>
279+ class ruby_logger_sink : public spdlog ::sinks::base_sink<Mutex>
280+ {
281+ public:
282+ explicit ruby_logger_sink (VALUE ruby_logger)
283+ : ruby_logger_{ ruby_logger }
284+ {
285+ }
286+
287+ void flush_deferred_messages ()
288+ {
289+ std::lock_guard<Mutex> lock (spdlog::sinks::base_sink<Mutex>::mutex_);
290+ auto messages_ = std::move (deferred_messages_);
291+ while (!messages_.empty ()) {
292+ write_message (messages_.front ());
293+ messages_.pop ();
294+ }
295+ }
296+
297+ static VALUE map_log_level (spdlog::level::level_enum level)
298+ {
299+ switch (level) {
300+ case spdlog::level::trace:
301+ return rb_id2sym (rb_intern (" trace" ));
302+ case spdlog::level::debug:
303+ return rb_id2sym (rb_intern (" debug" ));
304+ case spdlog::level::info:
305+ return rb_id2sym (rb_intern (" info" ));
306+ case spdlog::level::warn:
307+ return rb_id2sym (rb_intern (" warn" ));
308+ case spdlog::level::err:
309+ return rb_id2sym (rb_intern (" error" ));
310+ case spdlog::level::critical:
311+ return rb_id2sym (rb_intern (" critical" ));
312+ case spdlog::level::off:
313+ return rb_id2sym (rb_intern (" off" ));
314+ default :
315+ break ;
316+ }
317+ return Qnil;
318+ }
319+
320+ protected:
321+ struct log_message_for_ruby {
322+ spdlog::level::level_enum level;
323+ spdlog::log_clock::time_point time;
324+ size_t thread_id;
325+ std::string payload;
326+ const char * filename;
327+ int line;
328+ const char * funcname;
329+ };
330+
331+ void sink_it_ (const spdlog::details::log_msg& msg) override
332+ {
333+ deferred_messages_.emplace (log_message_for_ruby{
334+ msg.level ,
335+ msg.time ,
336+ msg.thread_id ,
337+ { msg.payload .begin (), msg.payload .end () },
338+ msg.source .filename ,
339+ msg.source .line ,
340+ msg.source .funcname ,
341+ });
342+ }
343+
344+ void flush_ () override
345+ {
346+ /* do nothing here, the flush will be initiated by the SDK */
347+ }
348+
349+ private:
350+ struct argument_pack {
351+ VALUE logger;
352+ const log_message_for_ruby& msg;
353+ };
354+
355+ static VALUE invoke_log (VALUE arg)
356+ {
357+ auto * args = reinterpret_cast <argument_pack*>(arg);
358+ const auto & msg = args->msg ;
359+
360+ VALUE filename = Qnil;
361+ if (msg.filename != nullptr ) {
362+ filename = cb_str_new (msg.filename );
363+ }
364+ VALUE line = Qnil;
365+ if (msg.line > 0 ) {
366+ line = ULL2NUM (msg.line );
367+ }
368+ VALUE function_name = Qnil;
369+ if (msg.funcname != nullptr ) {
370+ function_name = cb_str_new (msg.funcname );
371+ }
372+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>(msg.time .time_since_epoch ());
373+ auto nanoseconds = msg.time .time_since_epoch () - seconds;
374+ return rb_funcall (args->logger ,
375+ rb_intern (" log" ),
376+ 8 ,
377+ map_log_level (msg.level ),
378+ ULL2NUM (msg.thread_id ),
379+ ULL2NUM (seconds.count ()),
380+ ULL2NUM (nanoseconds.count ()),
381+ cb_str_new (msg.payload ),
382+ filename,
383+ line,
384+ function_name);
385+ }
386+
387+ void write_message (const log_message_for_ruby& msg)
388+ {
389+ if (NIL_P (ruby_logger_)) {
390+ return ;
391+ }
392+ argument_pack args{ ruby_logger_, msg };
393+ rb_rescue (invoke_log, reinterpret_cast <VALUE>(&args), nullptr , Qnil);
394+ }
395+
396+ VALUE ruby_logger_{ Qnil };
397+ std::queue<log_message_for_ruby> deferred_messages_{};
398+ };
399+
400+ using ruby_logger_sink_ptr = std::shared_ptr<ruby_logger_sink<std::mutex>>;
401+
402+ static ruby_logger_sink_ptr cb_global_sink{ nullptr };
403+
269404struct cb_backend_data {
270405 std::unique_ptr<asio::io_context> ctx;
271406 std::shared_ptr<couchbase::core::cluster> cluster;
@@ -1064,6 +1199,9 @@ cb_wait_for_future(Future&& f) -> decltype(f.get())
10641199 &arg,
10651200 nullptr ,
10661201 nullptr );
1202+ if (cb_global_sink) {
1203+ cb_global_sink->flush_deferred_messages ();
1204+ }
10671205 return std::move (arg.res );
10681206}
10691207
@@ -1373,6 +1511,9 @@ cb_Backend_close(VALUE self)
13731511 cb_backend_data* backend = nullptr ;
13741512 TypedData_Get_Struct (self, cb_backend_data, &cb_backend_type, backend);
13751513 cb_backend_close (backend);
1514+ if (cb_global_sink) {
1515+ cb_global_sink->flush_deferred_messages ();
1516+ }
13761517 return Qnil;
13771518}
13781519
@@ -7265,9 +7406,8 @@ cb_Backend_document_view(VALUE self, VALUE bucket_name, VALUE design_document_na
72657406}
72667407
72677408static VALUE
7268- cb_Backend_set_log_level (VALUE self, VALUE log_level)
7409+ cb_Backend_set_log_level (VALUE /* self */ , VALUE log_level)
72697410{
7270- (void )self;
72717411 Check_Type (log_level, T_SYMBOL);
72727412 if (ID type = rb_sym2id (log_level); type == rb_intern (" trace" )) {
72737413 spdlog::set_level (spdlog::level::trace);
@@ -7291,9 +7431,8 @@ cb_Backend_set_log_level(VALUE self, VALUE log_level)
72917431}
72927432
72937433static VALUE
7294- cb_Backend_get_log_level (VALUE self)
7434+ cb_Backend_get_log_level (VALUE /* self */ )
72957435{
7296- (void )self;
72977436 switch (spdlog::get_level ()) {
72987437 case spdlog::level::trace:
72997438 return rb_id2sym (rb_intern (" trace" ));
@@ -7315,6 +7454,43 @@ cb_Backend_get_log_level(VALUE self)
73157454 return Qnil;
73167455}
73177456
7457+ static VALUE
7458+ cb_Backend_install_logger_shim (VALUE self, VALUE logger, VALUE log_level)
7459+ {
7460+ couchbase::core::logger::reset ();
7461+ rb_iv_set (self, " @__logger_shim" , logger);
7462+ if (NIL_P (logger)) {
7463+ return Qnil;
7464+ }
7465+ Check_Type (log_level, T_SYMBOL);
7466+ couchbase::core::logger::level level{ couchbase::core::logger::level::off };
7467+ if (ID type = rb_sym2id (log_level); type == rb_intern (" trace" )) {
7468+ level = couchbase::core::logger::level::trace;
7469+ } else if (type == rb_intern (" debug" )) {
7470+ level = couchbase::core::logger::level::debug;
7471+ } else if (type == rb_intern (" info" )) {
7472+ level = couchbase::core::logger::level::info;
7473+ } else if (type == rb_intern (" warn" )) {
7474+ level = couchbase::core::logger::level::warn;
7475+ } else if (type == rb_intern (" error" )) {
7476+ level = couchbase::core::logger::level::err;
7477+ } else if (type == rb_intern (" critical" )) {
7478+ level = couchbase::core::logger::level::critical;
7479+ } else {
7480+ rb_iv_set (self, " __logger_shim" , Qnil);
7481+ return Qnil;
7482+ }
7483+
7484+ auto sink = std::make_shared<ruby_logger_sink<std::mutex>>(logger);
7485+ couchbase::core::logger::configuration configuration;
7486+ configuration.console = false ;
7487+ configuration.log_level = level;
7488+ configuration.sink = sink;
7489+ couchbase::core::logger::create_file_logger (configuration);
7490+ cb_global_sink = sink;
7491+ return Qnil;
7492+ }
7493+
73187494static VALUE
73197495cb_Backend_snappy_compress (VALUE self, VALUE data)
73207496{
@@ -7524,6 +7700,7 @@ init_backend(VALUE mCouchbase)
75247700 rb_define_singleton_method (cBackend, " parse_connection_string" , VALUE_FUNC (cb_Backend_parse_connection_string), 1 );
75257701 rb_define_singleton_method (cBackend, " set_log_level" , VALUE_FUNC (cb_Backend_set_log_level), 1 );
75267702 rb_define_singleton_method (cBackend, " get_log_level" , VALUE_FUNC (cb_Backend_get_log_level), 0 );
7703+ rb_define_singleton_method (cBackend, " install_logger_shim" , VALUE_FUNC (cb_Backend_install_logger_shim), 2 );
75277704 rb_define_singleton_method (cBackend, " snappy_compress" , VALUE_FUNC (cb_Backend_snappy_compress), 1 );
75287705 rb_define_singleton_method (cBackend, " snappy_uncompress" , VALUE_FUNC (cb_Backend_snappy_uncompress), 1 );
75297706 rb_define_singleton_method (cBackend, " leb128_encode" , VALUE_FUNC (cb_Backend_leb128_encode), 1 );
@@ -7536,14 +7713,15 @@ init_backend(VALUE mCouchbase)
75367713void
75377714init_logger ()
75387715{
7539- couchbase::core::logger::create_console_logger ();
7540- if (auto env_val = spdlog::details::os::getenv (" COUCHBASE_BACKEND_LOG_LEVEL" ); !env_val.empty ()) {
7541- couchbase::core::logger::set_log_levels (couchbase::core::logger::level_from_str (env_val));
7542- }
7543-
75447716 if (auto env_val = spdlog::details::os::getenv (" COUCHBASE_BACKEND_DONT_INSTALL_TERMINATE_HANDLER" ); env_val.empty ()) {
75457717 couchbase::core::platform::install_backtrace_terminate_handler ();
75467718 }
7719+ if (auto env_val = spdlog::details::os::getenv (" COUCHBASE_BACKEND_DONT_USE_BUILTIN_LOGGER" ); env_val.empty ()) {
7720+ couchbase::core::logger::create_console_logger ();
7721+ if (env_val = spdlog::details::os::getenv (" COUCHBASE_BACKEND_LOG_LEVEL" ); !env_val.empty ()) {
7722+ couchbase::core::logger::set_log_levels (couchbase::core::logger::level_from_str (env_val));
7723+ }
7724+ }
75477725}
75487726
75497727extern " C" {
0 commit comments