diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 64918652aa1cc9cd267007284ee558feae117532..0492e227ee3e57d46e6203874618999061f3e956 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(log)
 add_subdirectory(data)
 add_subdirectory(test)
 
@@ -77,7 +78,7 @@ target_link_libraries(Algo
             Boost::filesystem
             xpu
             external::yaml-cpp
-  INTERFACE FairLogger::FairLogger
+            external::fles_logging
             external::fles_ipc
 )
 target_compile_definitions(Algo PUBLIC NO_ROOT)
diff --git a/algo/base/Options.cxx b/algo/base/Options.cxx
index ea1b73c05994e0bdaf1486e9059db5a80ce3e83c..cecf5b9de779bbc1f3ae3eefc4a04b2465d2bc78 100644
--- a/algo/base/Options.cxx
+++ b/algo/base/Options.cxx
@@ -9,6 +9,27 @@
 
 using namespace cbm::algo;
 
+void validate(boost::any& v, const std::vector<std::string>& values, severity_level*, int)
+{
+
+  static const std::unordered_map<std::string, severity_level> levels {
+    {"trace", severity_level::trace}, {"debug", severity_level::debug},     {"status", severity_level::status},
+    {"info", severity_level::info},   {"warning", severity_level::warning}, {"error", severity_level::error},
+    {"fatal", severity_level::fatal}};
+
+  namespace po = boost::program_options;
+
+  po::validators::check_first_occurrence(v);
+
+  const std::string& s = po::validators::get_single_string(values);
+
+  auto it = levels.find(s);
+
+  if (it == levels.end()) { throw po::validation_error(po::validation_error::invalid_option_value); }
+
+  v = it->second;
+}
+
 Options::Options(int argc, char** argv)
 {
   namespace po = boost::program_options;
@@ -28,7 +49,7 @@ Options::Options(int argc, char** argv)
   generic.add_options()
     ("device,d", po::value<std::string>(&fDevice)->default_value("cpu")->value_name("<device>"),
       "select device (cpu, cuda0, cuda1, hip0, ...)")
-    ("log-level,l", po::value<std::string>(&fLogLevel)->default_value("info")->value_name("<level>"),
+    ("log-level,l", po::value(&fLogLevel)->default_value(info)->value_name("<level>"),
       "set log level (debug, info, warning, error, fatal)")
     ("num-ts,n", po::value<int>(&fNumTimeslices)->default_value(-1)->value_name("<num>"),
       "Stop after <num> timeslices (-1 = all)")
diff --git a/algo/base/Options.h b/algo/base/Options.h
index 5075152d0f8e580c41ddb4447b757355e944282a..25c3f1b99a32f7f583d5a7a4e49c813c39c35705 100644
--- a/algo/base/Options.h
+++ b/algo/base/Options.h
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "compat/Filesystem.h"
+#include "log.hpp"
 
 namespace cbm::algo
 {
@@ -19,7 +20,7 @@ namespace cbm::algo
 
     fs::path ParamsDir() const { return fParamsDir; }
     const std::string& InputLocator() const { return fInputLocator; }
-    const std::string& LogLevel() const { return fLogLevel; }
+    severity_level LogLevel() const { return fLogLevel; }
     const std::string& Device() const { return fDevice; }
     bool CollectKernelTimes() const { return fCollectKernelTimes; }
     int NumTimeslices() const { return fNumTimeslices; }
@@ -28,7 +29,7 @@ namespace cbm::algo
   private:
     std::string fParamsDir;  // TODO: can we make this a std::path?
     std::string fInputLocator;
-    std::string fLogLevel;
+    severity_level fLogLevel;
     std::string fDevice;
     bool fCollectKernelTimes = false;
     int fNumTimeslices       = -1;
diff --git a/algo/base/config/Yaml.h b/algo/base/config/Yaml.h
index a7100184a9144145779c7987d37389787adc8e95..1697a23be1de0876364f4868198479fcd186d40c 100644
--- a/algo/base/config/Yaml.h
+++ b/algo/base/config/Yaml.h
@@ -4,11 +4,10 @@
 #ifndef CBM_ALGO_BASE_CONFIG_DESERIALIZE_H
 #define CBM_ALGO_BASE_CONFIG_DESERIALIZE_H
 
-#include <fairlogger/Logger.h>
-
 #include <sstream>
 #include <string_view>
 
+#include <fmt/format.h>
 #include <yaml-cpp/yaml.h>
 
 #include "BaseTypes.h"
diff --git a/algo/data/CMakeLists.txt b/algo/data/CMakeLists.txt
index 62acd74b15849fe57c27354bbab6ec4502328096..e684dc5462a4c9c6d81d6fc614a2392d7e5a7b84 100644
--- a/algo/data/CMakeLists.txt
+++ b/algo/data/CMakeLists.txt
@@ -49,10 +49,11 @@ target_include_directories(OnlineData
 
 target_compile_definitions(OnlineData PUBLIC NO_ROOT)
 target_link_libraries(OnlineData
-                      FairLogger::FairLogger
+                      OnlineDataLog
                       external::fles_ipc
                       xpu
                       Boost::serialization
+                      external::fles_logging
                      )
 
 install(TARGETS OnlineData DESTINATION lib)
diff --git a/algo/data/sts/LandauTable.cxx b/algo/data/sts/LandauTable.cxx
index fcea6f0c7251d22b06e742a5c502c95104816a54..fb146c002351724a0f0452d3e50d79dafb28ee84 100644
--- a/algo/data/sts/LandauTable.cxx
+++ b/algo/data/sts/LandauTable.cxx
@@ -3,10 +3,10 @@
    Authors: Felix Weiglhofer [committer] */
 #include "LandauTable.h"
 
-#include <fairlogger/Logger.h>
-
 #include <fstream>
 
+#include "log.hpp"
+
 using namespace cbm::algo;
 
 sts::LandauTable sts::LandauTable::FromFile(fs::path path)
@@ -24,7 +24,7 @@ sts::LandauTable sts::LandauTable::FromFile(fs::path path)
     charge.push_back(q);
     prob.push_back(p);
 
-    LOG(trace) << "Reading Landau table " << path << " q=" << q << " p=" << p;
+    L_(trace) << "Reading Landau table " << path << " q=" << q << " p=" << p;
   }
 
   // TODO: check if charge is monotonically increasing, also more than 2 entries
diff --git a/algo/detectors/bmon/BmonReadoutConfig.cxx b/algo/detectors/bmon/BmonReadoutConfig.cxx
index 6c6eeb39a756e2bc14d4b20eab3ff9ebc4cac788..021c0167e69ea7fc2b587d99923350b293c32010 100644
--- a/algo/detectors/bmon/BmonReadoutConfig.cxx
+++ b/algo/detectors/bmon/BmonReadoutConfig.cxx
@@ -6,11 +6,10 @@
 
 #include "CbmTofAddress.h"
 
-#include <Logger.h>
-
 #include <bitset>
 #include <iomanip>
 
+#include "AlgoFairloggerCompat.h"
 #include "gDpbMessv100.h"
 
 using namespace std;
diff --git a/algo/detectors/much/MuchReadoutConfig.cxx b/algo/detectors/much/MuchReadoutConfig.cxx
index 0469b1d86afb5a7643934c3a54a076655760d589..eb6fa69a5bf26c8cd0074c04c6e73e66ae0159de 100644
--- a/algo/detectors/much/MuchReadoutConfig.cxx
+++ b/algo/detectors/much/MuchReadoutConfig.cxx
@@ -6,10 +6,10 @@
 
 #include "CbmMuchAddress.h"
 
-#include <Logger.h>
-
 #include <bitset>
 
+#include "AlgoFairloggerCompat.h"
+
 using namespace std;
 
 namespace cbm::algo
diff --git a/algo/detectors/sts/StsHitfinderChain.cxx b/algo/detectors/sts/StsHitfinderChain.cxx
index dc1f856d86b21a3b92f25f8aa605942035504f74..7cef8dd32dad70cde3667b4274787ee7a08f9431 100644
--- a/algo/detectors/sts/StsHitfinderChain.cxx
+++ b/algo/detectors/sts/StsHitfinderChain.cxx
@@ -4,8 +4,7 @@
 
 #include "StsHitfinderChain.h"
 
-#include <fairlogger/Logger.h>
-
+#include <log.hpp>
 #include <numeric>
 
 using namespace cbm::algo;
@@ -37,14 +36,14 @@ void sts::HitfinderChain::operator()(gsl::span<const CbmStsDigi> digis)
   // 3. Copy digis into flat array with offsets per module
   FlattenDigis(digiMap);
 
-  if (fair::Logger::GetConsoleSeverity() == fair::Severity::trace) { EnsureDigiOffsets(digiMap); }
+  if (Opts().LogLevel() == trace) EnsureDigiOffsets(digiMap);
 
   xpu::queue queue;
 
   // Clear buffers
   // Not all buffers have to initialized, but useful for debugging
 
-  LOG(debug) << "STS Hitfinder Chain: Clearing buffers...";
+  L_(debug) << "STS Hitfinder Chain: Clearing buffers...";
   auto& hfc = fHitfinder;
   // xpu::memset(hfc.digisPerModule, 0);
   // xpu::memset(hfc.digisPerModuleTmp, 0);
@@ -61,25 +60,25 @@ void sts::HitfinderChain::operator()(gsl::span<const CbmStsDigi> digis)
   queue.memset(hfc.nHitsPerModule, 0);
   queue.memset(hfc.maxClusterTimeErrorByModuleSide, 0);
 
-  LOG(debug) << "STS Hitfinder Chain: Copy digis buffers...";
+  L_(debug) << "STS Hitfinder Chain: Copy digis buffers...";
   xpu::set<TheHitfinder>(fHitfinder);
   queue.copy(hfc.digisPerModule, xpu::h2d);
   queue.copy(hfc.digiOffsetPerModule, xpu::h2d);
 
-  LOG(debug) << "STS Hitfinder Chain: Sort Digis...";
+  L_(debug) << "STS Hitfinder Chain: Sort Digis...";
   // TODO: skip temporary buffers and sort directly into digisSortedPerModule
 
   queue.launch<SortDigis>(xpu::n_blocks(nModuleSides));
   xpu::k_add_bytes<SortDigis>(nDigisTotal * sizeof(CbmStsDigi));
-  if (fair::Logger::GetConsoleSeverity() == fair::Severity::trace) {
-    LOG(trace) << "Ensuring STS digis are sorted...";
+  if (Opts().LogLevel() == trace) {
+    L_(trace) << "Ensuring STS digis are sorted...";
     queue.copy(hfc.digisPerModule, xpu::d2h);
     queue.copy(hfc.digiOffsetPerModule, xpu::d2h);
     queue.wait();
     EnsureDigisSorted();
   }
 
-  LOG(debug) << "STS Hitfinder Chain: Find Clusters...";
+  L_(debug) << "STS Hitfinder Chain: Find Clusters...";
   if (!Params().sts.findClustersMultiKernels) { queue.launch<FindClusters>(xpu::n_blocks(nModuleSides)); }
   else {
     queue.launch<ChannelOffsets>(xpu::n_blocks(nModuleSides));
@@ -89,8 +88,8 @@ void sts::HitfinderChain::operator()(gsl::span<const CbmStsDigi> digis)
     queue.launch<CreateClusters>(xpu::n_blocks(nModuleSides));
     xpu::k_add_bytes<CreateClusters>(nDigisTotal * sizeof(CbmStsDigi));
   }
-  if (fair::Logger::GetConsoleSeverity() == fair::Severity::trace) {
-    LOG(trace) << "Ensuring STS channel offsets correct...";
+  if (Opts().LogLevel() == trace) {
+    L_(trace) << "Ensuring STS channel offsets correct...";
     xpu::buffer_prop propsOffset {hfc.channelOffsetPerModule};
     std::vector<u32> channelOffsetPerModule;
     channelOffsetPerModule.resize(propsOffset.size());
@@ -98,7 +97,7 @@ void sts::HitfinderChain::operator()(gsl::span<const CbmStsDigi> digis)
     queue.wait();
     EnsureChannelOffsets(channelOffsetPerModule);
 
-    LOG(trace) << "Ensuring STS clusters are ok...";
+    L_(trace) << "Ensuring STS clusters are ok...";
     xpu::buffer_prop props {hfc.clusterIdxPerModule};
 
     std::vector<GpuClusterIdx> clusterIdxPerModule;
@@ -118,10 +117,10 @@ void sts::HitfinderChain::operator()(gsl::span<const CbmStsDigi> digis)
   // xpu::run_kernel<CalculateClusters>(xpu::grid::n_blocks(hfc.nModules * 2));
   // xpu::run_kernel<FindClustersBasic>(xpu::grid::n_blocks(hfc.nModules * 2));
   // xpu::run_kernel<CalculateClustersBasic>(xpu::grid::n_blocks(hfc.nModules * 2));
-  LOG(debug) << "STS Hitfinder Chain: Sort Clusters...";
+  L_(debug) << "STS Hitfinder Chain: Sort Clusters...";
   queue.launch<SortClusters>(xpu::n_blocks(nModuleSides));  // FIXME n_blocks(nModuleSides) once debugging is done
 
-  LOG(debug) << "STS Hitfinder Chain: Find Hits...";
+  L_(debug) << "STS Hitfinder Chain: Find Hits...";
   queue.copy(hfc.nClustersPerModule, xpu::d2h);
   queue.wait();
   xpu::h_view nClusters {hfc.nClustersPerModule};
@@ -150,12 +149,12 @@ void sts::HitfinderChain::operator()(gsl::span<const CbmStsDigi> digis)
   xpu::k_add_bytes<SortClusters>(nClustersTotal * sizeof(GpuClusterIdx));
   xpu::k_add_bytes<FindHits>(nClustersTotal * sizeof(sts::Cluster));
 
-  LOG(info) << "Timeslice contains " << nHitsTotal << " STS hits and " << nClustersTotal << " STS clusters";
+  L_(info) << "Timeslice contains " << nHitsTotal << " STS hits and " << nClustersTotal << " STS clusters";
 }
 
 void sts::HitfinderChain::EnsureParameters()
 {
-  LOG_IF(fatal, fPars == std::nullopt) << "sts::HitfinderChain: Parameters not set. Can't continue!";
+  if (fPars == std::nullopt) throw std::runtime_error("sts::HitfinderChain: Parameters not set. Can't continue!");
 }
 
 void sts::HitfinderChain::AllocateStatic()
@@ -221,8 +220,8 @@ void sts::HitfinderChain::AllocateStatic()
 
 void sts::HitfinderChain::AllocateDynamic(size_t maxNDigisPerModule, size_t nDigisTotal)
 {
-  LOG(debug) << "STS Hitfinder Chain: Allocating dynamic memory for " << maxNDigisPerModule << " digis per module and "
-             << nDigisTotal << " digis in total";
+  L_(debug) << "STS Hitfinder Chain: Allocating dynamic memory for " << maxNDigisPerModule << " digis per module and "
+            << nDigisTotal << " digis in total";
   EnsureParameters();
 
   xpu::scoped_timer t_ {"Allocate"};
@@ -266,7 +265,7 @@ void sts::HitfinderChain::AllocateDynamic(size_t maxNDigisPerModule, size_t nDig
 sts::HitfinderChain::DigiMap sts::HitfinderChain::SortDigisIntoModules(gsl::span<const CbmStsDigi> digis,
                                                                        size_t& maxNDigisPerModule)
 {
-  LOG(debug) << "STS Hitfinder Chain: Sorting " << digis.size() << " digis into modules";
+  L_(debug) << "STS Hitfinder Chain: Sorting " << digis.size() << " digis into modules";
   xpu::scoped_timer t_ {"Sort Digis"};
 
   DigiMap digiMap;
@@ -298,13 +297,13 @@ sts::HitfinderChain::DigiMap sts::HitfinderChain::SortDigisIntoModules(gsl::span
     }
   }
 
-  LOG_IF(warn, nPulsers > 0) << "STS Hitfinder: Discarded " << nPulsers << " pulser digis";
+  if (nPulsers > 0) L_(warning) << "STS Hitfinder: Discarded " << nPulsers << " pulser digis";
 
   // Print digi counts per module
   for (const auto& mod : fPars->modules) {
     i32 moduleAddr = mod.address;
-    LOG(debug1) << "Module " << moduleAddr << " has " << digiMap.front[moduleAddr].size() << " front digis and "
-                << digiMap.back[moduleAddr].size() << " back digis";
+    L_(debug) << "Module " << moduleAddr << " has " << digiMap.front[moduleAddr].size() << " front digis and "
+              << digiMap.back[moduleAddr].size() << " back digis";
   }
 
   maxNDigisPerModule = 0;
@@ -321,7 +320,7 @@ sts::HitfinderChain::DigiMap sts::HitfinderChain::SortDigisIntoModules(gsl::span
 
 void sts::HitfinderChain::FlattenDigis(DigiMap& digiMap)
 {
-  LOG(debug) << "STS Hitfinder Chain: Flattening digis";
+  L_(debug) << "STS Hitfinder Chain: Flattening digis";
   xpu::scoped_timer t_ {"Flatten Digis"};
   FlattenDigisSide(digiMap.front, true);
   FlattenDigisSide(digiMap.back, false);
@@ -412,19 +411,29 @@ void sts::HitfinderChain::EnsureDigiOffsets(DigiMap& digi)
   // Front
   for (size_t m = 0; m < nModules; m++) {
     const auto& moduleDigis = digi.front[fPars->modules[m].address];
-    LOG_IF(fatal, digiOffset[m] != offset) << "Digi offset mismatch";
+    if (digiOffset[m] != offset) {
+      L_(fatal) << "Module " << m << ": Digi offset mismatch: " << digiOffset[m] << " != " << offset;
+      std::abort();
+    }
     offset += moduleDigis.size();
   }
 
   // Back
   for (size_t m = 0; m < nModules; m++) {
     const auto& moduleDigis = digi.back[fPars->modules[m].address];
-    LOG_IF(fatal, digiOffset[nModules + m] != offset)
-      << "Module " << nModules + m << ": Digi offset mismatch: " << digiOffset[nModules + m] << " != " << offset;
+
+    if (digiOffset[nModules + m] != offset) {
+      L_(fatal) << "Module " << nModules + m << ": Digi offset mismatch: " << digiOffset[nModules + m]
+                << " != " << offset;
+      std::abort();
+    }
     offset += moduleDigis.size();
   }
 
-  LOG_IF(fatal, offset != digiOffset[2 * nModules]) << "Digi offset mismatch";
+  if (offset != digiOffset[2 * nModules]) {
+    L_(fatal) << "Digi offset mismatch: " << digiOffset[2 * nModules] << " != " << offset;
+    std::abort();
+  }
 }
 
 void sts::HitfinderChain::EnsureDigisSorted()
@@ -452,12 +461,15 @@ void sts::HitfinderChain::EnsureDigisSorted()
       }
 
       isSorted = false;
-      LOG(error) << "Module " << m << " not sorted: " << digi1.ToString() << " " << digi2.ToString();
+      L_(error) << "Module " << m << " not sorted: " << digi1.ToString() << " " << digi2.ToString();
       break;
     }
   }
 
-  LOG_IF(fatal, !isSorted) << "Digis are not sorted";
+  if (!isSorted) {
+    L_(fatal) << "Digis are not sorted";
+    std::abort();
+  }
 }
 
 void sts::HitfinderChain::EnsureChannelOffsets(span<u32> channelOffsetsByModule)
@@ -476,7 +488,10 @@ void sts::HitfinderChain::EnsureChannelOffsets(span<u32> channelOffsetsByModule)
 
     if (nDigis == 0) continue;
 
-    LOG_IF(fatal, channelOffsets[0] != 0) << "Module " << m << ": First channel offset is not 0";
+    if (channelOffsets[0] != 0) {
+      L_(fatal) << "Module " << m << ": First channel offset is not 0";
+      std::abort();
+    }
 
     int chan = digis[0].GetChannel();
     for (int i = 0; i < nDigis; i++) {
@@ -493,9 +508,11 @@ void sts::HitfinderChain::EnsureChannelOffsets(span<u32> channelOffsetsByModule)
     }
 
     for (int i = 0; i < nChannels; i++) {
-      LOG_IF(fatal, channelOffsets[i] != expectedChannelOffsets[i])
-        << "Module " << m << ": Channel offset for channel " << i << " is " << channelOffsets[i] << " but should be "
-        << expectedChannelOffsets[i];
+      if (channelOffsets[i] != expectedChannelOffsets[i]) {
+        L_(fatal) << "Module " << m << ": Channel offset for channel " << i << " is " << channelOffsets[i]
+                  << " but should be " << expectedChannelOffsets[i];
+        std::abort();
+      }
     }
   }
 }
@@ -505,14 +522,18 @@ void sts::HitfinderChain::EnsureClustersSane(span<GpuClusterIdx> hClusterIdx, sp
   for (size_t m = 0; m < 2 * fPars->modules.size(); m++) {
     int nClusters = hNClusters[m];
 
-    LOG(trace) << "Module " << m << " has " << nClusters << " clusters of " << fHitfinder.maxClustersPerModule;
+    L_(trace) << "Module " << m << " has " << nClusters << " clusters of " << fHitfinder.maxClustersPerModule;
 
     if (nClusters == 0) continue;
 
-    if (nClusters < 0) { LOG(fatal) << "Module " << m << " has negative number of clusters " << nClusters; }
+    if (nClusters < 0) {
+      L_(fatal) << "Module " << m << " has negative number of clusters " << nClusters;
+      std::abort();
+    }
     if (size_t(nClusters) > fHitfinder.maxClustersPerModule) {
-      LOG(fatal) << "Module " << m << " has " << nClusters << " clusters, but only " << fHitfinder.maxClustersPerModule
-                 << " are allowed";
+      L_(fatal) << "Module " << m << " has " << nClusters << " clusters, but only " << fHitfinder.maxClustersPerModule
+                << " are allowed";
+      std::abort();
     }
 
     auto* clusterIdx = &hClusterIdx[m * fHitfinder.maxClustersPerModule];
@@ -521,14 +542,16 @@ void sts::HitfinderChain::EnsureClustersSane(span<GpuClusterIdx> hClusterIdx, sp
       auto& cidx = clusterIdx[i];
 
       if (cidx.fIdx < 0 || size_t(cidx.fIdx) >= fHitfinder.maxClustersPerModule) {
-        LOG(fatal) << "Cluster " << i << " of module " << m << " has invalid index " << cidx.fIdx;
+        L_(fatal) << "Cluster " << i << " of module " << m << " has invalid index " << cidx.fIdx;
+        std::abort();
       }
 
       if (cidx.fTime == 0xFFFFFFFF) {
-        LOG(fatal) << "Cluster " << i << " of module " << m << " has invalid time " << cidx.fTime;
+        L_(fatal) << "Cluster " << i << " of module " << m << " has invalid time " << cidx.fTime;
+        std::abort();
       }
     }
   }
 
-  LOG(trace) << "Clusters ok";
+  L_(trace) << "Clusters ok";
 }
diff --git a/algo/detectors/sts/StsUnpackChain.cxx b/algo/detectors/sts/StsUnpackChain.cxx
index 3f3a7a55f9f28becfe6a99ce9fede8797c350433..506813298585ec326a44fb0bad19e864ce718a1f 100644
--- a/algo/detectors/sts/StsUnpackChain.cxx
+++ b/algo/detectors/sts/StsUnpackChain.cxx
@@ -7,10 +7,10 @@
 
 #include <Timeslice.hpp>
 
-#include <fairlogger/Logger.h>
-
 #include <xpu/host.h>
 
+#include "log.hpp"
+
 using namespace cbm::algo;
 
 void sts::UnpackChain::Init(StsReadoutConfig config)
@@ -43,7 +43,7 @@ void sts::UnpackChain::Init(StsReadoutConfig config)
       par->fElinkParams.push_back(elinkPar);
     }
     fAlgoSts[equip].SetParams(std::move(par));
-    LOG(debug) << "--- Configured STS equipment " << equip << " with " << numElinks << " elinks";
+    L_(debug) << "--- Configured STS equipment " << equip << " with " << numElinks << " elinks";
   }  //# equipments
 }
 
