The branch, master has been updated
       via  788af2540eb WHATSNEW document JSON audit logging changes
       via  35cb872ab44 lib:audit_logging JSON remove leading spaces
       via  b86bcc58b0e lib:util:debug tests for JSON logging macros
       via  3c30c4740f4 lib:util:debug Logging macros for JSON output
      from  24dc455362f s3:rpc_client: Fix memory leak opening local named pipe

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 788af2540eb289fae6780ce7684172c5a3487166
Author: Gary Lockyer <[email protected]>
Date:   Wed Jan 28 10:39:19 2026 +1300

    WHATSNEW document JSON audit logging changes
    
    Document the JSON audit logging changes.
      - leading spaces before opening '{' removed
      - any embedded '\n' chars replaced with spaces
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15898
    
    Signed-off-by: Gary Lockyer <[email protected]>
    Reviewed-by: Volker Lendecke <[email protected]>
    
    Autobuild-User(master): Douglas Bagnall <[email protected]>
    Autobuild-Date(master): Thu Jan 29 00:33:43 UTC 2026 on atb-devel-224

commit 35cb872ab44ba9fe0782164a979c112705374cee
Author: Gary Lockyer <[email protected]>
Date:   Fri Jan 23 09:33:10 2026 +1300

    lib:audit_logging JSON remove leading spaces
    
    Remove the leading spaces in JSON audit lines.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15898
    
    Signed-off-by: Gary Lockyer <[email protected]>
    Reviewed-by: Volker Lendecke <[email protected]>

commit b86bcc58b0ea43c4ab85e0030a9522c686877043
Author: Gary Lockyer <[email protected]>
Date:   Fri Jan 23 09:27:54 2026 +1300

    lib:util:debug tests for JSON logging macros
    
    Add unit tests for the new JSON debugging macros DBGJSON and DBGJSONC
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15898
    
    Signed-off-by: Gary Lockyer <[email protected]>
    Reviewed-by: Volker Lendecke <[email protected]>

commit 3c30c4740f40f633b5a71c772374e6c1283ec13c
Author: Gary Lockyer <[email protected]>
Date:   Fri Jan 23 09:25:13 2026 +1300

    lib:util:debug Logging macros for JSON output
    
    Add new debug macros for outputting JSON log lines
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15898
    
    Signed-off-by: Gary Lockyer <[email protected]>
    Reviewed-by: Volker Lendecke <[email protected]>

-----------------------------------------------------------------------

Summary of changes:
 WHATSNEW.txt                       |   6 +
 lib/audit_logging/audit_logging.c  |  10 +-
 lib/util/debug.c                   |  46 ++++++
 lib/util/debug.h                   |  13 ++
 lib/util/tests/test_json_logging.c | 285 +++++++++++++++++++++++++++++++++++++
 lib/util/wscript_build             |   6 +
 selftest/tests.py                  |   2 +
 7 files changed, 361 insertions(+), 7 deletions(-)
 create mode 100644 lib/util/tests/test_json_logging.c


Changeset truncated at 500 lines:

diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index addd3a5932a..d845d16cbe7 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -16,6 +16,12 @@ UPGRADING
 NEW FEATURES/CHANGES
 ====================
 
+JSON Audit logging
+------------------
+
+The two leading spaces before the opening '{' on JSON audit log lines have been
+removed. And any embedded new line characters '\n' are converted to spaces.
+
 
 REMOVED FEATURES
 ================
diff --git a/lib/audit_logging/audit_logging.c 
b/lib/audit_logging/audit_logging.c
index e2fe4b0edb7..58f32df1a25 100644
--- a/lib/audit_logging/audit_logging.c
+++ b/lib/audit_logging/audit_logging.c
@@ -132,14 +132,10 @@ void audit_log_json(struct json_object* message,
                return;
        }
        /*
-        * This is very strange, but we call this routine to get a log
-        * output without the header.  JSON logs all have timestamps
-        * so this only makes parsing harder.
-        *
-        * We push out the raw JSON blob without a prefix, consumers
-        * can find such lines by the leading {
+        * Output a single line to the logs, with no prefix
+        * or time stamps, any embedded '\n' chars will be removed
         */
