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")