@@ -88,11 +88,10 @@ std::vector<CbmStsDigi> sts::UnpackChain::Run(const fles::Timeslice& timeslice)
         xpu::t_add_bytes(msDescriptor.size);
 
         auto result = (algoIt->second)(msContent, msDescriptor, timeslice.start_time());
-        LOG(debug1) << "STS Unpacker: Component " << comp << ", microslice " << mslice << ", digis "
-                    << result.first.size() << ", errors " << result.second.fNumNonHitOrTsbMessage << " | "
-                    << result.second.fNumErrElinkOutOfRange << " | " << result.second.fNumErrInvalidFirstMessage
-                    << " | " << result.second.fNumErrInvalidMsSize << " | " << result.second.fNumErrTimestampOverflow
-                    << " | ";
+        L_(trace) << "STS Unpacker: Component " << comp << ", microslice " << mslice << ", digis "
+                  << result.first.size() << ", errors " << result.second.fNumNonHitOrTsbMessage << " | "
+                  << result.second.fNumErrElinkOutOfRange << " | " << result.second.fNumErrInvalidFirstMessage << " | "
+                  << result.second.fNumErrInvalidMsSize << " | " << result.second.fNumErrTimestampOverflow << " | ";
 
         // numPulsers += result.second.fNumPulserHits;
         numDigisInComp += result.first.size();