-       DEBUGADDC(debug_class, debug_level, ("%s\n", s));
+       DEBUGJSONC(debug_class, debug_level, (s));
        TALLOC_FREE(frame);
 }
 
diff --git a/lib/util/debug.c b/lib/util/debug.c
index f79b8811a4b..2a1620898fb 100644
--- a/lib/util/debug.c
+++ b/lib/util/debug.c
@@ -46,6 +46,10 @@
 /*
  * format_bufr[FORMAT_BUFR_SIZE - 1] should always be reserved
  * for a terminating null byte.
+ *
+ * Note: The json logging unit tests lib/util/tests/test_json_logging.c
+ *       assume this value is 4096, they'll need to be updated if
+ *       this is changed
  */
 #define FORMAT_BUFR_SIZE 4096
 
@@ -1687,6 +1691,48 @@ static void format_debug_text( const char *msg )
        format_bufr[format_pos] = '\0';
 }
 
+/***************************************************************************
+  Output a single line of JSON to the logs
+
+ Input:  msg - text to be output
+
+ Output: none.
+
+ Notes:  - msg is output without any added leading white space
+        - Any embedded "\n" characters are replaced with spaces
+        - A terminating "\n" is output.
+**************************************************************************/
+
+bool dbgjson( const char *msg )
+{
+       size_t i;
+       const char eol[] = "\n";
+
+       debug_init();
+
+       for( i = 0; msg[i]; i++ ) {
+               /* If the buffer is full output it */
+               if (format_pos >= FORMAT_BUFR_SIZE - 1) {
+                       bufr_print();
+               }
+               /* replace any new lines with spaces*/
+               if( '\n' == msg[i] ) {
+                       format_bufr[format_pos++] = ' ';
+               } else {
+                       format_bufr[format_pos++] = msg[i];
+               }
+
+       }
+       if (format_pos > 0) {
+               bufr_print();
+       }
+       (void)Debug1(eol , sizeof(eol) - 1);
+
+       /* Just to be safe... */
+       format_bufr[format_pos] = '\0';
+       return true;
+}
+
 /***************************************************************************
  Flush debug output, including the format buffer content.
 
diff --git a/lib/util/debug.h b/lib/util/debug.h
index 2a56c7d48fe..6f59c7f194e 100644
--- a/lib/util/debug.h
+++ b/lib/util/debug.h
@@ -237,6 +237,19 @@ void debuglevel_set_class(size_t idx, int level);
        && (dbgsetclass(level, dbgc_class))                     \
        && (dbgtext body) )
 
+bool dbgjson( const char *msg );
+#define DEBUGJSON( level, line) \
+  (void) ( ((level) <= MAX_DEBUG_LEVEL) && \
+       unlikely(debuglevel_get_class(DBGC_CLASS) >= (level)) \
+       && (dbgsetclass(level, DBGC_CLASS))                   \
+       && (dbgjson line) )
+
+#define DEBUGJSONC( dbgc_class, level, line) \
+  (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+       unlikely((debuglevel_get_class(dbgc_class) >= (level))) \
+       && (dbgsetclass(level, dbgc_class))                     \
+       && (dbgjson line) )
+
 /* Print a separator to the debug log. */
 #define DEBUGSEP(level)\
        
