diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8612629802..7b1f241e09 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -957,6 +957,7 @@ set(LIBNETDATA_FILES
         src/libnetdata/locks/benchmark.h
         src/libnetdata/locks/benchmark-rw.c
         src/libnetdata/locks/benchmark-rw.h
+        src/libnetdata/log/nd_log-libunwind.c
 )
 
 set(LIBH2O_FILES
@@ -2092,6 +2093,25 @@ netdata_add_jsonc_to_target(libnetdata)
 
 netdata_add_libyaml_to_target(libnetdata)
 
+# libunwind
+pkg_check_modules(LIBUNWIND libunwind IMPORTED_TARGET)
+if(TARGET PkgConfig::LIBUNWIND)
+    set(HAVE_LIBUNWIND On)
+
+    if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(amd64)")
+        target_link_libraries(libnetdata PUBLIC PkgConfig::LIBUNWIND -lunwind-x86_64)
+    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
+        target_link_libraries(libnetdata PUBLIC PkgConfig::LIBUNWIND -lunwind-aarch64)
+    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
+        target_link_libraries(libnetdata PUBLIC PkgConfig::LIBUNWIND -lunwind-arm)
+    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "powerpc|ppc")
+        target_link_libraries(libnetdata PUBLIC PkgConfig::LIBUNWIND -lunwind-ppc64)
+    else()
+        message(WARNING "Unknown architecture ${CMAKE_SYSTEM_PROCESSOR} for libunwind. Stack traces may not work.")
+        target_link_libraries(libnetdata PUBLIC PkgConfig::LIBUNWIND)
+    endif()
+endif()
+
 # zlib
 if(OS_MACOS)
         find_package(ZLIB REQUIRED)
diff --git a/packaging/cmake/config.cmake.h.in b/packaging/cmake/config.cmake.h.in
index d82cf52ad7..b4613b5532 100644
--- a/packaging/cmake/config.cmake.h.in
+++ b/packaging/cmake/config.cmake.h.in
@@ -75,6 +75,7 @@
 #cmakedefine HAVE_GETRANDOM
 #cmakedefine HAVE_SYSINFO
 
+#cmakedefine HAVE_LIBUNWIND
 #cmakedefine HAVE_BACKTRACE
 #cmakedefine HAVE_CLOSE_RANGE
 #cmakedefine HAVE_SCHED_GETSCHEDULER