@@ -103,6 +102,6 @@ std::vector<CbmStsDigi> sts::UnpackChain::Run(const fles::Timeslice& timeslice)
 
   }  // component
 
-  LOG(info) << "Timeslice contains " << digis.size() << " STS digis (discarded " << numPulsers << " pulser hits)";
+  L_(info) << "Timeslice contains " << digis.size() << " STS digis (discarded " << numPulsers << " pulser hits)";
   return digis;
 }
diff --git a/algo/detectors/tof/TofReadoutConfig.cxx b/algo/detectors/tof/TofReadoutConfig.cxx
index 76617736b4c9cbb89ef73fc184d46e9d3bccfa95..5ce129e3b1f23a126871131c14bd58b541e9be3f 100644
--- a/algo/detectors/tof/TofReadoutConfig.cxx
+++ b/algo/detectors/tof/TofReadoutConfig.cxx
@@ -6,11 +6,10 @@
 
 #include "CbmTofAddress.h"
 
-#include <Logger.h>
-
 #include <bitset>
 #include <iomanip>
 
+#include "AlgoFairloggerCompat.h"
 #include "gDpbMessv100.h"
 
 using namespace std;
diff --git a/algo/evselector/DigiEventSelector.cxx b/algo/evselector/DigiEventSelector.cxx
index 813790422b405e4cc0247eb816f7e66ca79b7088..c2f5a67b60d56508193e7f49a4ea06a93715f6dc 100644
--- a/algo/evselector/DigiEventSelector.cxx
+++ b/algo/evselector/DigiEventSelector.cxx
@@ -6,14 +6,14 @@
 
 #include "CbmStsDigi.h"
 
