diff --git a/meson.build b/meson.build
index 0a222ba96dcb..634488589a46 100644
--- a/meson.build
+++ b/meson.build
@@ -26,6 +26,10 @@ libcamera_version = libcamera_git_version.split('+')[0]
 cc = meson.get_compiler('c')
 config_h = configuration_data()
 
+if cc.has_header_symbol('execinfo.h', 'backtrace')
+    config_h.set('HAVE_BACKTRACE', 1)
+endif
+
 if cc.has_header_symbol('stdlib.h', 'secure_getenv', prefix : '#define _GNU_SOURCE')
     config_h.set('HAVE_SECURE_GETENV', 1)
 endif
diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp
index 50f345b98c74..f4eb8c11adc3 100644
--- a/src/libcamera/log.cpp
+++ b/src/libcamera/log.cpp
@@ -7,6 +7,9 @@
 
 #include "log.h"
 
+#if HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
 #include <fstream>
 #include <iostream>
 #include <list>
@@ -108,10 +111,11 @@ public:
 
 	bool isValid() const;
 	void write(const LogMessage &msg);
+	void write(const std::string &msg);
 
 private:
-	void writeSyslog(const LogMessage &msg);
-	void writeStream(const LogMessage &msg);
+	void writeSyslog(LogSeverity severity, const std::string &msg);
+	void writeStream(const std::string &msg);
 
 	std::ostream *stream_;
 	LoggingTarget target_;
@@ -180,34 +184,55 @@ bool LogOutput::isValid() const
  * \param[in] msg Message to write
  */
 void LogOutput::write(const LogMessage &msg)
+{
+	std::string str;
+
+	switch (target_) {
+	case LoggingTargetSyslog:
+		str = std::string(log_severity_name(msg.severity())) + " "
+		    + msg.category().name() + " " + msg.fileInfo() + " "
+		    + msg.msg();
+		writeSyslog(msg.severity(), str);
+		break;
+	case LoggingTargetStream:
+	case LoggingTargetFile:
+		str = "[" + utils::time_point_to_string(msg.timestamp()) + "]"
+		    + log_severity_name(msg.severity()) + " "
+		    + msg.category().name() + " " + msg.fileInfo() + " "
+		    + msg.msg();
+		writeStream(str);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * \brief Write string to log output
+ * \param[in] str String to write
+ */
+void LogOutput::write(const std::string &str)
 {
 	switch (target_) {
 	case LoggingTargetSyslog:
-		writeSyslog(msg);
+		writeSyslog(LogDebug, str);
 		break;
 	case LoggingTargetStream:
 	case LoggingTargetFile:
-		writeStream(msg);
+		writeStream(str);
 		break;
 	default:
 		break;
 	}
 }
 
-void LogOutput::writeSyslog(const LogMessage &msg)
+void LogOutput::writeSyslog(LogSeverity severity, const std::string &str)
 {
-	std::string str = std::string(log_severity_name(msg.severity())) + " " +
-	      		  msg.category().name() + " " + msg.fileInfo() + " " +
-			  msg.msg();
-	syslog(log_severity_to_syslog(msg.severity()), "%s", str.c_str());
+	syslog(log_severity_to_syslog(severity), "%s", str.c_str());
 }
 
-void LogOutput::writeStream(const LogMessage &msg)
+void LogOutput::writeStream(const std::string &str)
 {
-	std::string str = "[" + utils::time_point_to_string(msg.timestamp()) +
-			  "]" + log_severity_name(msg.severity()) + " " +
-			  msg.category().name() + " " + msg.fileInfo() + " " +
-			  msg.msg();
 	stream_->write(str.c_str(), str.size());
 	stream_->flush();
 }
@@ -223,6 +248,7 @@ public:
 	static Logger *instance();
 
 	void write(const LogMessage &msg);
+	void backtrace();
 
 	int logSetFile(const char *path);
 	int logSetStream(std::ostream *stream);
@@ -240,9 +266,6 @@ private:
 	void registerCategory(LogCategory *category);
 	void unregisterCategory(LogCategory *category);
 
-	void writeSyslog(const LogMessage &msg);
-	void writeStream(const LogMessage &msg);
-
 	std::unordered_set<LogCategory *> categories_;
 	std::list<std::pair<std::string, LogSeverity>> levels_;
 
@@ -370,6 +393,38 @@ void Logger::write(const LogMessage &msg)
 	output->write(msg);
 }
 
+/**
+ * \brief Write a backtrace to the log
+ */
+void Logger::backtrace()
+{
+#if HAVE_BACKTRACE
+	std::shared_ptr<LogOutput> output = std::atomic_load(&output_);
+	if (!output)
+		return;
+
+	void *buffer[32];
+	int num_entries = ::backtrace(buffer, ARRAY_SIZE(buffer));
+	char **strings = backtrace_symbols(buffer, num_entries);
+	if (!strings)
+		return;
+
+	std::ostringstream msg;
+	msg << "Backtrace:" << std::endl;
+
+	/*
+	 * Skip the first two entries that correspond to this method and
+	 * ~LogMessage().
+	 */
+	for (int i = 2; i < num_entries; ++i)
+		msg << strings[i] << std::endl;
+
+	output->write(msg.str());
+
+	free(strings);
+#endif
+}
+
 /**
  * \brief Set the log file
  * \param[in] path Full path to the log file
@@ -783,8 +838,10 @@ LogMessage::~LogMessage()
 	if (severity_ >= category_.severity())
 		Logger::instance()->write(*this);
 
-	if (severity_ == LogSeverity::LogFatal)
+	if (severity_ == LogSeverity::LogFatal) {
+		Logger::instance()->backtrace();
 		std::abort();
+	}
 }
 
 /**
