diff --git a/corsika/detail/framework/random/RNGManager.inl b/corsika/detail/framework/random/RNGManager.inl
index 749107cd9206adb3e3e3fc78b9e666f59f7b7f27..a7385ab32ade1a74c8b2f0d5713b43ec8f5a297e 100644
--- a/corsika/detail/framework/random/RNGManager.inl
+++ b/corsika/detail/framework/random/RNGManager.inl
@@ -22,10 +22,20 @@ namespace corsika {
     rngs[pStreamName] = std::move(rng);
   }
 
-  inline RNG& RNGManager::GetRandomStream(std::string const& pStreamName) {
-    return rngs.at(pStreamName);
+  inline RNG& RNGManager::GetRandomStream(std::string const& pStreamName)  {
+	  if (IsRegistered(pStreamName)) {
+	    return rngs.at(pStreamName);
+	  } else { // this stream name is not in the map
+	    throw std::runtime_error("'" + pStreamName + "' is not a registered stream.");
+	  }
+	}
+
+
+  inline bool RNGManager::IsRegistered(std::string const& pStreamName) const {
+    return rngs.count(pStreamName) > 0;
   }
 
+
   inline std::stringstream RNGManager::dumpState() const {
     std::stringstream buffer;
     for (auto const& [streamName, rng] : rngs) {
@@ -41,11 +51,15 @@ namespace corsika {
 
   inline void RNGManager::SeedAll() {
     std::random_device rd;
-
+    std::seed_seq sseq{rd(), rd(), rd(), rd(), rd(), rd()};
     for (auto& entry : rngs) {
-      std::seed_seq sseq{rd(), rd(), rd(), rd(), rd(), rd()};
-      entry.second.seed(sseq);
+      std::vector<std::uint32_t> seeds(1);
+      sseq.generate(seeds.begin(), seeds.end());
+      std::uint32_t seed = seeds[0];
+      C8LOG_TRACE("Random seed stream {} seed {}", entry.first, seed);
+      entry.second.seed(seed);
     }
   }
 
+
 } // namespace corsika
diff --git a/corsika/detail/modules/pythia8/Random.inl b/corsika/detail/modules/pythia8/Random.inl
deleted file mode 100644
index cfd19bb19cb768a79c303760e34733f5e23f0f15..0000000000000000000000000000000000000000
--- a/corsika/detail/modules/pythia8/Random.inl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * See file AUTHORS for a list of contributors.
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
-
-#pragma once
-
-#include <corsika/modules/pythia8/Random.hpp>
-
-namespace corsika::pythia8 {
-
-  double Random::flat() { return fDist(fRNG); }
-
-} // namespace corsika::pythia8
diff --git a/corsika/framework/logging/Logging.h b/corsika/framework/logging/Logging.h
new file mode 100644
index 0000000000000000000000000000000000000000..814f35de4de5c025c3bab8eaa7fdf51e135e7010
--- /dev/null
+++ b/corsika/framework/logging/Logging.h
@@ -0,0 +1,189 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+/**
+ *  @File Logging.h
+ *
+ * CORSIKA8 logging utilities.
+ *
+ * See testLogging.cc for a complete set of examples for
+ * how the logging functions should be used.
+ */
+
+#pragma once
+
+// Configure some behaviour of sdlog.
+// This must be done before spdlog is included.
+
+// use the coarse system clock. This is *much* faster
+// but introduces a real time error of O(10 ms).
+#define SPDLOG_CLOCK_COARSE
+
+// do not create a default logger (we provide our own "corsika" logger)
+#define SPDLOG_DISABLE_DEFAULT_LOGGER
+
+// use __PRETTY_FUNCTION__ instead of __FUNCTION__ where
+// printing function names in trace statements. This is much
+// nicer than __FUNCTION__ under GCC/clang.
+#define SPDLOG_FUNCTION __PRETTY_FUNCTION__
+
+// if this is a Debug build, include debug messages in objects
+#ifdef DEBUG
+// trace is the highest level of logging (ALL messages will be printed)
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
+#else // otherwise, remove everything but "critical" messages
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_CRITICAL
+#endif
+
+#include <spdlog/fmt/ostr.h> // will output whenerver a streaming operator is found
+#include <spdlog/sinks/stdout_color_sinks.h>
+#include <spdlog/spdlog.h>
+
+namespace corsika::logging {
+
+  // bring spdlog into the corsika::logging namespace
+  using namespace spdlog;
+
+  /*
+   * The default pattern for CORSIKA8 loggers.
+   */
+  const std::string default_pattern{"[%n:%^%-8l%$] %v"};
+
+  /**
+   * Create a new C8-style logger.
+   *
+   * Use this if you are explicitly (and can guarantee) that you
+   * are creating a logger for the first time. It is recommended
+   * that for regular usage, the `GetLogger` function is used instead
+   * as that will also create the logger if it has not yet been created.
+   *
+   * Calling `CreateLogger` twice to create the same logger will
+   * result in an spdlog duplicate exception.
+   *
+   * @param name           The unique name of the logger.
+   * @param defaultlog     If True, set this as the default logger.
+   * @returns              The constructed and formatted logger.
+   */
+  inline auto CreateLogger(std::string const& name, bool const defaultlog = false) {
+
+    // create the logger
+    // this is currently a colorized multi-threading safe logger
+    auto logger = spdlog::stdout_color_mt(name);
+
+    // set the default C8 format
+    logger->set_pattern(default_pattern);
+
+    // if defaultlog is True, we set this as the default spdlog logger.
+    if (defaultlog) { spdlog::set_default_logger(logger); }
+
+    // and return the logger
+    return logger;
+  }
+
+  /**
+   * Get a smart pointer to an existing logger.
+   *
+   * This should be the default method for code to obtain a
+   * logger. If the logger *does not* exist, it is *created* and
+   * returned to the caller.
+   *
+   * This should be preferred over `CreateLogger`.
+   *
+   * @param name    The name of the logger to get.
+   * @param defaultlog   If True, make this the default logger.
+   * @returns              The constructed and formatted logger.
+   */
+  inline auto GetLogger(std::string const& name, bool const defaultlog = false) {
+
+    // attempt to get the logger from the registry
+    auto logger = spdlog::get(name);
+
+    // weg found the logger, so just return it
+    if (logger) {
+      return logger;
+    } else { // logger was not found so create it
+      return CreateLogger(name, defaultlog);
+    }
+  }
+
+  /**
+   * The default "corsika" logger.
+   */
+  inline auto corsika = GetLogger("corsika", true);
+
+  /**
+   * Set the default log level for all *newly* created loggers.
+   *
+   *  @param name    The minimum log level required to print.
+   *
+   */
+  inline auto SetDefaultLevel(level::level_enum const minlevel) -> void {
+    spdlog::set_level(minlevel);
+  }
+
+  /**
+   * Set the log level for the "corsika" logger.
+   *
+   * @param name    The minimum log level required to print.
+   *
+   */
+  inline auto SetLevel(level::level_enum const minlevel) -> void {
+    corsika->set_level(minlevel);
+  }
+
+  /**
+   * Set the log level for a specific logger.
+   *
+   * @param logger  The logger to set the level of.
+   * @param name    The minimum log level required to print.
+   *
+   */
+  template <typename TLogger>
+  inline auto SetLevel(TLogger& logger, level::level_enum const minlevel) -> void {
+    logger->set_level(minlevel);
+  }
+
+  /**
+   * Add the source (filename, line no) info to the logger.
+   *
+   * @param logger  The logger to set the level of.
+   *
+   */
+  template <typename TLogger>
+  inline auto AddSourceInfo(TLogger& logger) -> void {
+    logger->set_pattern("[%n:%^%-8l%$(%s:%!:%#)] %v");
+  }
+
+  /**
+   * Reset the logging pattern to the default.
+   *
+   * @param logger  The logger to set the level of.
+   *
+   */
+  template <typename TLogger>
+  inline auto ResetPattern(TLogger& logger) -> void {
+    logger->set_pattern(default_pattern);
+  }
+
+// define our macro-style loggers
+#define C8LOG_TRACE SPDLOG_TRACE
+#define C8LOG_DEBUG SPDLOG_DEBUG
+#define C8LOG_INFO SPDLOG_INFO
+#define C8LOG_WARN SPDLOG_WARN
+#define C8LOG_ERROR SPDLOG_ERROR
+#define C8LOG_CRITICAL SPDLOG_CRITICAL
+
+// and the specific logger versions
+#define C8LOG_LOGGER_TRACE SPDLOG_LOGGER_TRACE
+#define C8LOG_LOGGER_DEBUG SPDLOG_LOGGER_DEBUG
+#define C8LOG_LOGGER_INFO SPDLOG_LOGGER_INFO
+#define C8LOG_LOGGER_WARN SPDLOG_LOGGER_WARN
+#define C8LOG_LOGGER_ERROR SPDLOG_LOGGER_ERROR
+#define C8LOG_LOGGER_CRITICAL SPDLOG_LOGGER_CRITICAL
+
+} // namespace corsika::logging
diff --git a/corsika/framework/random/RNGManager.hpp b/corsika/framework/random/RNGManager.hpp
index 6e822466ecd3c2b4673d8c84089f09a30d44eddb..4778c179dd172ab13499ef5eded3db66ebec70f6 100644
--- a/corsika/framework/random/RNGManager.hpp
+++ b/corsika/framework/random/RNGManager.hpp
@@ -9,6 +9,7 @@
 #pragma once
 
 #include <corsika/framework/utility/Singleton.hpp>
+#include <corsika/framework/logging/Logging.h>
 #include <map>
 #include <random>
 #include <string>
diff --git a/modules/conex b/modules/conex
index c17fc68bd35612e1b610ce2b43e84e91577b8a86..c6b0301c0f8eecf530ce8498d47cb1b56e070d37 160000
--- a/modules/conex
+++ b/modules/conex
@@ -1 +1 @@
-Subproject commit c17fc68bd35612e1b610ce2b43e84e91577b8a86
+Subproject commit c6b0301c0f8eecf530ce8498d47cb1b56e070d37
diff --git a/tests/framework/testRandom.cpp b/tests/framework/testRandom.cpp
index 7a894c9e0c3b0b4c7679fd007bbe76e32effefb4..d094582a69f191ba755f2ce2979901f07e8227b2 100644
--- a/tests/framework/testRandom.cpp
+++ b/tests/framework/testRandom.cpp
@@ -15,26 +15,41 @@
 #include <iostream>
 #include <limits>
 #include <random>
-#include <type_traits>
+
 
 using namespace corsika;
 
+
 SCENARIO("random-number streams can be registered and retrieved") {
   GIVEN("a RNGManager") {
     RNGManager& rngManager = RNGManager::GetInstance();
 
-    WHEN("a sequence is registered by name") {
-      rngManager.RegisterRandomStream("stream_A");
+    WHEN("the sequence name is not registered") {
+      REQUIRE(rngManager.IsRegistered("stream_A") == false);
 
-      THEN("the sequence can be retrieved") {
-        REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A"));
-      }
+      THEN("a sequence is registered by name") {
+        rngManager.RegisterRandomStream("stream_A");
 
-      THEN("an unknown sequence cannot be retrieved") {
-        REQUIRE_THROWS(rngManager.GetRandomStream("stream_UNKNOWN"));
-      }
+        THEN("the sequence can be retrieved") {
+          REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A"));
 
-      // seeding not covered yet
+          THEN("we can check that the sequence exists") {
+            REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A"));
+
+            THEN("an unknown sequence cannot be retrieved") {
+              REQUIRE(rngManager.IsRegistered("stream_A") == true);
+
+              THEN("an unknown sequence cannot be retrieved") {
+                REQUIRE_THROWS(rngManager.GetRandomStream("stream_UNKNOWN"));
+
+                THEN("an unknown sequence is not registered") {
+                  REQUIRE(rngManager.IsRegistered("stream_UNKNOWN") == false);
+                }
+              }
+            }
+          }
+        }
+      }
     }
   }
 }