-#include <Logger.h>
-
 #include "TofConfig.h"
 
 #include <iterator>
 #include <map>
 #include <unordered_set>
 
+#include "AlgoFairloggerCompat.h"
+
 
 namespace cbm::algo
 {
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index c87975a7cee780746c31e3a8461b1903ea586033..bc42367af62f71803f7cc66639e2615759869a0b 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -6,6 +6,7 @@
 #include <xpu/host.h>
 
 #include "config/Yaml.h"
+#include "log.hpp"
 #include "util/TimingsFormat.h"
 
 using namespace cbm::algo;
@@ -23,7 +24,7 @@ void Reco::Init(const Options& opts)
   fStsHitFinder.SetContext(&fContext);
 
   xpu::device_prop props {xpu::device::active()};
-  LOG(info) << "Running CBM Reco on Device " << props.name();
+  L_(info) << "Running CBM Reco on Device " << props.name();
 
   // Reco Params
   fs::path recoParamsPath = opts.ParamsDir() / "RecoParams.yaml";
@@ -58,9 +59,7 @@ void Reco::Run(const fles::Timeslice& ts)
 
   xpu::push_timer(fmt::format("TS {}", ts.index()));
 
-  LOG(info) << fair::Logger::startColor(fair::Logger::Color::fgRed) << ">>> Processing TS " << ts.index()
-            << fair::Logger::endColor();
-
+  L_(info) << ">>> Processing TS " << ts.index();
   xpu::set<cbm::algo::Params>(Params());
 
   std::vector<CbmStsDigi> digis;
@@ -84,20 +83,20 @@ void Reco::Finalize()
   // Pop timer that was started in Init()
   xpu::timings t = xpu::pop_timer();
   if (Opts().CollectKernelTimes()) {
-    LOG(info) << MakeReportSubtimers("Run Summary", fTimesliceTimesAcc) << "\n" << MakeReportSummary("Total", t);
+    L_(info) << MakeReportSubtimers("Run Summary", fTimesliceTimesAcc) << "\n" << MakeReportSummary("Total", t);
   }
   else {
-    LOG(info) << "Total Processing time (Wall): " << t.wall() << " ms";
+    L_(info) << "Total Processing time (Wall): " << t.wall() << " ms";
   }
 }
 
 void Reco::PrintTimings(xpu::timings& timings)
 {
   if (Opts().CollectKernelTimes()) {
-    LOG(info) << MakeReportSubtimers("TS timings", timings) << "\n" << MakeReportSummary("Total", timings);
+    L_(info) << MakeReportSubtimers("TS timings", timings) << "\n" << MakeReportSummary("Total", timings);
     fTimesliceTimesAcc.merge(timings);
   }
   else {
-    LOG(info) << "TS Processing time (Wall): " << timings.wall() << " ms";
+    L_(info) << "TS Processing time (Wall): " << timings.wall() << " ms";
   }
 }
