diff --git a/emper/ContextManager.cpp b/emper/ContextManager.cpp
index b923c1b86321601b6364e5c105cf3861b72a0c3d..baab2fdf6d780d442765b8bb3852b9affa9e09d4 100644
--- a/emper/ContextManager.cpp
+++ b/emper/ContextManager.cpp
@@ -61,7 +61,7 @@ void ContextManager::saveAndStartNew(func_t freshContextHook) {
 	Context::getCurrentContext()->saveAndStart(freeContext);
 }
 
-#ifdef EMPER_LOG_OFF
+#if Debug > EMPER_LOG_LEVEL
 #define HOOK_CAPTURE_LIST contextToFree
 #else
 // We only need to caputure 'this' for the LOGD() debug macro.
diff --git a/emper/Debug.hpp b/emper/Debug.hpp
index 3c077ffe66bbf69b6677ec7dcef07439072af0a5..57d1dfac33fd17c65e2283621f799c2849aa5b3f 100644
--- a/emper/Debug.hpp
+++ b/emper/Debug.hpp
@@ -1,22 +1,12 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright © 2020 Florian Schmaus
+// Copyright © 2020-2021 Florian Schmaus, Florian Fischer
 #pragma once
 
-#include <iostream>	 // IWYU pragma: keep
-#include <sstream>	 // IWYU pragma: keep
-#include <string>		 // for string, operator<<
+#include <sstream>	// IWYU pragma: keep
+#include <string>
 #include <string_view>
 
-#include "emper-config.h"	 // IWYU pragma: keep
-
-#ifdef EMPER_LOG_OFF
-
-#define DBG(x)
-#define WDBG(x)
-#define LOGD(x)
-#define LOGDD(x)
-
-#else
+#include "emper-config.h"
 
 // If we apply clang-format to the following region, then clang-format
 // will create multi-line macros. However clang-tidy's NOLINTNEXTLINE
@@ -25,28 +15,29 @@
 // clang-format off
 
 // NOLINTNEXTLINE(bugprone-macro-parentheses)
-#define DBG(x) do { std::stringstream sst; sst << x << std::endl; emper_log("", sst.str()); } while (false)
+#define LOG(level, x, log_func, new_line) do {if constexpr (level > EMPER_LOG_LEVEL) { break; } std::stringstream sst; sst << x; if constexpr (new_line) { sst << std::endl; }; log_func(sst.str()); } while (false)
+
+// NOLINTNEXTLINE(bugprone-macro-parentheses)
+#define DBG(x) LOG(Debug, x, emper_log_no_prefix, true);
 // NOLINTNEXTLINE(bugprone-macro-parentheses)
-#define WDBG(x)	do { std::stringstream sst;	sst << x; emper_log("", sst.str()); } while (false)
+#define WDBG(x) LOG(Debug, x, emper_log_no_prefix, false);
+
 // To avoid "error: there are no arguments to ‘logD’ that depend on a
 // template parameter, so a declaration of ‘logD’ must be available"
 // we use "this->logD()" instead of simply "logD()" below.
 // NOLINTNEXTLINE(bugprone-macro-parentheses)
-#define LOGD(x)	do { std::stringstream sst;	sst << x; this->logD(sst.str()); } while (false)
+#define LOGD(x) LOG(Debug, x, this->logD, true);
 // NOLINTNEXTLINE(bugprone-macro-parentheses)
-#define LOGDD(x) do { std::stringstream sst; sst << x; this->logDD(sst.str()); } while (false)
-
-#endif
+#define LOGDD(x) LOG(FineDebug, x, this->logDD, true);
 
 // NOLINTNEXTLINE(bugprone-macro-parentheses)
-#define LOGI(x)	do { std::cerr << "Info: " << x << std::endl; } while (false)
+#define LOGI(x) LOG(Info, "Info: " << x, emper_log_no_prefix, true);
 // NOLINTNEXTLINE(bugprone-macro-parentheses)
-#define LOGW(x)	do { std::cerr << "Warning: " << x << std::endl; } while (false)
+#define LOGW(x) LOG(Warning, "Warning: " << x, emper_log_no_prefix, true);
 // NOLINTNEXTLINE(bugprone-macro-parentheses)
-#define LOGE(x)	do {std::cerr << "Error: " << x << std::endl; } while (false)
-
+#define LOGE(x) LOG(Error, "Error: " << x, emper_log_no_prefix, true);
 // NOLINTNEXTLINE(bugprone-macro-parentheses)
-#define ABORT(x) do { std::stringstream sst; sst << x; logI(sst.str()); abort(); } while (false)
+#define ABORT(x) { LOGE(x); abort(); }
 
 // clang-format on
 
@@ -82,6 +73,8 @@ void emper_add_timestamp_to(std::ostringstream& logMessage);
 
 void emper_log(const std::string& prefix, const std::string& message);
 