DEBUG((level),("===============================================================\n"))
diff --git a/lib/util/tests/test_json_logging.c 
b/lib/util/tests/test_json_logging.c
new file mode 100644
index 00000000000..42e6206a041
--- /dev/null
+++ b/lib/util/tests/test_json_logging.c
@@ -0,0 +1,285 @@
+/*
+ * cmocka unit tests for the DEBUGJSON and DEBUGJSONC debug macros
+ *
+ *  Copyright (C) Gary Lockyer 2026 <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * from cmocka.c:
+ * These headers or their equivalents should be included prior to
+ * including
+ * this header file.
+ *
+ * #include <stdarg.h>
+ * #include <stddef.h>
+ * #include <setjmp.h>
+ *
+ * This allows test applications to use custom definitions of C standard
+ * library functions and types.
+ *
+ */
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/util/debug.h"
+#include <talloc.h>
+#include <string.h>
+
+#define MAX_CALLS 4
+
+/*
+ * This needs to be the same as the value set in lib/util/debug.c
+ */
+#define FORMAT_BUFR_SIZE 4096
+
+#define FORMAT_STRING_SIZE (FORMAT_BUFR_SIZE - 1)
+
+/*
+ * Test context
+ */
+struct test_ctx {
+       size_t calls;  /* number of times callback fn called */
+       char **data;   /* data passed in each call           */
+};
+
+/*
+* debug logging call back function.
+*
+* NOTE: The debug code calling this function debug.c:debug_callback_log
+*       will replace a trailing '\n' with a '\0'
+*/
+static void debug_callback(void *state, int level, const char *msg) {
+       struct test_ctx *test_ctx = talloc_get_type_abort(state,
+                                                         struct test_ctx);
+       assert_non_null(test_ctx);
+
+       assert_int_not_equal(MAX_CALLS, test_ctx->calls);
+       test_ctx->data[test_ctx->calls] = talloc_strdup(test_ctx, msg);
+       assert_non_null(test_ctx->data[test_ctx->calls]);
+
+       test_ctx->calls++;
+}
+
+/*
+* Test set-up.
+*
+* creates and initializes the test context
+*/
+static int setup(void **state)
+{
+       struct test_ctx *test_ctx;
+
+       test_ctx = talloc_zero(NULL, struct test_ctx);
+       assert_non_null(test_ctx);
+
+       test_ctx->data = talloc_array(test_ctx, char *, MAX_CALLS);
+
+       debug_set_callback(test_ctx, debug_callback);
+       debuglevel_set_class(DBGC_ALL, DBGLVL_NOTICE);
+       debuglevel_set_class(DBGC_DSDB_AUDIT_JSON, DBGLVL_INFO);
+
+       *state = test_ctx;
+       return 0;
+}
+
+/*
+* Test clean up
+*
+* deallocate any memory used by the test context
+*
+*/
+static int teardown(void **state)
+{
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+
+       talloc_free(test_ctx);
+       return 0;
+}
+/*
+* Test DEBUGJSONC with an empty message
+*/
+static void test_empty_message(void **state)
+{
+       const char *message = "";
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       assert_non_null(test_ctx);
+
+       DEBUGJSONC(DBGC_DSDB_AUDIT_JSON, DBGLVL_INFO, (message));
+       assert_int_equal(1, test_ctx->calls);
+       assert_string_equal("", test_ctx->data[0]);
+}
+
+/*
+* Test DEBUGJSONC with a short message
+*/
+static void test_short_message(void **state)
+{
+       const char *message = "A message";
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       assert_non_null(test_ctx);
+
+       DEBUGJSONC(DBGC_DSDB_AUDIT_JSON, DBGLVL_INFO, (message));
+       assert_int_equal(2, test_ctx->calls);
+       assert_string_equal(message, test_ctx->data[0]);
+       assert_string_equal("", test_ctx->data[1]);
+}
+
+/*
+* Test DEBUGJSONC honours the debug class levels
+*/
+static void test_log_class(void **state)
+{
+       const char *message = "A message";
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       assert_non_null(test_ctx);
+
+       DEBUGJSONC(DBGC_DSDB_AUDIT, DBGLVL_INFO, (message));
+       assert_int_equal(0, test_ctx->calls);
+}
+
+/*
+* Test DEBUGJSONC honours its debug level
+*/
+static void test_log_level(void **state)
+{
+       const char *message = "A message";
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       assert_non_null(test_ctx);
+
+       DEBUGJSONC(DBGC_DSDB_AUDIT_JSON, DBGLVL_DEBUG, (message));
+       assert_int_equal(0, test_ctx->calls);
+}
+/*
+* Test DEBUGJSONC with a message larger than the buffer.
+*/
+static void test_large_message(void **state)
+{
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       char *message = talloc_zero_size(test_ctx, 5112);
+       memset(message, 'x', 5111);
+
+       assert_non_null(test_ctx);
+
+       DEBUGJSONC(DBGC_DSDB_AUDIT_JSON, DBGLVL_INFO, (message));
+       assert_int_equal(3, test_ctx->calls);
+       assert_int_equal(4095, strlen(test_ctx->data[0]));
+       assert_int_equal(1016, strlen(test_ctx->data[1]));
+       assert_int_equal(5111, (4095+1016));
+       assert_string_equal("", test_ctx->data[2]);
+}
+
+/*
+* Test DEBUGJSONC with a message equal to the buffer size.
+*/
+static void test_buffer_size_message(void **state)
+{
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       char *message = talloc_zero_size(test_ctx, FORMAT_BUFR_SIZE);
+       memset(message, 'x', FORMAT_STRING_SIZE);
+
+       assert_non_null(test_ctx);
+
+       DEBUGJSONC(DBGC_DSDB_AUDIT_JSON, DBGLVL_INFO, (message));
+       assert_int_equal(2, test_ctx->calls);
+       assert_int_equal(FORMAT_STRING_SIZE, strlen(test_ctx->data[0]));
+       assert_string_equal("", test_ctx->data[1]);
+}
+
+/*
+* Test DEBUGJSONC replaces '\n' with spaces
+*/
+static void test_embedded_new_lines(void **state)
+{
+       const char *message = "A \nmessage\n with new lines\n";
+       const char *expected_message = "A  message  with new lines ";
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       assert_non_null(test_ctx);
+
+       DEBUGJSONC(DBGC_DSDB_AUDIT_JSON, DBGLVL_INFO, (message));
+       assert_int_equal(2, test_ctx->calls);
+       assert_string_equal(expected_message, test_ctx->data[0]);
+       assert_string_equal("", test_ctx->data[1]);
+}
+
+/*
+* Test DEBUGJSON with a short message
+*/
+static void test_debugjson(void **state)
+{
+       const char *message = "A message";
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       assert_non_null(test_ctx);
+
+       DEBUGJSON(DBGLVL_NOTICE, (message));
+       assert_int_equal(2, test_ctx->calls);
+       assert_string_equal(message, test_ctx->data[0]);
+       assert_string_equal("", test_ctx->data[1]);
+}
+
+/*
+* Test DEBUGJSON honours the DBGC_ALL log level
+*/
+static void test_debugjson_log_level(void **state)
+{
+       const char *message = "A message";
+       struct test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct test_ctx);
+       assert_non_null(test_ctx);
+
+       DEBUGJSON(DBGLVL_INFO, (message));
+       assert_int_equal(0, test_ctx->calls);
+}
+
+int main(int argc, const char **argv)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test_setup_teardown(
+                       test_empty_message, setup, teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_short_message, setup, teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_large_message, setup, teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_buffer_size_message, setup, teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_embedded_new_lines, setup, teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_log_class, setup, teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_log_level, setup, teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_debugjson, setup, teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_debugjson_log_level, setup, teardown),
+       };
+
+       cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+
+}
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index 21a94f60cac..0f25fbd2160 100644
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -399,6 +399,12 @@ else:
                      local_include=False,
                      for_selftest=True)
 
+    bld.SAMBA_BINARY('test_json_logging',
+                     source='tests/test_json_logging.c',
+                     deps='cmocka samba-util',
+                     local_include=False,
+                     for_selftest=True)
+
     bld.SAMBA_BINARY('test_s4_logging',
                      source='tests/test_logging.c',
                      deps=' '.join(['CMDLINE_S4',
diff --git a/selftest/tests.py b/selftest/tests.py
index 7eace3cbced..0695ac67ea1 100644
--- a/selftest/tests.py
+++ b/selftest/tests.py
@@ -556,6 +556,8 @@ plantestsuite("samba.unittests.memcache", "none",
               [os.path.join(bindir(), "default/lib/util/test_memcache")])
 plantestsuite("samba.unittests.sys_rw", "none",
               [os.path.join(bindir(), "default/lib/util/test_sys_rw")])
+plantestsuite("samba.unittests.json_logging", "none",
+              [os.path.join(bindir(), "default/lib/util/test_json_logging")])
 plantestsuite("samba.unittests.stable_sort", "none",
               [os.path.join(bindir(), "default/lib/util/test_stable_sort")])
 plantestsuite("samba.unittests.ntlm_check", "none",


-- 
Samba Shared Repository

Reply via email to