diff --git a/src/libnetdata/log/nd_log-common.h b/src/libnetdata/log/nd_log-common.h
index d06bbbd16e..c85d914aab 100644
--- a/src/libnetdata/log/nd_log-common.h
+++ b/src/libnetdata/log/nd_log-common.h
@@ -119,11 +119,13 @@ typedef enum __attribute__((__packed__)) {
     NDF_ALERT_NOTIFICATION_REALTIME_USEC = 62,
     // NDF_ALERT_FLAGS,
 
+    NDF_STACK_TRACE = 63,                           // stack trace of the thread logging
+
     // put new items here
     // leave the request URL and the message last
 
-    NDF_REQUEST = 63,                               // the request we are currently working on
-    NDF_MESSAGE = 64,                               // the log message, if any
+    NDF_REQUEST = 64,                               // the request we are currently working on
+    NDF_MESSAGE = 65,                               // the log message, if any
 
     // terminator
     _NDF_MAX,
diff --git a/src/libnetdata/log/nd_log-internals.c b/src/libnetdata/log/nd_log-internals.c
index c57c7f72c3..e233df5352 100644
--- a/src/libnetdata/log/nd_log-internals.c
+++ b/src/libnetdata/log/nd_log-internals.c
@@ -707,6 +707,12 @@ __thread struct log_field thread_log_fields[_NDF_MAX] = {
         .logfmt = "alert_notification_timestamp",
         .annotator = timestamp_usec_annotator,
     },
+    [NDF_STACK_TRACE] = {
+        .journal = "ND_STACK_TRACE",
+        .eventlog = "StackTrace",
+        .logfmt = NULL,
+        .annotator = stack_trace_annotator,
+    },
 
     // put new items here
     // leave the request URL and the message last
diff --git a/src/libnetdata/log/nd_log-internals.h b/src/libnetdata/log/nd_log-internals.h
index 7bebf5a4a6..8c9ea6a2fb 100644
--- a/src/libnetdata/log/nd_log-internals.h
+++ b/src/libnetdata/log/nd_log-internals.h
@@ -195,6 +195,7 @@ struct log_field;
 const char *errno_annotator(struct log_field *lf);
 const char *priority_annotator(struct log_field *lf);
 const char *timestamp_usec_annotator(struct log_field *lf);
+const char *stack_trace_annotator(struct log_field *lf);
 
 #if defined(OS_WINDOWS)
 const char *winerror_annotator(struct log_field *lf);
diff --git a/src/libnetdata/log/nd_log-libunwind.c b/src/libnetdata/log/nd_log-libunwind.c
new file mode 100644
index 0000000000..838ef8885f
--- /dev/null
+++ b/src/libnetdata/log/nd_log-libunwind.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "nd_log-internals.h"
+
+#ifdef HAVE_LIBUNWIND
+#include <libunwind.h>
+
+const char *stack_trace_annotator(struct log_field *lf __maybe_unused) {
+    static __thread char stack[4096];
+    static __thread bool in_stack_trace = false;
+
+    // prevent recursion
+    if(in_stack_trace)
+        return "stack trace recursion detected";
+
+    in_stack_trace = true;
+
+    unw_cursor_t cursor;
+    unw_context_t context;
+    char *d = stack;
+    size_t frames = 0;
+
+    // Initialize context for current thread
+    unw_getcontext(&context);
+    unw_init_local(&cursor, &context);
+
+    // Skip first 3 frames (our annotator and the logging infrastructure)
+    unw_step(&cursor);
+    unw_step(&cursor);
+    unw_step(&cursor);
+
+    while (unw_step(&cursor) > 0) {
+        unw_word_t offset, pc;
+        char sym[256];
+
+        unw_get_reg(&cursor, UNW_REG_IP, &pc);
+        if (pc == 0)
+            break;
+
+        const char *name = sym;
+        if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
+            if(frames++)
+                d += snprintfz(d, sizeof(stack) - (d - stack), "\n");
+            d += snprintfz(d, sizeof(stack) - (d - stack), "%s+0x%lx", name, (unsigned long)offset);
+        }
+        else {
+            if(frames++)
+                d += snprintfz(d, sizeof(stack) - (d - stack), "\n");
+            d += snprintfz(d, sizeof(stack) - (d - stack), "<unknown>");
+        }
+    }
+
+    in_stack_trace = false;
+    return stack;
+}
+
+#else
+const char *stack_trace_annotator(struct log_field *lf __maybe_unused) {
+    return "libunwind not available";
+}
+#endif
diff --git a/src/libnetdata/log/nd_log.c b/src/libnetdata/log/nd_log.c
index 28c2512a2d..50c868fb92 100644
--- a/src/libnetdata/log/nd_log.c
+++ b/src/libnetdata/log/nd_log.c
@@ -232,6 +232,9 @@ static void nd_logger(const char *file, const char *function, const unsigned lon
 
     // set the common fields that are automatically set by the logging subsystem
 
+    if(likely(!thread_log_fields[NDF_STACK_TRACE].entry.set))
+        thread_log_fields[NDF_STACK_TRACE].entry = ND_LOG_FIELD_U64(NDF_STACK_TRACE, 1);
+
     if(likely(!thread_log_fields[NDF_INVOCATION_ID].entry.set))
         thread_log_fields[NDF_INVOCATION_ID].entry = ND_LOG_FIELD_UUID(NDF_INVOCATION_ID, &nd_log.invocation_id);