+static inline void emper_log_no_prefix(const std::string& message) { emper_log("", message); }
+
 template <LogSubsystem logSubsystem>
 class Logger {
  private:
diff --git a/emper/Fiber.cpp b/emper/Fiber.cpp
index 3753d920bb761df61645d35dfbd99eaa8d505a48..14a6dcc0507d418858b183a5988113db24596594 100644
--- a/emper/Fiber.cpp
+++ b/emper/Fiber.cpp
@@ -3,13 +3,10 @@
 #include "Fiber.hpp"
 
 #include <iostream>	 // for operator<<, basic_ostream, ostream, basic_ostrea...
+#include <typeinfo>	 // for type_info
 
 #include "emper-config.h"	 // IWYU pragma: keep
 
-#ifndef EMPER_LOG_OFF
-#include <typeinfo>	 // for type_info
-#endif
-
 void Fiber::run() const {
 	LOGD("run() calling " << function.target<FIBER_FUN_TEMPLATE_ARG>() << " ("
 												<< function.target_type().name() << ") with arg " << arg);
diff --git a/emper/Runtime.cpp b/emper/Runtime.cpp
index 0771fd619dbbdbc72b9fd9b79b104fffacb4fb8d..282c3354893ca95fae0c2393fecbff8f2bae724b 100644
--- a/emper/Runtime.cpp
+++ b/emper/Runtime.cpp
@@ -45,10 +45,8 @@
 #include <urcu.h>	 // for rcu_register_thread
 #endif
 
-#ifndef EMPER_LOG_OFF
 #include <syscall.h>	// for SYS_gettid
 #include <unistd.h>		// for syscall
-#endif
 
 std::mutex Runtime::currentRuntimeMutex;
 Runtime* Runtime::currentRuntime;
diff --git a/emper/io/IoContext.cpp b/emper/io/IoContext.cpp
index d1fec427ab31318cc87833f221ac57957fd929f0..3e83d55bdf5e34ff08450a64ab210877fb6f70c5 100644
--- a/emper/io/IoContext.cpp
+++ b/emper/io/IoContext.cpp
@@ -12,6 +12,7 @@
 #include <cassert>	// for assert
 #include <cerrno>		// for errno, ECANCELED, EBUSY, EAGAIN, EINTR
 #include <cstring>	// for memset
+#include <ostream>	// for basic_osteram::operator<<, operator<<
 #include <utility>
 #include <vector>
 
@@ -27,10 +28,6 @@
 #include "io/Stats.hpp"	 // for Stats, nanoseconds
 #include "lib/TaggedPtr.hpp"
 
-#ifndef EMPER_LOG_OFF
-#include <ostream>	// for basic_osteram::operator<<, operator<<
-#endif
-
 using emper::lib::TaggedPtr;
 
 namespace emper::io {
diff --git a/emper/io/io.cpp b/emper/io/io.cpp
index 83c1abeb739c090179b8a6e8eeb1c79b8065e7b7..48fb629386c1b52ec31ad8bf7b016bbb8cf03f85 100644
--- a/emper/io/io.cpp
+++ b/emper/io/io.cpp
@@ -7,16 +7,13 @@
 
 #include <cerrno>
 #include <cstring>
+#include <ostream>
 
 #include "Common.hpp"
 #include "Debug.hpp"
 #include "Fiber.hpp"
 #include "Runtime.hpp"
 
-#ifndef EMPER_LOG_OFF
-#include <ostream>
-#endif
-
 namespace emper::io {
 auto tcp_listener(std::string& host, std::string& port, const std::function<void(int)>& handler,
 									int backlog) -> Fiber* {
diff --git a/tests/SimpleActorTest.cpp b/tests/SimpleActorTest.cpp
index 76b4cfe3d08b717531c400ef348366367378c39f..e02da94c78f390196218a41fa724d65704a4a492 100644
--- a/tests/SimpleActorTest.cpp
+++ b/tests/SimpleActorTest.cpp
@@ -8,16 +8,13 @@
 #include "Actor.hpp"										 // for Actor
 #include "CountingPrivateSemaphore.hpp"	 // for CPS
 #include "Debug.hpp"										 // for WDBG
+#include "Dispatcher.hpp"								 // for Dispatcher
 #include "Fiber.hpp"										 // for Fiber
 #include "Runtime.hpp"									 // for Runtime
 #include "emper-common.h"								 // for UNUSED_ARG
 #include "emper-config.h"								 // // IWYU pragma: keep
 #include "emper.hpp"										 // for spawn
 
-#ifndef EMPER_LOG_OFF
-#include "Dispatcher.hpp"	 // for Dispatcher
-#endif
-
 class SumActor : public Actor<uint64_t> {
  private:
 	uint64_t sum = 0;