diff --git a/algo/log/AlgoFairloggerCompat.h b/algo/log/AlgoFairloggerCompat.h
new file mode 100644
index 0000000000000000000000000000000000000000..67deb7fed5f1da33bea65aae4c1a6059a057ce5b
--- /dev/null
+++ b/algo/log/AlgoFairloggerCompat.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Felix Weiglhofer [committer] */
+#ifndef CBM_ALGO_BASE_COMPAT_ONLINEDATALOG_H
+#define CBM_ALGO_BASE_COMPAT_ONLINEDATALOG_H
+
+#ifndef NO_ROOT
+
+#include <fairlogger/Logger.h>
+
+#else  // defined NO_ROOT
+
+#include <log.hpp>
+
+static constexpr severity_level warn = severity_level::warning;
+#define LOG(level) L_(level)
+
+#endif
+
+#endif  // CBM_ALGO_BASE_COMPAT_ONLINEDATALOG_H
diff --git a/algo/log/CMakeLists.txt b/algo/log/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d99747ba2fb7c5b9d931379d49199f4fcb3223f6
--- /dev/null
+++ b/algo/log/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_library(OnlineDataLog INTERFACE)
+target_include_directories(OnlineDataLog INTERFACE .)
+
+install(FILES AlgoFairloggerCompat.h DESTINATION include)
diff --git a/core/data/CMakeLists.txt b/core/data/CMakeLists.txt
index 2b3bd5a6c0975ac4a774b124085e487028ffaedc..4d1ee4e36b4543414e1e5a6950a5cb1c2e01dae4 100644
--- a/core/data/CMakeLists.txt
+++ b/core/data/CMakeLists.txt
@@ -133,6 +133,7 @@ tof/CbmTofDigiData.h psd/CbmPsdDigiData.h)
 set(LIBRARY_NAME CbmData)
 set(LINKDEF ${LIBRARY_NAME}LinkDef.h)
 set(PUBLIC_DEPENDENCIES
+  OnlineDataLog
   FairRoot::Base
   ROOT::Core
   ROOT::Matrix
diff --git a/core/data/much/CbmMuchAddress.cxx b/core/data/much/CbmMuchAddress.cxx
index bb9a2ff7109c349607a6cbefd9ce916dad96cb49..9f0bb00cb9bc3cf8bfe001b8f5b568b2997c232d 100644
--- a/core/data/much/CbmMuchAddress.cxx
+++ b/core/data/much/CbmMuchAddress.cxx
@@ -6,11 +6,11 @@
 
 #include "CbmDefs.h"  // for kMuch
 
-#include <Logger.h>  // for Logger, LOG
-
 #include <iomanip>  // for setw, __iom_t6
 #include <ios>      // for right
 
+#include "AlgoFairloggerCompat.h"  // for Logger, LOG
+
 
 // -----    Definition of the address field   -------------------------------
 const int32_t CbmMuchAddress::fgkBits[] = {fgkSystemBits,  // system = kMUCH
diff --git a/core/data/sts/CbmStsAddress.cxx b/core/data/sts/CbmStsAddress.cxx
index 7af19304f5f4ca8c9c88f8815244287b58e0f725..a9847954035561f06df637f3023634df6e4d9dbe 100644
--- a/core/data/sts/CbmStsAddress.cxx
+++ b/core/data/sts/CbmStsAddress.cxx
@@ -11,11 +11,11 @@
 
 #include "CbmDefs.h"  // for kSts
 
-#include <Logger.h>  // for Logger, LOG
-
 #include <cassert>  // for assert
 #include <sstream>  // for operator<<, basic_ostream, stringstream
 
+#include "AlgoFairloggerCompat.h"  // for Logger, LOG
+
 // -----   Construct address from element Ids   ------------------------------
 int32_t CbmStsAddress::GetAddress(uint32_t unit, uint32_t ladder, uint32_t halfladder, uint32_t module, uint32_t sensor,
                                   uint32_t side, uint32_t version)
diff --git a/core/data/trd/CbmTrdDigi.cxx b/core/data/trd/CbmTrdDigi.cxx
index 442d0f18611606120eb505274d66700b00206eae..88f5b183d591d1889f75af62f00f5c0b096f3793 100644
--- a/core/data/trd/CbmTrdDigi.cxx
+++ b/core/data/trd/CbmTrdDigi.cxx
@@ -6,8 +6,6 @@
 
 #include "CbmTrdAddress.h"  // for CbmTrdAddress
 
-#include <Logger.h>  // for LOG
-
 #include <iomanip>  // for operator<<, setprecision, setw
 #include <sstream>  // for operator<<, basic_ostream, stringstream
 #include <string>   // for char_traits
@@ -15,6 +13,8 @@
 
 #include <cmath>
 
+#include "AlgoFairloggerCompat.h"  // for LOG
+
 #ifdef NO_ROOT
 // Coming from Rtypes.h in ROOT mode
 #define BIT(n) (1ULL << (n))
diff --git a/core/data/trd/CbmTrdRawMessageSpadic.cxx b/core/data/trd/CbmTrdRawMessageSpadic.cxx
index 43388b0af6072bacd5d1eaa4ff76d417fbb1dda2..113e0605dc1fd6254b4af9315db80a2ddf603fb5 100644
--- a/core/data/trd/CbmTrdRawMessageSpadic.cxx
+++ b/core/data/trd/CbmTrdRawMessageSpadic.cxx
@@ -4,12 +4,12 @@
 
 #include "CbmTrdRawMessageSpadic.h"
 
-#include <Logger.h>  // for LOG
-
 #include <algorithm>  // for max_element
 #include <cstdint>
 #include <stdexcept>  // for range_error
 
+#include "AlgoFairloggerCompat.h"  // for LOG
+
 // -------  Default Constructor  ----------------
 CbmTrdRawMessageSpadic::CbmTrdRawMessageSpadic()
   : fChannelID()
diff --git a/reco/app/cbmreco/main.cxx b/reco/app/cbmreco/main.cxx
index 543f576c8c5f7c90b805e2adaca8a713e81b8f07..1aff967930164a6d02caadf161b695c6fcc33a86 100644
--- a/reco/app/cbmreco/main.cxx
+++ b/reco/app/cbmreco/main.cxx
@@ -3,8 +3,7 @@
    Authors: Felix Weiglhofer [committer] */
 #include <TimesliceAutoSource.hpp>
 
-#include <fairlogger/Logger.h>
-
+#include <log.hpp>
 #include <sstream>
 
 #include <xpu/host.h>
@@ -18,21 +17,15 @@ int main(int argc, char** argv)
 {
   Options opts {argc, argv};
 
-  // Logger
-  fair::Logger::SetConsoleSeverity(std::string {opts.LogLevel()});
-  if (fair::Logger::GetConsoleSeverity() == fair::Severity::trace) {
-    fair::Logger::SetVerbosity(fair::Verbosity::veryhigh);
-  }
-  fair::Logger::OnFatal([] { std::abort(); });
-  fair::Logger::SetConsoleColor(true);
+  logging::add_console(opts.LogLevel());
 
   // XPU
   xpu::settings settings;
   settings.profile = opts.CollectKernelTimes();
   settings.device  = opts.Device();
-  if (fair::Logger::GetConsoleSeverity() == fair::Severity::trace) {
+  if (opts.LogLevel() == trace) {
     settings.verbose      = true;
-    settings.logging_sink = [](std::string_view msg) { LOG(trace) << msg; };
+    settings.logging_sink = [](std::string_view msg) { L_(trace) << msg; };
   }
   xpu::initialize(settings);
   xpu::preload<GPUReco>();
@@ -42,7 +35,7 @@ int main(int argc, char** argv)
   for (int i = 0; i < argc; i++) {
     ss << argv[i] << " ";
   }
-  LOG(info) << ss.str();
+  L_(info) << ss.str();
 
   Reco reco;
   reco.Init(opts);