diff --git a/.github/workflows/ci-alpine.yml b/.github/workflows/ci-alpine.yml index efbfd8892..d25920a37 100644 --- a/.github/workflows/ci-alpine.yml +++ b/.github/workflows/ci-alpine.yml @@ -35,9 +35,9 @@ jobs: pcre2-dev \ perl \ pkgconf \ - rapidjson-dev \ re2-dev \ - sqlite-dev + sqlite-dev \ + yyjson-dev - name: Run configure run: | diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index b2fb906af..62bf7fb11 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -25,7 +25,7 @@ jobs: - name: Install dependencies run: | brew update || true - for PACKAGE in pkg-config argon2 gnutls libmaxminddb libpq libpsl mysql-client openssl openldap pcre2 re2 rapidjson sqlite + for PACKAGE in pkg-config argon2 gnutls libmaxminddb libpq libpsl mysql-client openssl openldap pcre2 re2 sqlite yyjson do brew install $PACKAGE || brew upgrade $PACKAGE diff --git a/configure b/configure index 2579d70c6..1036bfb8b 100755 --- a/configure +++ b/configure @@ -393,7 +393,7 @@ if (prompt_bool $interactive, $question, 0) { 'mysql' => 'mysql_config --version', 'pgsql' => 'pg_config --version', 'ldap' => "pkg-config --exists lber && pkg-config --exists ldap", - 'log_json' => 'pkg-config --exists RapidJSON', + 'log_json' => 'pkg-config --exists yyjson', 'log_syslog' => undef, 'regex_pcre2' => 'pkg-config --exists libpcre2-8', 'regex_posix' => undef, diff --git a/docs/conf/modules.example.conf b/docs/conf/modules.example.conf index 655735706..abc4ba7db 100644 --- a/docs/conf/modules.example.conf +++ b/docs/conf/modules.example.conf @@ -1536,9 +1536,10 @@ #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # JSON logging module: Allows writing messages to a JSON file. # # # -# This module depends on a third-party library (RapidJSON) and may # -# need to be manually enabled at build time. If you are building from # -# source you can do this by installing this dependency and running: # +# This module depends on a third-party library (yyjson or RapidJSON) # +# and may need to be manually enabled at build time. If you are # +# building from source you can do this by installing this dependency # +# and running: # # # # ./configure --enable-extras log_json # # make install # diff --git a/src/modules/extra/m_log_json.cpp b/src/modules/extra/m_log_json.cpp index 3e9f2030b..d8e2f1936 100644 --- a/src/modules/extra/m_log_json.cpp +++ b/src/modules/extra/m_log_json.cpp @@ -16,20 +16,32 @@ * along with this program. If not, see . */ -/// $CompilerFlags: find_compiler_flags("RapidJSON") +/// $CompilerFlags: require_library("yyjson") find_compiler_flags("yyjson") -DHAS_YYJSON +/// $CompilerFlags: require_library("!yyjson") find_compiler_flags("RapidJSON") warning("Building log_json against RapidJSON is deprecated. Please build against yyjson instead.") -/// $PackageInfo: require_system("alpine") rapidjson-dev pkgconf -/// $PackageInfo: require_system("arch") rapidjson pkgconf -/// $PackageInfo: require_system("darwin") rapidjson pkg-config +/// $LinkerFlags: require_library("yyjson") find_linker_flags("yyjson") + +/// $PackageInfo: require_system("alpine") pkgconf yyjson-dev +/// $PackageInfo: require_system("arch") pkgconf yyjson +/// $PackageInfo: require_system("darwin") pkg-config yyjson /// $PackageInfo: require_system("debian~") rapidjson-dev pkg-config +#ifdef _WIN32 +# define HAS_YYJSON +# pragma comment(lib, "yyjson.lib") +#endif -#include -#include +#ifdef HAS_YYJSON +# include +#else +# include +# include +#endif #include "inspircd.h" #include "timeutils.h" +#include class JSONMethod final : public Log::Method , public Timer @@ -71,8 +83,10 @@ public: fclose(file); } +#ifndef HAS_YYJSON // RapidJSON API: We implement our own flushing in OnLog. void Flush() { } +#endif void OnLog(time_t time, Log::Level level, const std::string& type, const std::string& message) override { @@ -84,6 +98,28 @@ public: timestr = Time::ToString(prevtime, "%Y-%m-%dT%H:%M:%S%z"); } + const auto* levelstr = Log::LevelToString(level); + +#ifdef HAS_YYJSON + auto* doc = yyjson_mut_doc_new(nullptr); + + auto* root = yyjson_mut_obj(doc); + yyjson_mut_doc_set_root(doc, root); + + auto error = false; + error |= yyjson_mut_obj_add_strn(doc, root, "time", timestr.c_str(), timestr.length()); + error |= yyjson_mut_obj_add_strn(doc, root, "type", type.c_str(), type.length()); + error |= yyjson_mut_obj_add_strn(doc, root, "level", levelstr, strlen(levelstr)); + error |= yyjson_mut_obj_add_strn(doc, root, "message", message.c_str(), message.length()); + + yyjson_write_err errmsg; + error |= yyjson_mut_write_fp(file, doc, YYJSON_WRITE_ALLOW_INVALID_UNICODE | YYJSON_WRITE_NEWLINE_AT_END, nullptr, &errmsg); + + yyjson_mut_doc_free(doc); + + if (error) + throw CoreException(INSP_FORMAT("Unable to generate JSON for {}: {}", name, errmsg.msg ? errmsg.msg : "unknown error")); +#else rapidjson::Writer writer(*this); writer.StartObject(); { @@ -94,18 +130,18 @@ public: writer.String(type.c_str(), static_cast(type.size())); writer.Key("level", 5); - const std::string levelstr = Log::LevelToString(level); - writer.String(levelstr.c_str(), static_cast(levelstr.size())); + writer.String(levelstr, static_cast(strlen(levelstr))); writer.Key("message", 7); writer.Key(message.c_str(), static_cast(message.size())); } writer.EndObject(); -#ifdef _WIN32 - fputs("\r\n", file); -#else - fputs("\n", file); +# ifdef _WIN32 + fputs("\r\n", file); +# else + fputs("\n", file); +# endif #endif if (!(++lines % flush)) @@ -115,11 +151,13 @@ public: throw CoreException(INSP_FORMAT("Unable to write to {}: {}", name, strerror(errno))); } +#ifndef HAS_YYJSON // RapidJSON API: Write a character to the file. void Put(Ch c) { fputc(c, file); } +#endif bool Tick() override { @@ -194,8 +232,14 @@ public: void init() override { +#ifdef HAS_YYJSON + const auto yyversion = yyjson_version(); + ServerInstance->Logs.Normal(MODNAME, "Module was compiled against yyjson version {} and is running against version {}.{}.{}", + YYJSON_VERSION_STRING, (yyversion >> 16) & 0xFF, (yyversion >> 8) & 0xFF, yyversion & 0xFF); +#else ServerInstance->Logs.Normal(MODNAME, "Module was compiled against RapidJSON version {}", RAPIDJSON_VERSION_STRING); +#endif } }; diff --git a/win/conanfile.txt b/win/conanfile.txt index a16194902..1bae45eb1 100644 --- a/win/conanfile.txt +++ b/win/conanfile.txt @@ -12,9 +12,9 @@ libpq/15.5 libpsl/0.21.1 ## openssl/3.2.2 pcre2/10.44 -rapidjson/cci.20230929 re2/20240702 sqlite3/3.46.0 +yyjson/0.10.0 [options] argon2:shared=True @@ -26,6 +26,7 @@ openssl:shared=True pcre2:shared=True re2:shared=True sqlite3:shared=True +yyjson:shared=True [imports] ., *.dll -> extradll @ keep_path=False diff --git a/win/modules/CMakeLists.txt b/win/modules/CMakeLists.txt index e8bf3423f..8fc4c3a81 100644 --- a/win/modules/CMakeLists.txt +++ b/win/modules/CMakeLists.txt @@ -43,7 +43,7 @@ if(EXISTS "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake") enable_extra("argon2" "ARGON2") enable_extra("geo_maxmind" "LIBMAXMINDDB") - enable_extra("log_json" "RAPIDJSON") + enable_extra("log_json" "YYJSON") enable_extra("mysql" "LIBMYSQLCLIENT") enable_extra("pgsql" "LIBPQ") enable_extra("regex_pcre2" "PCRE2")