From dccb20ff777b20f7ec63c9d2aa7a02de2949a76a Mon Sep 17 00:00:00 2001 From: Qijun Niu Date: Mon, 9 Dec 2024 20:12:05 +0800 Subject: [PATCH] Feature/issue 869 backport console log (#870) * add 'log' function to JavaScript UDA/UDF for debugging. close #4912 (#4997) * minor refinement for javascript log console (#5018) * change LOG_DEBUG to LOG INFO and add try-catch (#5631) * change LOG_DEBUG to LOG INFO and add try-catch * clang-format * add map,set transform * remove useless include * fix some comments * move comments * format code * fix last comments * fix catch * fix compile error --------- Co-authored-by: sunset3000 Co-authored-by: Ken Chen Co-authored-by: chhtimeplus <162944718+chhtimeplus@users.noreply.github.com> --- src/AggregateFunctions/tests/gtest_uda.cpp | 2 +- src/Common/ProtonCommon.h | 3 + src/V8/CMakeLists.txt | 1 + src/V8/Modules/Console.cpp | 84 ++++++++++++++++++++++ src/V8/Modules/Console.h | 17 +++++ src/V8/Utils.cpp | 10 ++- 6 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 src/V8/Modules/Console.cpp create mode 100644 src/V8/Modules/Console.h diff --git a/src/AggregateFunctions/tests/gtest_uda.cpp b/src/AggregateFunctions/tests/gtest_uda.cpp index 686ec658a52..557f41fa097 100644 --- a/src/AggregateFunctions/tests/gtest_uda.cpp +++ b/src/AggregateFunctions/tests/gtest_uda.cpp @@ -712,4 +712,4 @@ TEST_F(UDATestCase, prepareArgumentsUInt8) }; checkPrepareArguments("uint8", create_uint8_arr, check_uint8); -} \ No newline at end of file +} diff --git a/src/Common/ProtonCommon.h b/src/Common/ProtonCommon.h index b99fc95e7e0..a4683631b80 100644 --- a/src/Common/ProtonCommon.h +++ b/src/Common/ProtonCommon.h @@ -4,6 +4,7 @@ #include #include +#include namespace DB { @@ -66,6 +67,8 @@ const String JSON_VALUES_PREFIX = "__json_values_"; const String UDF_XML_PATTERN = "*_function.xml"; /// UDF VERSION used by this version of proton const uint32_t UDF_VERSION = 1; +/// Prefix for all Javascript UDF or UDA loggers +const String PROTON_JAVASCRIPT_UDF_LOGGER_PREFIX = "JavaScriptUDF"; /// Storage modes const String APPEND_MODE = "append"; diff --git a/src/V8/CMakeLists.txt b/src/V8/CMakeLists.txt index 9a133daf7c1..cbc34f867bf 100644 --- a/src/V8/CMakeLists.txt +++ b/src/V8/CMakeLists.txt @@ -1,6 +1,7 @@ include("${proton_SOURCE_DIR}/cmake/dbms_glob_sources.cmake") add_headers_and_sources(V8 .) +add_headers_and_sources(V8 Modules) add_library(V8 ${V8_headers} ${V8_sources}) diff --git a/src/V8/Modules/Console.cpp b/src/V8/Modules/Console.cpp new file mode 100644 index 00000000000..bbfb58147ef --- /dev/null +++ b/src/V8/Modules/Console.cpp @@ -0,0 +1,84 @@ +#include +#include + +#include +#include + +namespace DB +{ + +namespace V8 +{ + +void log(const v8::FunctionCallbackInfo & args) +{ + v8::Isolate * isolate = args.GetIsolate(); + assert(args.Length() >= 0); + std::vector result(args.Length()); + try + { + auto context = isolate->GetCurrentContext(); + for (int i = 0; i < args.Length(); i++) + { + if (args[i]->IsString() || args[i]->IsNumber()) + { + result[i] = from_v8(isolate, args[i]); + } + else if (args[i]->IsObject()) + { + /// trans Object to String + bool is_key_value = false; + auto obj = args[i].As(); + /// trans Map,Set to String + auto array = obj->PreviewEntries(&is_key_value); + if (!array.IsEmpty()) + result[i] = from_v8(isolate, array.ToLocalChecked()->ToString(context).ToLocalChecked()); + else if (obj->IsRegExp()) + result[i] = from_v8(isolate, obj->ToString(context).ToLocalChecked()); + else + result[i] = from_v8(isolate, v8::JSON::Stringify(context, obj).ToLocalChecked()); + } + else + { + /// default trans to String + result[i] = from_v8(isolate, args[i]->ToString(context).ToLocalChecked()); + } + } + LOG_INFO(&Poco::Logger::get(from_v8(isolate, args.Data())), "{}", fmt::join(result, " ")); + } + catch (DB::Exception & e) + { + LOG_ERROR(&Poco::Logger::get(from_v8(isolate, args.Data())), "Hit an udf/uda error : {}", e.what()); + } + catch(...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } +} + +v8::Local WrapObject(v8::Isolate * isolate, const std::string & func_name) +{ + v8::EscapableHandleScope handle_scope(isolate); + + v8::Local module = v8::ObjectTemplate::New(isolate); + + module->SetInternalFieldCount(1); + + /// add 'log' function + v8::Local logger_name = to_v8(isolate, fmt::format("{}({})", DB::ProtonConsts::PROTON_JAVASCRIPT_UDF_LOGGER_PREFIX, func_name)); + module->Set(isolate, "log", v8::FunctionTemplate::New(isolate, log, logger_name)); + + /// create instance + v8::Local result = module->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); + + return handle_scope.Escape(result); +} + +void installConsole(v8::Isolate * isolate, v8::Local & ctx, const std::string & func_name) +{ + v8::Local console = WrapObject(isolate, func_name); + ctx->Global()->Set(ctx, to_v8(isolate, "console"), console).Check(); +} + +} +} diff --git a/src/V8/Modules/Console.h b/src/V8/Modules/Console.h new file mode 100644 index 00000000000..687295c926f --- /dev/null +++ b/src/V8/Modules/Console.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +namespace DB +{ + +namespace V8 +{ +/// Install the module console to the specified v8 context, +/// \param func_name the function name of Javascript UDF or UDA, which is used to get the corresponding function logger +void installConsole(v8::Isolate * isolate, v8::Local & ctx, const std::string & func_name); + +} +} diff --git a/src/V8/Utils.cpp b/src/V8/Utils.cpp index 26413003927..100eef47e5a 100644 --- a/src/V8/Utils.cpp +++ b/src/V8/Utils.cpp @@ -1,8 +1,11 @@ #include +#include #include #include #include +#include + #define FOR_V8_BASIC_NUMERIC_TYPES(M) \ M(UInt16) \ @@ -289,9 +292,14 @@ void compileSource( /// try_catch.SetVerbose(true); try_catch.SetCaptureMessage(true); - v8::Local local_ctx = v8::Context::New(isolate); + /// Setup global object + v8::Local global = v8::ObjectTemplate::New(isolate); + v8::Local local_ctx = v8::Context::New(isolate, nullptr, global); v8::Context::Scope context_scope(local_ctx); + /// Setup 'console' object + installConsole(isolate, local_ctx, func_name); + v8::Local script_code = v8::String::NewFromUtf8(isolate, source.data(), v8::NewStringType::kNormal, static_cast(source.size())).ToLocalChecked();