diff --git a/CMakeLists.txt b/CMakeLists.txt
index edb6c4f9740ebf5f5958aaf16fd6b525f8bfea6c..498df5aac7578066f286834fe18671f9a7e3ecdf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -90,11 +90,18 @@ target_compile_features(Vivy PRIVATE
 
 # More options and warnings
 target_compile_options(Vivy PRIVATE
-    -Wall -Wextra -Wshadow -pedantic
-    -Wcast-align -Wconversion -Wsign-conversion -Wunused-variable
-    -Wmisleading-indentation -Wnull-dereference -Wdouble-promotion
+    -Wall -Wextra -Wpedantic
+    -Wshadow
+    -Wcast-align
+    -Wconversion
+    -Wsign-conversion
+    -Wunused-variable
+    -Wmisleading-indentation
+    -Wnull-dereference
+    -Wdouble-promotion
     -Wformat=2
-    -Woverloaded-virtual -Wnon-virtual-dtor
+    -Woverloaded-virtual
+    -Wnon-virtual-dtor
     -Wignored-qualifiers
 
     -fopenmp
@@ -117,8 +124,11 @@ if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
 
         # Disable some things because we want C++20 and don't control some Qt
         # generated files...
-        -Wno-c++98-compat -Wno-c++98-c++11-c++14-c++17-compat-pedantic
+        -Wno-c++98-compat
         -Wno-c++98-compat-pedantic
+        -Wno-c++98-c++11-c++14-c++17-compat-pedantic
+        -Wno-c++20-compat
+
         -Wno-extra-semi-stmt
         -Wno-redundant-parens
         -Wno-padded
diff --git a/PreCompiledHeaders.cmake b/PreCompiledHeaders.cmake
index dce8f03b4c87f3ac598c970fe04e5e173e476c6e..f09e72ab569a20b3f841dd0b73132b36f301481f 100644
--- a/PreCompiledHeaders.cmake
+++ b/PreCompiledHeaders.cmake
@@ -23,6 +23,7 @@ set(STL_INC
     <climits>
     <ctype.h>
     <mutex>
+    <iomanip>
 )
 
 set(EXT_INC PRIVATE
diff --git a/src/Lib/AbstractDocument.hh b/src/Lib/AbstractDocument.hh
index dfec447f3bdbc696e313bc6985b487ea94ad1a8a..c39fb5661a9cdcc3bd841950fe18c011f9214972 100644
--- a/src/Lib/AbstractDocument.hh
+++ b/src/Lib/AbstractDocument.hh
@@ -4,8 +4,10 @@
 #error "This is a C++ header"
 #endif
 
+#include "../VivyApplication.hh"
 #include "Utils.hh"
 #include "Uuid.hh"
+#include "Log.hh"
 
 namespace Vivy
 {
@@ -13,6 +15,9 @@ class AbstractDocument : public QObject {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(AbstractDocument)
 
+protected:
+    VIVY_APP_LOGGABLE_OBJECT(AbstractDocument, logger)
+
     void copyOrRenameWith(const QFileInfo &newFile, auto action, auto success)
     {
         const QFileInfo oldFile(getName());
@@ -21,7 +26,7 @@ class AbstractDocument : public QObject {
         QDir dirOp;
         const QString newAbsDirPath = newFile.dir().absolutePath();
         if (!dirOp.exists(newAbsDirPath)) {
-            qInfo() << "Create folder " << newAbsDirPath;
+            logInfo() << "Create folder " << VIVY_LOG_QUOTED(newAbsDirPath);
             dirOp.mkpath(newAbsDirPath);
         }
 
@@ -30,7 +35,7 @@ class AbstractDocument : public QObject {
         }
 
         if (newFile.exists()) {
-            qWarning() << "Deleting the already existing" << newFile;
+            logWarning() << "Deleting the already existing file " << VIVY_LOG_QUOTED(newFile);
             if (!dirOp.remove(newFile.absoluteFilePath()))
                 throw std::runtime_error("Failed to remove " +
                                          newFile.absoluteFilePath().toStdString());
diff --git a/src/Lib/AbstractMediaContext.hh b/src/Lib/AbstractMediaContext.hh
index f4a09ed6c040a96dcbea04d0b71fe2491430f727..a15609de50345b89f238deae99cbb8722de14456 100644
--- a/src/Lib/AbstractMediaContext.hh
+++ b/src/Lib/AbstractMediaContext.hh
@@ -10,10 +10,11 @@ extern "C" {
 #include <libavformat/avformat.h>
 #include <libswresample/swresample.h>
 #include <libavcodec/avfft.h>
-#include <memory.h>
 }
 
+#include "../VivyApplication.hh"
 #include "Utils.hh"
+#include "Log.hh"
 
 namespace Vivy
 {
@@ -53,6 +54,7 @@ public:
     static inline constexpr AVMediaType avMediaType = static_cast<AVMediaType>(AVMEDIA_TYPE);
 
 protected:
+    VIVY_APP_LOGGABLE_OBJECT(AbstractMediaStream, logger)
     using Super = AbstractMediaStream<AVMEDIA_TYPE>;
 
 protected:
@@ -78,10 +80,10 @@ protected:
         if (avcodec_open2(codecContext.get(), codec, nullptr) < 0)
             throw std::runtime_error("failed to open audio decoder for a stream");
 
-        qDebug() << "[Stream] Codec" << codec->name << "id:" << codecId;
-        qDebug() << "[Stream] sample rate:" << codecParams->sample_rate;
-        qDebug() << "[Stream] bit rate:   " << codecParams->bit_rate;
-        qDebug() << "[Stream] channels:   " << codecParams->channels;
+        VIVY_LOG_CTOR() << "Codec: " << VIVY_LOG_QUOTED(codec->name) << ", id: " << codecId;
+        VIVY_LOG_CTOR() << "Sample rate: " << codecParams->sample_rate;
+        VIVY_LOG_CTOR() << "Bit rate: " << codecParams->bit_rate;
+        VIVY_LOG_CTOR() << "Channels: " << codecParams->channels;
     }
 
 public:
@@ -121,6 +123,7 @@ public:
     using StreamWeakPtr                             = std::weak_ptr<Stream>;
 
 protected:
+    VIVY_APP_LOGGABLE_OBJECT(AbstractMediaContext, logger)
     using Super = AbstractMediaContext<Stream>;
 
 public:
@@ -160,10 +163,10 @@ public:
                                                  -1, // We don't want related streams
                                                  nullptr, 0);
         if (defaultStreamIndex < 0)
-            qCritical() << "Could not find the best stream";
+            logError() << "Could not find the best stream";
 
-        qDebug() << "Opened context for" << path << "with duration" << formatPtr->duration
-                 << "and default stream index" << defaultStreamIndex;
+        logDebug() << "Opened context for " << VIVY_LOG_QUOTED(path) << " with duration "
+                   << formatPtr->duration << " and default stream index " << defaultStreamIndex;
     }
 
     virtual ~AbstractMediaContext() {}
diff --git a/src/Lib/Ass/AssFactory.cc b/src/Lib/Ass/AssFactory.cc
index 08ca4b7e08e93d57f0ca273adb463fb27b2032cf..93f8a92dddfeaee1929db77d0ca3b74dcf58dab2 100644
--- a/src/Lib/Ass/AssFactory.cc
+++ b/src/Lib/Ass/AssFactory.cc
@@ -23,9 +23,10 @@ AssFactory::initFromStorage() noexcept
         // Dectect sections
         else if (line.startsWith("[") && line.endsWith("]")) {
             currentSection = line.mid(1, line.size() - 2);
-            qDebug() << "Parsing section" << currentSection;
+            logDebug() << "Parsing section " << VIVY_LOG_QUOTED(currentSection);
             if (!validSections.contains(currentSection)) {
-                qWarning() << "The current section" << currentSection << "is invalid, ignoring it";
+                logWarning() << "The current section " << VIVY_LOG_QUOTED(currentSection)
+                             << " is invalid, ignoring it";
                 currentSection = "";
             }
         }
@@ -37,12 +38,12 @@ AssFactory::initFromStorage() noexcept
 
             // Easy way to see if the line was invalid
             if (separatorIndex < 0)
-                qWarning() << "Invalid line #" << lineIndex << ":" << line;
+                logWarning() << "Invalid line #" << lineIndex << ": " << line;
 
             // Script's info
             else if (currentSection == sectionScriptInfo) {
                 assInfo.insert(line.mid(0, separatorIndex), line.mid(baseValueIndex));
-                qDebug() << "Got line #" << lineIndex << ":" << line;
+                logDebug() << "Got line #" << lineIndex << ": " << line;
             }
 
             // Skip the headers and the comment lines
@@ -65,10 +66,10 @@ AssFactory::initFromStorage() noexcept
         for (const auto &assLine : eventsContent)
             assLines.push_back(std::make_shared<Line>(this, assLine));
     } catch (const std::runtime_error &e) {
-        qCritical() << "Failed to create ASS style or events with error:" << e.what();
+        logError() << "Failed to create ASS style or events with error: " << e.what();
     }
 
-    qDebug() << "Got" << assLines.size() << "ASS dialog lines";
+    logDebug() << "Got " << assLines.size() << " ASS dialog lines";
 
     return true;
 }
@@ -77,7 +78,7 @@ bool
 AssFactory::checkValidity() const noexcept
 {
     if (assInfo.isEmpty()) {
-        qCritical() << "Empty info section";
+        logError() << "Empty info section";
         return false;
     }
 
@@ -87,7 +88,7 @@ AssFactory::checkValidity() const noexcept
     while (it != end) {
         bool ok = false;
         if (intTypeFields.contains(it.key()) && (static_cast<void>(it.value().toInt(&ok)), !ok)) {
-            qCritical() << it.key() << "is not an integer:" << it.value();
+            logError() << it.key() << " is not an integer: " << it.value();
             return false;
         }
         ++it;
@@ -97,8 +98,8 @@ AssFactory::checkValidity() const noexcept
     for (const auto &fixedValues : checkedValues) {
         if (const auto &first = fixedValues.first;
             assInfo.contains(first) && assInfo[first] != fixedValues.second) {
-            qCritical() << "Invalid" << first << "as it should be equal to" << fixedValues.second
-                        << "but was" << assInfo[first];
+            logError() << "Invalid " << first << " as it should be equal to " << fixedValues.second
+                       << " but was " << assInfo[first];
             return false;
         }
     }
diff --git a/src/Lib/Ass/AssFactory.hh b/src/Lib/Ass/AssFactory.hh
index b220c0f1995a4c633664cb42554505080b494452..98c8f3fea31fd3513f238b77c181f61f69b9e0e1 100644
--- a/src/Lib/Ass/AssFactory.hh
+++ b/src/Lib/Ass/AssFactory.hh
@@ -1,6 +1,7 @@
-#ifndef VIVY_ASS_FACTORY_H
-#define VIVY_ASS_FACTORY_H
+#pragma once
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "../Utils.hh"
 #include "Style.hh"
 #include "Line.hh"
@@ -13,6 +14,7 @@ namespace Vivy::Ass
 {
 class AssFactory final {
     VIVY_UNMOVABLE_OBJECT(AssFactory)
+    VIVY_APP_LOGGABLE_OBJECT(AssFactory, logger)
 
 public:
     enum class Section { ScriptInfo = 1, Styles = 2, Events = 3 };
@@ -55,7 +57,4 @@ public:
     void getStyles(QVector<StylePtr> &) const noexcept;
     void getLines(QVector<LinePtr> &) const noexcept;
 };
-
 }
-
-#endif // VIVY_ASS_FACTORY_H
diff --git a/src/Lib/Ass/Line.cc b/src/Lib/Ass/Line.cc
index fe0b257ab4585b7d0d962f2577177238cd328db9..046e313168aeaaf52a917bafc7e0a00d0f7ea718 100644
--- a/src/Lib/Ass/Line.cc
+++ b/src/Lib/Ass/Line.cc
@@ -65,7 +65,8 @@ Line::initSylFromString(const QString &line) noexcept
     // Matches syllabes like: `{\toto}{\alpha&HFF}content`
     QRegularExpression re("((?:{[^}]*})+[^{]*)");
     if (!re.isValid())
-        qFatal("The regex '%s' is not valid...", re.pattern().toStdString().c_str());
+        logFatal() << "The regex " << VIVY_LOG_QUOTED(re.pattern().toStdString().c_str())
+                   << " is not valid...";
 
     bool once = false;
     try {
@@ -77,9 +78,8 @@ Line::initSylFromString(const QString &line) noexcept
             once |= true;
         }
     } catch (const std::runtime_error &e) {
-        qCritical() << "Failed to init syllabes with line:" << line;
-        qCritical() << "Error was:" << e.what();
-        qCritical() << "Fallback to all line is one syllabe";
+        qCritical() << "Failed to init syllabes with line: " << VIVY_LOG_QUOTED(line)
+                    << ". Error was: " << e.what() << ". Fallback to all line is one syllabe";
         once = false;
     }
 
diff --git a/src/Lib/Ass/Line.hh b/src/Lib/Ass/Line.hh
index 15984da129990d512c3cdc388b67143a4d808d08..11f91de64ed4c0c204b096080b16ec453f97eb63 100644
--- a/src/Lib/Ass/Line.hh
+++ b/src/Lib/Ass/Line.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "Syl.hh"
 #include "StyleProperties.hh"
 #include "Style.hh"
@@ -9,7 +11,8 @@ namespace Vivy::Ass
 class AssFactory;
 
 class Line final {
-private:
+    VIVY_APP_LOGGABLE_OBJECT(Line, logger)
+
     quint64 start{ 0 };
     quint64 end{ 0 };
     int layer{ 0 };
diff --git a/src/Lib/Ass/Style.cc b/src/Lib/Ass/Style.cc
index 1a6ce05cc1658ca8169c81a338917cb37f535ba8..2f80c9d1db58c37be84001ba4bd3ca5b3b1072ed 100644
--- a/src/Lib/Ass/Style.cc
+++ b/src/Lib/Ass/Style.cc
@@ -102,7 +102,7 @@ Style::Style(const QString &styleString)
         Utils::decodeLineToInteger(content[StyleIndex::Encoding], "Encoding is not an integer");
 
     if (encoding != 1)
-        qWarning() << "Encoding is not '1' in the ASS Style";
+        logWarning() << "Encoding is not '1' in the ASS Style";
 }
 
 QString
@@ -127,13 +127,8 @@ Color::fromString(const QString &colorString) noexcept
         startIndex++;
 
     // A valid string color is like 'AARRGGBB' for now (skipped 'aH')
-    if (colorString.size() - startIndex != 8) {
-        qCritical()
-            << "Invalid color string: size - index_start =" << (colorString.size() - startIndex)
-            << "| string =" << colorString.mid(startIndex);
-        qCritical() << "Found an invalid color string:" << colorString;
+    if (colorString.size() - startIndex != 8)
         return Color::defaultValue;
-    }
 
     bool ok_alpha = false;
     bool ok_red   = false;
@@ -144,10 +139,8 @@ Color::fromString(const QString &colorString) noexcept
     int green     = colorString.mid(startIndex + 4, 2).toInt(&ok_green, 16);
     int blue      = colorString.mid(startIndex + 6, 2).toInt(&ok_blue, 16);
 
-    if (!(ok_alpha && ok_red && ok_green && ok_blue)) {
-        qCritical() << "Found an invalid color string:" << colorString;
+    if (!(ok_alpha && ok_red && ok_green && ok_blue))
         return Color::defaultValue;
-    }
 
     return QColor(red, green, blue, alpha);
 }
diff --git a/src/Lib/Ass/Style.hh b/src/Lib/Ass/Style.hh
index 696cfc79e4b082a14e8b3ad295e31455611f283c..c6c5f10b91924355bb23f33039a1a34b278a2e2d 100644
--- a/src/Lib/Ass/Style.hh
+++ b/src/Lib/Ass/Style.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "AssPrivate.hh"
 
 namespace Vivy::Ass
@@ -15,7 +17,8 @@ private:
 };
 
 class Style final {
-private:
+    VIVY_APP_LOGGABLE_OBJECT(Style, logger)
+
     QString styleName;
     QString fontName;
     int fontSize{};
diff --git a/src/Lib/Ass/Syl.cc b/src/Lib/Ass/Syl.cc
index bac0a4d0363e269ec1d984466a767881c53108a6..65d29a85cd80ce92f5a259edfed905f59f6440f1 100644
--- a/src/Lib/Ass/Syl.cc
+++ b/src/Lib/Ass/Syl.cc
@@ -1,8 +1,6 @@
 #include "Syl.hh"
 #include "Line.hh"
 
-#include <QRegularExpression>
-
 using namespace Vivy::Ass;
 
 Syl::Syl(Line *const line, const QString &lineString, ConstructMode mode) noexcept
@@ -23,9 +21,6 @@ quint64
 Syl::getDurationFromString(const QString &line) noexcept
 {
     QRegularExpression re("\\\\(?:k|K|ko|kf)(\\d+)");
-    if (!re.isValid())
-        qFatal("The regex '%s' is not valid...", re.pattern().toStdString().c_str());
-
     quint64 duration                   = 0;
     QRegularExpressionMatchIterator it = re.globalMatch(line);
 
diff --git a/src/Lib/Ass/Syl.hh b/src/Lib/Ass/Syl.hh
index 084e14e5f93ea11e001260bae8543aeef48c96d9..461746e90d6f8acf4027ddab62a3fcedf58bdb40 100644
--- a/src/Lib/Ass/Syl.hh
+++ b/src/Lib/Ass/Syl.hh
@@ -1,10 +1,6 @@
-#ifndef VIVY_SYL_H
-#define VIVY_SYL_H
+#pragma once
 
 #include "StyleProperties.hh"
-#include <QString>
-#include <QVector>
-#include <QtGlobal>
 
 namespace Vivy::Ass
 {
@@ -37,7 +33,4 @@ public:
 private:
     static quint64 getDurationFromString(const QString &) noexcept;
 };
-
 }
-
-#endif
diff --git a/src/Lib/Audio.cc b/src/Lib/Audio.cc
index 218763c297441fe96745decac1d076922f209f15..fdf79c0fc29e9d58165d701c452447412f6b9655 100644
--- a/src/Lib/Audio.cc
+++ b/src/Lib/Audio.cc
@@ -46,10 +46,10 @@ AudioStream::AudioStream(AVCodec *streamCodec, AVFormatContext *formatPtr, AVStr
 AudioStream::~AudioStream() noexcept
 {
     if (dataPtr) {
-        qDebug() << "Free data ptr";
+        VIVY_LOG_DTOR() << "Free data ptr for audio stream";
         free(dataPtr);
     }
-    qDebug() << "Delete stream object";
+    VIVY_LOG_DTOR() << "Delete audio stream object";
 }
 
 QJsonObject
@@ -68,7 +68,7 @@ AudioStream::decodeData()
 {
     if (isDecoded())
         throw std::logic_error("audio stream is already resampled");
-    qDebug() << "Launch decoding of stream" << streamIndexInContext;
+    logDebug() << "Launch decoding of stream " << streamIndexInContext;
 
     AVPacket packet;
     av_init_packet(&packet);
@@ -137,8 +137,8 @@ AudioStream::decodeData()
         av_packet_unref(&packet);
     }
 
-    qDebug() << "Decoding data finished for stream" << streamIndexInContext
-             << "dataPtr =" << dataPtr << "with dataSize =" << dataSize;
+    logDebug() << "Decoding data finished for stream " << streamIndexInContext << " with dataPtr "
+               << dataPtr << " and dataSize " << dataSize;
 }
 
 // Delete decoded data, clean up thing
diff --git a/src/Lib/CRTPStore.hh b/src/Lib/CRTPStore.hh
index 8324d9c606635812731708f754c7997bbe6c49e2..2aa6da2c7bcb34ae8ed039058049d944b82bf4cf 100644
--- a/src/Lib/CRTPStore.hh
+++ b/src/Lib/CRTPStore.hh
@@ -4,6 +4,8 @@
 #error "This is a C++ header"
 #endif
 
+#include "../VivyApplication.hh"
+#include "Log.hh"
 #include "Utils.hh"
 #include "Uuid.hh"
 
@@ -21,6 +23,8 @@ template <class Store, class Document> class CRTPStore {
     VIVY_UNMOVABLE_OBJECT(CRTPStore)
 
 protected:
+    VIVY_APP_LOGGABLE_OBJECT(CRTPStore, logger)
+
     std::map<Uuid, std::shared_ptr<Document>> documents{};
     uint newDocumentNumber{ 1 };
     static inline const QString newDocumentBaseName = QStringLiteral("Untitled ");
@@ -56,7 +60,7 @@ public:
         auto *self = static_cast<Store *>(this);
 
         if (!self->documents.contains(uuid)) {
-            qCritical() << "Couldn't find the document " << uuid;
+            logError() << "Couldn't find the document " << uuid;
             throw std::runtime_error("Can't find the document");
         }
 
diff --git a/src/Lib/Document/CRTPSubDocument.hh b/src/Lib/Document/CRTPSubDocument.hh
index c89f25c587539613cc5ad1f0c00305629f2eb984..7100a19ab30c7275d4cae86535dc0cd5c9f5c8c0 100644
--- a/src/Lib/Document/CRTPSubDocument.hh
+++ b/src/Lib/Document/CRTPSubDocument.hh
@@ -4,6 +4,8 @@
 #error "This is a C++ header"
 #endif
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "../Utils.hh"
 #include "../Audio.hh"
 #include "../Video.hh"
@@ -15,10 +17,13 @@
 namespace Vivy
 {
 // The Big CRTP class for all common things to all the subdocuments
-template <class CRTPSubDocumentType, class Document, class Context> class CRTPSubDocument {
+template <class CRTPSubDocumentType, class Document, class Context = void> class CRTPSubDocument {
+protected:
+    VIVY_APP_LOGGABLE_OBJECT(CRTPSubDocument, logger)
+    friend std::unique_ptr<Document>;
+
 public:
     using Type = CRTPSubDocumentType;
-    friend std::unique_ptr<Document>;
 
 protected:
     QString filePath;
@@ -35,14 +40,15 @@ protected:
 public:
     static std::unique_ptr<Document> fromFile(const QString &path) noexcept
     {
-        qDebug() << "Init document from file " << path;
-        auto ret      = std::unique_ptr<Document>(new Document());
+        auto ret = std::unique_ptr<Document>(new Document());
+        ret->logDebug() << "Init document from file " << VIVY_LOG_QUOTED(path);
         ret->filePath = path;
 
         try {
             ret->initFromPath(path); // May throw
         } catch (const std::runtime_error &e) {
-            qDebug().nospace() << "Failed to init document from file " << path << ": " << e.what();
+            ret->logDebug() << "Failed to init document from file " << VIVY_LOG_QUOTED(path) << ": "
+                            << e.what();
             ret.reset();
         }
 
@@ -118,7 +124,7 @@ public:
 };
 
 // ASS document
-class AssSubDocument final : public CRTPSubDocument<AssDocumentType, AssSubDocument, void> {
+class AssSubDocument final : public CRTPSubDocument<AssDocumentType, AssSubDocument> {
     static constexpr inline bool hasContext = false;
     const QStringList &suffixList           = Vivy::Utils::assFileSuffix;
 
diff --git a/src/Lib/Document/CRTPSubDocument/AudioSubDocument.cc b/src/Lib/Document/CRTPSubDocument/AudioSubDocument.cc
index c91c3752109af9df968d21d46d32ca433557c1b9..d10fb949520f043cfd3d489f3127ddf23f4229fe 100644
--- a/src/Lib/Document/CRTPSubDocument/AudioSubDocument.cc
+++ b/src/Lib/Document/CRTPSubDocument/AudioSubDocument.cc
@@ -8,10 +8,10 @@ void
 AudioSubDocument::initFromPath(const QString &path)
 {
     if (contextPtr)
-        qDebug() << "Replacing the audio contetx by a new one for file" << path;
+        logDebug() << "Replacing the audio contetx by a new one for file " << VIVY_LOG_QUOTED(path);
     contextPtr.reset(new AudioContext(path)); // May throw
 
-    qDebug() << "Audio OK for" << path;
+    logDebug() << "Audio OK for " << VIVY_LOG_QUOTED(path);
 }
 
 QString
diff --git a/src/Lib/Document/VivyDocument.cc b/src/Lib/Document/VivyDocument.cc
index 6749fcc3cce6e5acb44bf584ccb9c345903835cc..6cff0405c0ea45aa91b05ce900c7660d150acbbc 100644
--- a/src/Lib/Document/VivyDocument.cc
+++ b/src/Lib/Document/VivyDocument.cc
@@ -154,18 +154,18 @@ VivyDocument::loadSubDocument(const QString &subName) noexcept
     if (detectDocumentType(file, &ableType)) {
         switch (ableType) {
         case Capabilities::AudioAble:
-            qDebug() << "Auto-detect audio document for" << subName;
+            logDebug() << "Auto-detect audio document for " << subName;
             setAudioSubDocument(file.absoluteFilePath());
             return true;
 
         case Capabilities::VideoAble:
-            qDebug() << "Auto-detect video (and try audio) document for" << subName;
+            logDebug() << "Auto-detect video (and try audio) document for " << subName;
             setVideoSubDocument(file.absoluteFilePath());
             setAudioSubDocument(file.absoluteFilePath());
             return true;
 
         case Capabilities::AssAble:
-            qDebug() << "Auto-detect ASS document for" << subName;
+            logDebug() << "Auto-detect ASS document for " << subName;
             setAssSubDocument(file.absoluteFilePath());
             return true;
         }
@@ -180,24 +180,24 @@ VivyDocument::loadSubDocument(const QString &subName, VivyDocument::Capabilities
     QFileInfo file(subName);
     Capabilities ableType;
     if (!detectDocumentType(file, &ableType)) {
-        qCritical() << "Failed to detect type for file " << subName;
+        logError() << "Failed to detect type for file " << subName;
         return false;
     }
 
     if (ableType == Capabilities::AssAble && asType == Capabilities::AssAble) {
-        qDebug() << "Create an ass subDocument from " << subName;
+        logDebug() << "Create an ass subDocument from " << subName;
         setAssSubDocument(file.absoluteFilePath());
     }
 
     else if (ableType == Capabilities::VideoAble && asType == Capabilities::VideoAble) {
-        qDebug() << "Create a video subDocument from " << subName;
+        logDebug() << "Create a video subDocument from " << subName;
         setVideoSubDocument(file.absoluteFilePath());
     }
 
     else if (const bool requestAudio = (asType == Capabilities::AudioAble);
              (ableType == Capabilities::VideoAble && requestAudio) ||
              (ableType == Capabilities::AudioAble && requestAudio)) {
-        qDebug() << "Create an audio subDocument from " << subName;
+        logDebug() << "Create an audio subDocument from " << subName;
         setAudioSubDocument(file.absoluteFilePath());
     }
 
@@ -237,7 +237,7 @@ VivyDocument::copy(const QString &newName)
 
     // Compute new paths, the document is really on disk initially
     else {
-        qDebug() << "Renaming a real file";
+        logDebug() << "Renaming a real file to " << newName;
         copyWith(newPath, [=, this]() noexcept -> void {
             documentLocation = newPath.dir();
             documentName     = newPath.baseName();
@@ -259,7 +259,7 @@ VivyDocument::rename(const QString &newName)
 
     // Compute new paths, the document is really on disk initially
     else {
-        qDebug() << "Renaming a real file";
+        logDebug() << "Renaming a real file to " << newName;
         renameWith(newPath, [=, this]() noexcept -> void {
             documentLocation = newPath.dir();
             documentName     = newPath.baseName();
@@ -285,8 +285,8 @@ VivyDocument::saveMemoryFile(const QFileInfo &newPath)
     documentLocation = newPath.absoluteDir();
     name             = newPath.absoluteFilePath();
     documentOptions  = static_cast<Options>(documentOptions & (~MemoryDocumentCreation));
-    qDebug().nospace() << "Renaming a memory file => create it on disk with { " << documentLocation
-                       << ", " << documentName << " }";
+    logDebug() << "Renaming a memory file => create it on disk with { "
+               << documentLocation.absolutePath() << ", " << documentName << " }";
     save();
 }
 
@@ -336,7 +336,7 @@ VivyDocument::getDocumentCapabilitiesString() const noexcept
 void
 VivyDocument::setAudioSubDocument(const QString filename) noexcept
 {
-    qDebug() << "[Audio sub-doc] Trying to open file" << filename;
+    logDebug() << "Audio sub-doc: Trying to open file " << filename;
     QFileInfo fileInfo(filename);
     const QString baseName = fileInfo.baseName();
 
@@ -353,7 +353,7 @@ VivyDocument::setAudioSubDocument(const QString filename) noexcept
 void
 VivyDocument::setVideoSubDocument(const QString filename) noexcept
 {
-    qDebug() << "[Video sub-doc] Trying to open file" << filename;
+    logDebug() << "Video sub-doc: Trying to open file " << filename;
     QFileInfo fileInfo(filename);
     const QString baseName = fileInfo.baseName();
 
@@ -370,7 +370,7 @@ VivyDocument::setVideoSubDocument(const QString filename) noexcept
 void
 VivyDocument::setAssSubDocument(const QString filename) noexcept
 {
-    qDebug() << "[ASS sub-doc] Trying to open file" << filename;
+    logDebug() << "ASS sub-doc: Trying to open file " << filename;
     QFileInfo fileInfo(filename);
     const QString baseName = fileInfo.baseName();
 
diff --git a/src/Lib/Document/VivyDocumentStore.cc b/src/Lib/Document/VivyDocumentStore.cc
index 909d9e270367c90526b04c3c5c5f52d75d0fd93a..3d9618c86f192b0c79b8abce9a25f1fd34ddb042 100644
--- a/src/Lib/Document/VivyDocumentStore.cc
+++ b/src/Lib/Document/VivyDocumentStore.cc
@@ -15,10 +15,10 @@ VivyDocumentStore::newDocument(VivyDocument::Options opt)
     if (ret) {
         const Uuid uuid = ret->getUuid();
         documents[uuid] = ret;
-        qDebug() << "Create new document " << newDocName << "with uuid" << uuid;
+        logDebug() << "Create new document " << newDocName << " with uuid" << uuid;
         return ret;
     } else {
-        qDebug() << "Failed to create new document " << newDocName;
+        logDebug() << "Failed to create new document " << newDocName;
         throw std::runtime_error("Failed to create the document");
     }
 }
diff --git a/src/Lib/Log.cc b/src/Lib/Log.cc
index d23cabcc6e1e92fcb0ed3a30b254034a91cf8b6c..8cbebd7594a45800e6fc2b13f1b6f2a80a7506ea 100644
--- a/src/Lib/Log.cc
+++ b/src/Lib/Log.cc
@@ -1,10 +1,34 @@
 #include "Log.hh"
 
+// Vivy::LogLevel utility implementation
+namespace Vivy
+{
+const std::string_view
+LogLevel::toStdStringView(const LogLevel::Level lvl) noexcept
+{
+    return LevelsStringViews[static_cast<Array::size_type>(lvl)];
+}
+}
+
 // Vivy::LogSinkDispatcher and child classes implementation
 namespace Vivy
 {
 LogSinkDispatcher::~LogSinkDispatcher() noexcept {}
 
+const std::string_view
+StderrLogSinkDispatcher::trunkFileName(const char *fileName) noexcept
+{
+    using size_type = std::string_view::size_type;
+
+    static constexpr char basePrefix[]       = "/src/";
+    static constexpr size_type basePrefixLen = (sizeof(basePrefix) / sizeof(char)) - 1;
+
+    const std::string_view fileNameView{ fileName };
+    const size_type basePath = fileNameView.rfind(basePrefix);
+    return std::string_view{ fileNameView.data() + basePath + basePrefixLen,
+                             fileNameView.data() + fileNameView.size() };
+}
+
 StderrLogSinkDispatcher::StderrLogSinkDispatcher() noexcept
     : LogSinkDispatcher(std::string_view{ "stderr" })
 {
@@ -14,7 +38,10 @@ void
 StderrLogSinkDispatcher::handleLogMessage(const std::string_view category,
                                           const LogMessage &msg) noexcept
 {
-    std::cerr << '[' << category << "] " << msg.getTextBuffer() << '\n';
+    std::cerr << "#(" << trunkFileName(msg.getHeader().fileName) << " +"
+              << msg.getHeader().lineNumberInFile << " | "
+              << LogLevel::toStdStringView(msg.getHeader().severity) << " -> " << category << ")\t"
+              << msg.getTextBuffer() << '\n';
 }
 }
 
@@ -59,6 +86,12 @@ LogSink::registerLogDispatcher(std::shared_ptr<LogSinkDispatcher> dispatcher) no
 {
     logDispatchers.push_back(dispatcher);
 }
+
+void
+LogSink::registerLogger(std::shared_ptr<Logger> ptr) noexcept
+{
+    loggers.push_back(ptr);
+}
 }
 
 // Vivy::Logger implementation
@@ -72,7 +105,7 @@ Logger::sendLogMessage(LogMessage &&msg) const noexcept
 
 LogMessage
 Logger::logEvent(const char *fileName, const char *functionName, const int lineNumber,
-                 const LogLevel logSeverity) noexcept
+                 const LogLevel::Level logSeverity) noexcept
 {
     return LogMessage(this, LogMessage::Header{ .fileName         = fileName,
                                                 .functionName     = functionName,
@@ -111,12 +144,60 @@ LogMessage::operator<<(const std::string &msg) noexcept
     return (*this << msg.c_str());
 }
 
+LogMessage &
+LogMessage::operator<<(const QVariant &variant) noexcept
+{
+    return (*this << variant.toString());
+}
+
 LogMessage &
 LogMessage::operator<<(const QString &msg) noexcept
 {
     return (*this << msg.toStdString());
 }
 
+LogMessage &
+LogMessage::operator<<(const QFileInfo &fileInfo) noexcept
+{
+    return (*this << "QFileInfo{ " << fileInfo.absoluteFilePath() << "}");
+}
+
+LogMessage &
+LogMessage::operator<<(const double *ptr) noexcept
+{
+    return (*this << "Pointer{ double, " << pointerToString<double>(ptr) << " }");
+}
+
+LogMessage &
+LogMessage::operator<<(const unsigned char c) noexcept
+{
+    return (*this << static_cast<char>(c));
+}
+
+LogMessage &
+LogMessage::operator<<(const unsigned int i) noexcept
+{
+    return (*this << static_cast<unsigned long>(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const unsigned long i) noexcept
+{
+    return (*this << std::to_string(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const unsigned long long i) noexcept
+{
+    return (*this << std::to_string(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const long long i) noexcept
+{
+    return (*this << std::to_string(i));
+}
+
 LogMessage &
 LogMessage::operator<<(const int i) noexcept
 {
@@ -129,6 +210,17 @@ LogMessage::operator<<(const long i) noexcept
     return (*this << std::to_string(i));
 }
 
+LogMessage &
+LogMessage::operator<<(const std::string_view strv) noexcept
+{
+    for (std::size_t i = 0; (i < strv.size()) && (indexInArray < messageBufferLength - 1);
+         ++i, ++indexInArray) {
+        textBuffer[indexInArray] = strv[i];
+    }
+    textBuffer[indexInArray] = '\0';
+    return *this;
+}
+
 LogMessage &
 LogMessage::operator<<(const char *str) noexcept
 {
diff --git a/src/Lib/Log.hh b/src/Lib/Log.hh
index 5b49381f4c705f2572730d535abe130544468b48..a14b53920c4bc3a08561a799106ffef144cc8c96 100644
--- a/src/Lib/Log.hh
+++ b/src/Lib/Log.hh
@@ -3,10 +3,10 @@
 #include "Utils.hh"
 
 // Create a logger with a category
-#define VIVY_GET_LOGGER(sink, cat) std::make_shared<Logger>((sink).get(), std::string_view{ #cat })
+#define VIVY_GET_LOGGER(sink, cat) (sink)->newLogger(std::string_view{ #cat })
 
 // Log something in a logger
-#define VIVY_LOG_WITH_LEVEL(log, level) log->logEvent(__FILE__, __func__, __LINE__, level)
+#define VIVY_LOG_WITH_LEVEL(log, level) (log)->logEvent(__FILE__, __func__, __LINE__, level)
 #define VIVY_LOG_WARN(log)              VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Warning)
 #define VIVY_LOG_DEBUG(log)             VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Debug)
 #define VIVY_LOG_INFO(log)              VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Info)
@@ -17,15 +17,15 @@
 // intended to be used in an object to not polluate namespaces.
 #define VIVY_DCL_LOG_SINK(sink)                          \
     std::shared_ptr<LogSink> sink{ LogSink::newSink() }; \
-    void flushLogSink() const noexcept { sink->flush(); }
+    void flushLogSink() const noexcept { (sink)->flush(); }
 
 // Declare a dispatch for a sink with no arguments in the constructor.
 #define VIVY_DCL_LOG_DISPATCH(sink, name, dispatch) \
-    std::shared_ptr<dispatch> name{ sink->newDispatcher<dispatch>() };
+    std::shared_ptr<dispatch> name{ (sink)->newDispatcher<dispatch>() };
 
 // Declare a dispatch for a sink with arguments in the constructor.
 #define VIVY_DCL_LOG_DISPATCH_WITH(sink, name, dispatch, ...) \
-    std::shared_ptr<dispatch> name{ sink->newDispatcher<dispatch>(__VA_ARGS__) };
+    std::shared_ptr<dispatch> name{ (sink)->newDispatcher<dispatch>(__VA_ARGS__) };
 
 // Install logger for the object.
 #define VIVY_LOGGABLE_OBJECT(sink, name, logger)                             \
@@ -36,8 +36,10 @@
     LogMessage logInfo() const noexcept { return VIVY_LOG_INFO(logger); }    \
     LogMessage logDebug() const noexcept { return VIVY_LOG_DEBUG(logger); }
 
-#define VIVY_LOG_CTOR(classname) logDebug() << #classname "::CTOR "
-#define VIVY_LOG_DTOR(classname) logDebug() << #classname "::DTOR "
+#define VIVY_LOG_CTOR() logDebug() << "<<CTOR>> "
+#define VIVY_LOG_DTOR() logDebug() << "<<DTOR>> "
+
+#define VIVY_LOG_QUOTED(something) '\'' << (something) << '\''
 
 // Install logger, use the global LogSink
 #define VIVY_APP_LOGGABLE_OBJECT(name, logger) \
@@ -52,13 +54,25 @@ class LogMessage;
 
 // The severity of an event. Critical will cause the LogSink to flush all
 // messages to its emeters and then abort.
-enum class LogLevel : int {
-    Critical = std::numeric_limits<int>::max(), // Will trigger qFatal
-    Error    = 4,
-    Warning  = 3,
-    Info     = 2,
-    Debug    = 1,
-    None     = std::numeric_limits<int>::min() // In option setup to disable logs
+struct LogLevel final {
+    enum Level : int {
+        None, // In option setup to disable logs
+        Debug,
+        Info,
+        Warning,
+        Error,
+        Critical, // Will trigger qFatal
+        ___MaxAndUnused
+    };
+
+    static const std::string_view toStdStringView(const LogLevel::Level) noexcept;
+
+private:
+    using Array = std::array<const std::string_view, ___MaxAndUnused>;
+    static inline constexpr Array LevelsStringViews = { "None",   "Debug", "Info",
+                                                        "Warnin", "Error", "Critical" };
+
+    LogLevel() {}
 };
 
 // A LogSinkDispatcher will excavate LogMessages from the Sink and do something
@@ -85,6 +99,8 @@ public:
 class StderrLogSinkDispatcher : public LogSinkDispatcher {
     VIVY_UNMOVABLE_OBJECT(StderrLogSinkDispatcher)
 
+    static const std::string_view trunkFileName(const char *) noexcept;
+
 public:
     explicit StderrLogSinkDispatcher() noexcept;
     void handleLogMessage(const std::string_view, const LogMessage &) noexcept override;
@@ -103,6 +119,7 @@ public:
     ~LogSink() noexcept;
 
     void registerLogDispatcher(std::shared_ptr<LogSinkDispatcher>) noexcept;
+    void registerLogger(std::shared_ptr<Logger>) noexcept;
     void recieveLogMessage(const Logger *const, LogMessage &&) noexcept;
     void flush() noexcept;
 
@@ -115,10 +132,18 @@ public:
         return dispatch;
     }
 
+    std::shared_ptr<Logger> newLogger(const StringType auto &category) noexcept
+    {
+        std::shared_ptr<Logger> logger = std::make_shared<Logger>(this, category);
+        registerLogger(logger);
+        return logger;
+    }
+
 private:
     std::mutex messageQueueLock{};
     std::vector<std::tuple<const std::string_view, LogMessage>> messageQueue;
     std::vector<std::shared_ptr<LogSinkDispatcher>> logDispatchers;
+    std::vector<std::shared_ptr<Logger>> loggers;
 };
 
 // Message to be logged, constructed by a logger then send
@@ -130,7 +155,7 @@ public:
     struct Header final {
         const char *fileName;
         const char *functionName;
-        const LogLevel severity;
+        const LogLevel::Level severity;
         const int lineNumberInFile;
     };
 
@@ -144,6 +169,14 @@ private:
     std::size_t indexInArray{ 0 };
     const Logger *parentLogger{ nullptr };
 
+    static std::string pointerToString(const auto *ptr) noexcept
+    {
+        std::stringstream stream;
+        stream << "0x" << std::setfill('0') << std::setw(sizeof(std::intptr_t) * 2) << std::hex
+               << reinterpret_cast<const std::intptr_t>(ptr);
+        return stream.str();
+    }
+
 public:
     VIVY_DISABLE_COPY_CTOR(LogMessage)
     VIVY_DISABLE_ASSIGN_OPERATORS(LogMessage)
@@ -159,11 +192,20 @@ public:
     LogMessage &&sink() noexcept;
 
     LogMessage &operator<<(const std::string &) noexcept;
+    LogMessage &operator<<(const std::string_view) noexcept;
     LogMessage &operator<<(const QString &) noexcept;
+    LogMessage &operator<<(const QVariant &) noexcept;
+    LogMessage &operator<<(const QFileInfo &) noexcept;
     LogMessage &operator<<(const char *) noexcept;
+    LogMessage &operator<<(const double *) noexcept;
     LogMessage &operator<<(const char) noexcept;
     LogMessage &operator<<(const int) noexcept;
     LogMessage &operator<<(const long) noexcept;
+    LogMessage &operator<<(const long long) noexcept;
+    LogMessage &operator<<(const unsigned char) noexcept;
+    LogMessage &operator<<(const unsigned int) noexcept;
+    LogMessage &operator<<(const unsigned long) noexcept;
+    LogMessage &operator<<(const unsigned long long) noexcept;
 };
 
 // A logger class, a client to LogSink. Will generate and send instances of
@@ -187,6 +229,6 @@ public:
 
     void sendLogMessage(LogMessage &&) const noexcept;
     LogMessage logEvent(const char *fileName, const char *functionName, const int lineNumber,
-                        const LogLevel) noexcept;
+                        const LogLevel::Level) noexcept;
 };
 }
diff --git a/src/Lib/Uuid.cc b/src/Lib/Uuid.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7062680471fe5fa08e2aeb965807eac2b0dabd52
--- /dev/null
+++ b/src/Lib/Uuid.cc
@@ -0,0 +1,13 @@
+#include "Uuid.hh"
+#include "Log.hh"
+
+Vivy::Uuid::Uuid()
+    : QUuid(QUuid::createUuid())
+{
+}
+
+Vivy::LogMessage &
+Vivy::operator<<(Vivy::LogMessage &msg, Vivy::Uuid uuid) noexcept
+{
+    return msg << "UUID{ " << uuid.toString() << " }";
+}
diff --git a/src/Lib/Uuid.hh b/src/Lib/Uuid.hh
index 068277deeac9f8b33673fa2243a0c5e30e9c2223..083e001f7192acd29c20d6ee447ebd68b8fcb8f6 100644
--- a/src/Lib/Uuid.hh
+++ b/src/Lib/Uuid.hh
@@ -2,13 +2,11 @@
 
 namespace Vivy
 {
-class Uuid : public QUuid {
-public:
-    explicit Uuid()
-        : QUuid(QUuid::createUuid())
-    {
-    }
-
+struct Uuid : QUuid {
+    explicit Uuid();
     QString toString() const noexcept { return QUuid::toString(Uuid::WithoutBraces); }
 };
+
+class LogMessage;
+LogMessage &operator<<(LogMessage &, Uuid) noexcept;
 }
diff --git a/src/UI/AboutWindow.cc b/src/UI/AboutWindow.cc
index 432c7fbf5b39c68138e08dfb33f8927534790dcb..5995a016b80a60fdd7cdb9ec9c708b36da4ce264 100644
--- a/src/UI/AboutWindow.cc
+++ b/src/UI/AboutWindow.cc
@@ -55,7 +55,7 @@ AboutWindow::LicenceLabel::LicenceLabel(QWidget *parent, const QString &url,
                                         const Qt::TextFormat format)
     : QTextEdit(parent)
 {
-    VIVY_LOG_CTOR() << "Creating label for licence '" << url << '\'';
+    VIVY_LOG_CTOR() << "Creating label for licence " << VIVY_LOG_QUOTED(url);
     QFile content(url);
     if (!content.open(QIODevice::ReadOnly | QIODevice::Text))
         throw std::runtime_error("Failed to open file that should be accessible");
diff --git a/src/UI/DockWidgetTitleBar.cc b/src/UI/DockWidgetTitleBar.cc
index 2e1cdaa099d9246e785e46919a317dd51cbb6785..46bc79c28f393361aef522ff04c94fee9394b6bf 100644
--- a/src/UI/DockWidgetTitleBar.cc
+++ b/src/UI/DockWidgetTitleBar.cc
@@ -7,18 +7,20 @@ DockWidgetTitleBar::DockWidgetTitleBar(QDockWidget *parent) noexcept
     , attachedDock(parent)
 {
     if (parent == nullptr)
-        qFatal("Can't pass a nullptr as a parent widget pointer");
+        logFatal() << "Can't pass a nullptr as a parent widget pointer";
 
-    auto *box = new QHBoxLayout(this);
-    box->addWidget(new QLabel(parent->windowTitle(), this));
+    const QString name = parent->windowTitle();
+    auto *const box    = new QHBoxLayout(this);
+    box->addWidget(new QLabel(name, this));
     qobject_cast<QHBoxLayout *>(layout())->setStretch(0, 1);
+    VIVY_LOG_CTOR() << "TitleBar added to dock " << VIVY_LOG_QUOTED(name);
 }
 
 void
 DockWidgetTitleBar::addToDock(QDockWidget *const dock) noexcept
 {
     DockWidgetTitleBar *const titleBar = new DockWidgetTitleBar(dock);
-    qDebug() << "Adding" << dock->windowTitle() << "to dock...";
+    titleBar->logDebug() << "Adding " << VIVY_LOG_QUOTED(dock->windowTitle()) << " to dock...";
     Utils::setTransparentBackgroundForWidget(titleBar);
     dock->setTitleBarWidget(titleBar);
 }
diff --git a/src/UI/DockWidgetTitleBar.hh b/src/UI/DockWidgetTitleBar.hh
index 620c9a947e3537b566ee45a69be521cf63b97005..f3eace6b0c63d4194878a6a9dc23b5cf6f041034 100644
--- a/src/UI/DockWidgetTitleBar.hh
+++ b/src/UI/DockWidgetTitleBar.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../VivyApplication.hh"
+#include "../Lib/Log.hh"
 #include "Utils.hh"
 
 namespace Vivy
@@ -7,6 +9,7 @@ namespace Vivy
 class DockWidgetTitleBar final : public QWidget {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(DockWidgetTitleBar)
+    VIVY_APP_LOGGABLE_OBJECT(DockWidgetTitleBar, logger)
 
     QDockWidget *attachedDock{ nullptr };
     explicit DockWidgetTitleBar(QDockWidget *parent) noexcept;
diff --git a/src/UI/MainWindow.cc b/src/UI/MainWindow.cc
index bfff0df4d0880b608db699d7a205fc782f19208e..0130076f5a9ea93237f18d6aa2273ae8310cca3c 100644
--- a/src/UI/MainWindow.cc
+++ b/src/UI/MainWindow.cc
@@ -1,3 +1,5 @@
+#include "../Lib/Document/VivyDocumentStore.hh"
+#include "../Lib/Script/ScriptStore.hh"
 #include "../Lib/Utils.hh"
 #include "../VivyApplication.hh"
 #include "MainWindow.hh"
@@ -164,7 +166,7 @@ MainWindow::MainWindow() noexcept
 void
 MainWindow::closeEvent(QCloseEvent *event) noexcept
 {
-    qDebug() << "Closing the main window!";
+    logDebug() << "Closing the main window!";
     forEachViews<VivyDocumentView>([](VivyDocumentView *view, int) { view->closeDocument(); });
     QMainWindow::closeEvent(event);
 }
@@ -191,7 +193,7 @@ MainWindow::openProperties(int index) noexcept
         return;
     }
 
-    qDebug().nospace() << "Tab n°" << index << " was double clicked";
+    logDebug() << "Tab n°" << index << " was double clicked";
     AbstractDocumentView *current = getTab(index);
     current->openProperties();
 }
@@ -226,7 +228,7 @@ MainWindow::saveFile() noexcept
     }
 
     catch (const std::runtime_error &e) {
-        qCritical() << "Failed to save current document:" << e.what();
+        logError() << "Failed to save current document: " << e.what();
     }
 }
 
@@ -248,7 +250,7 @@ MainWindow::renameFile() noexcept
     }
 
     catch (const std::runtime_error &e) {
-        qCritical() << "Failed to save current document:" << e.what();
+        logError() << "Failed to save current document: " << e.what();
     }
 }
 
@@ -270,7 +272,7 @@ MainWindow::saveFileAs() noexcept
     }
 
     catch (const std::runtime_error &e) {
-        qCritical() << "Failed to save current document:" << e.what();
+        logError() << "Failed to save current document: " << e.what();
     }
 }
 
@@ -300,7 +302,8 @@ MainWindow::closeDocument(int index) noexcept
                &MainWindow::documentViewActionsChanged);
 
     if (documentToClose) {
-        qDebug() << "Delete document view" << documentToClose->getDocumentTabName();
+        logDebug()
+            << "Delete document view " << VIVY_LOG_QUOTED(documentToClose->getDocumentTabName());
         documentToClose->closeDocument();
         delete documentToClose;
     }
@@ -311,9 +314,9 @@ MainWindow::newDocument() noexcept
 {
     try {
         addTab(new VivyDocumentView(
-            vivyApp->documentStore.newDocument(VivyDocument::UntouchedByDefault), documents));
+            vivyApp->documentStore->newDocument(VivyDocument::UntouchedByDefault), documents));
     } catch (const std::runtime_error &e) {
-        qCritical() << "Failed to create a new empty document:" << e.what();
+        logError() << "Failed to create a new empty document: " << e.what();
     }
 }
 
@@ -323,7 +326,7 @@ MainWindow::openDocument() noexcept
     const QString filename = dialogOpenFileName("Select a document to open", QDir::homePath(),
                                                 Utils::getAnyTopLevelDocumentFileSuffixFilter());
     if (filename.isEmpty()) {
-        qWarning() << "Found an empty filename, don't open a file";
+        logDebug() << "Found an empty filename, don't open a file";
         return;
     }
 
@@ -331,18 +334,18 @@ MainWindow::openDocument() noexcept
     Utils::DocumentType fileType;
 
     if (!Utils::detectDocumentType(fileInfo, &fileType)) {
-        qWarning() << "Failed to detect file type for" << filename;
+        logWarning() << "Failed to detect file type for " << VIVY_LOG_QUOTED(filename);
         return;
     }
 
     // Handle the different types here
     try {
         if (fileType == Utils::DocumentType::Vivy)
-            addTab(new VivyDocumentView(vivyApp->documentStore.loadDocument(filename), documents));
+            addTab(new VivyDocumentView(vivyApp->documentStore->loadDocument(filename), documents));
 
         else if (fileType == Utils::DocumentType::VivyScript) {
-            auto scriptDocument = vivyApp->scriptStore.loadDocument(filename);
-            auto errorTuple     = vivyApp->scriptStore.executeScript(scriptDocument->getUuid());
+            auto scriptDocument = vivyApp->scriptStore->loadDocument(filename);
+            auto errorTuple     = vivyApp->scriptStore->executeScript(scriptDocument->getUuid());
             ScriptDocumentView *newView = new ScriptDocumentView(scriptDocument, documents);
 
             if (errorTuple.has_value()) {
@@ -353,7 +356,8 @@ MainWindow::openDocument() noexcept
             addTab(newView);
         }
     } catch (const std::runtime_error &e) {
-        qCritical() << "Failed to load document" << filename << "with error:" << e.what();
+        logError() << "Failed to load document " << VIVY_LOG_QUOTED(filename)
+                   << " with error: " << e.what();
     }
 }
 
@@ -420,7 +424,7 @@ MainWindow::addTab(AbstractDocumentView *const tab)
                 }
             });
     documentViewActionsChanged();
-    qDebug() << "View constructed successfully";
+    logDebug() << "View constructed successfully";
 }
 
 int
@@ -487,19 +491,19 @@ MainWindow::findFirstUntouchedDocument() const noexcept
 void
 MainWindow::documentViewActionsChanged() noexcept
 {
-    qInfo() << "Document view action changed";
+    logDebug() << "Document view action changed";
     viewMenu->clear();
 
     // Change document view menu if we have a document
     try {
         viewMenu->addActions(getCurrentDocumentView()->getViewsActions());
     } catch (const std::runtime_error &e) {
-        qInfo() << "No view to display:" << e.what();
+        logDebug() << "No view to display: " << e.what();
     }
 }
 
-static inline QString
-executeDialog(MainWindow *const self, QFileDialog *const dialog) noexcept
+QString
+MainWindow::executeDialog(MainWindow *const self, QFileDialog *const dialog) noexcept
 {
     bool dialogAccepted = false;
     std::unique_ptr<VivyFileIconProvider> iconProvider(new VivyFileIconProvider());
@@ -515,7 +519,8 @@ executeDialog(MainWindow *const self, QFileDialog *const dialog) noexcept
 
     const QStringList resList = dialog->selectedFiles();
     if (resList.size() != 1) {
-        qCritical() << "You must select only one file";
+        self->logError()
+            << "You must select only one file, here got " << resList.size() << " files";
         return QStringLiteral("");
     }
 
diff --git a/src/UI/MainWindow.hh b/src/UI/MainWindow.hh
index 5389ab56ad3d5d139b3a5a8932cbc203dba20a37..30a31af108f94ceb43ce3dc34aa06f905ab33c91 100644
--- a/src/UI/MainWindow.hh
+++ b/src/UI/MainWindow.hh
@@ -4,7 +4,9 @@
 #error "This is a C++ header"
 #endif
 
+#include "../VivyApplication.hh"
 #include "../Lib/Utils.hh"
+#include "../Lib/Log.hh"
 #include "../Lib/AbstractDocument.hh"
 #include "../Lib/Document/VivyDocumentStore.hh"
 #include "DocumentViews/AudioVisualizer.hh"
@@ -17,6 +19,7 @@ class AboutWindow;
 
 class MainWindow final : public QMainWindow {
     Q_OBJECT
+    VIVY_APP_LOGGABLE_OBJECT(MainWindow, logger)
 
     QTabWidget *documents{ nullptr };
     QMenu *viewMenu{ nullptr };
@@ -54,6 +57,8 @@ private:
     QString dialogSaveFileName(const QString &title, const QString &folder,
                                const QString &filter) noexcept;
 
+    QString executeDialog(MainWindow *const self, QFileDialog *const dialog) noexcept;
+
     // Do an action with the selected filename. The 'call' variable must be
     // callable like this: call(DocumentView, Document, QString)
     template <typename DV, typename D>
@@ -62,7 +67,7 @@ private:
         const QString filename = dialogOpenFileName(title, QDir::homePath(), filter);
 
         if (filename.isEmpty()) {
-            qWarning() << "Found an empty filename, don't open a file";
+            logWarning() << "Found an empty filename, don't open a file";
             return;
         }
 
@@ -74,7 +79,7 @@ private:
                 call(view, doc, filename);
             }
         } catch (const std::runtime_error &e) {
-            qCritical() << "Operation failed with:" << e.what();
+            logError() << "Operation failed with: " << e.what();
         }
     }
 
diff --git a/src/UI/ScriptDocumentView.cc b/src/UI/ScriptDocumentView.cc
index 4adf39bc4438575573641438e57dc58483ac7cb1..1f510c7c4f48db6686035121bd5229f4db3a34e1 100644
--- a/src/UI/ScriptDocumentView.cc
+++ b/src/UI/ScriptDocumentView.cc
@@ -33,12 +33,15 @@ ScriptDocumentView::ScriptDocumentView(std::shared_ptr<ScriptDocument> ptr, QWid
                                  "  font-family: \"FiraCode\";"
                                  "  font-size: 10pt"
                                  "}"));
+
+    VIVY_LOG_CTOR() << "Creating view for script " << VIVY_LOG_QUOTED(ptr->getName()) << " with "
+                    << ptr->getUuid();
 }
 
 ScriptDocumentView::~ScriptDocumentView()
 {
     setUseFakeVimEditor(false);
-    qDebug() << "~ScriptDocumentView";
+    VIVY_LOG_DTOR() << "Closing a script view";
 }
 
 void
diff --git a/src/UI/ScriptDocumentView.hh b/src/UI/ScriptDocumentView.hh
index c1acc9c31524571e2b7eedf0703991a12296b8fe..26c240cd81fef4ae759a655f4768de49c7344600 100644
--- a/src/UI/ScriptDocumentView.hh
+++ b/src/UI/ScriptDocumentView.hh
@@ -5,7 +5,9 @@
 #error "This is a C++ header"
 #endif
 
+#include "../VivyApplication.hh"
 #include "../Lib/Utils.hh"
+#include "../Lib/Log.hh"
 #include "../Lib/Script/ScriptDocument.hh"
 #include "AbstractDocumentView.hh"
 #include "FakeVim/FakeVimHandler.hh"
@@ -18,6 +20,7 @@ namespace Vivy
 class ScriptDocumentView final : public AbstractDocumentView {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(ScriptDocumentView)
+    VIVY_APP_LOGGABLE_OBJECT(ScriptDocumentView, logger)
 
     using FakeVimHandler = FakeVim::Internal::FakeVimHandler;
 
diff --git a/src/UI/ScriptViews/EditorProxy.cc b/src/UI/ScriptViews/EditorProxy.cc
index 1664c5adc72c09be58d54603a3385256c86d7a35..6cf6616d753a4b83465f814655869eed4ac84257 100644
--- a/src/UI/ScriptViews/EditorProxy.cc
+++ b/src/UI/ScriptViews/EditorProxy.cc
@@ -11,11 +11,10 @@ EditorProxy::connectSignals(FakeVimHandler *handler, QPlainTextEdit *editor) noe
 {
     EditorProxy *proxy = new EditorProxy(editor);
 
-    handler->commandBufferChanged.connect([proxy](const QString &contents, int cursorPos,
-                                                  int /* anchorPos */,
-                                                  int /* messageLevel */) noexcept -> void {
-        proxy->changeStatusMessage(contents, cursorPos);
-    });
+    handler->commandBufferChanged.connect(
+        [proxy](const QString &contents, int cursorPos, int, int) noexcept -> void {
+            proxy->changeStatusMessage(contents, cursorPos);
+        });
 
     handler->extraInformationChanged.connect(
         [proxy](const QString &text) noexcept -> void { proxy->changeExtraInformation(text); });
@@ -50,9 +49,11 @@ EditorProxy::EditorProxy(QPlainTextEdit *widg) noexcept
     : QObject()
     , widget(widg)
 {
+    VIVY_LOG_CTOR()
+        << "Creating new proxy for a the text view " << VIVY_LOG_QUOTED(widg->windowFilePath());
 }
 
-EditorProxy::~EditorProxy() { qDebug() << "~EditorProxy"; }
+EditorProxy::~EditorProxy() { VIVY_LOG_DTOR() << "Delete editor proxy!"; }
 
 void
 EditorProxy::changeStatusData(const QString &info) noexcept
@@ -136,12 +137,16 @@ EditorProxy::handleExCommand(bool *handled, const ExCommand &cmd) noexcept
 
     else if (wantQuit(cmd)) {
         // :q!
-        if (cmd.hasBang)
+        if (cmd.hasBang) {
+            logInfo() << "Want to force quit the editor!";
             emit requestQuit();
+        }
 
         // :q
-        else
+        else {
+            logInfo() << "Want to quit the editor";
             emit requestQuit();
+        }
     }
 
     else if (wantRun(cmd))
@@ -306,17 +311,20 @@ EditorProxy::wantSaveAndQuit(const ExCommand &cmd) noexcept
 bool
 EditorProxy::wantSave(const ExCommand &cmd) noexcept
 {
+    logInfo() << "Want to save the editor's file";
     return cmd.matches("w", "write") || cmd.matches("wa", "wall");
 }
 
 bool
 EditorProxy::wantQuit(const ExCommand &cmd) noexcept
 {
+    logInfo() << "Want to quit editor";
     return cmd.matches("q", "quit") || cmd.matches("qa", "qall");
 }
 
 bool
 EditorProxy::wantRun(const ExCommand &cmd) noexcept
 {
+    logInfo() << "Want to run a command: " << cmd.cmd;
     return cmd.matches("run", "run") || cmd.matches("make", "make");
 }
diff --git a/src/UI/ScriptViews/EditorProxy.hh b/src/UI/ScriptViews/EditorProxy.hh
index c93a3823f753320f5521b446ddcb309bc5090d39..2abd88669c29c261cb55c3ed267a1c65370b1348 100644
--- a/src/UI/ScriptViews/EditorProxy.hh
+++ b/src/UI/ScriptViews/EditorProxy.hh
@@ -1,13 +1,9 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../../Lib/Log.hh"
 #include "ScriptEditor.hh"
 
-class QMainWindow;
-class QTextDocument;
-class QString;
-class QWidget;
-class QTextCursor;
-
 namespace FakeVim::Internal
 {
 class FakeVimHandler;
@@ -21,6 +17,7 @@ class EditorProxy;
 class EditorProxy final : public QObject {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(EditorProxy)
+    VIVY_APP_LOGGABLE_OBJECT(EditorProxy, logger)
 
     explicit EditorProxy(QPlainTextEdit *widget) noexcept;
     using FakeVimHandler = FakeVim::Internal::FakeVimHandler;
diff --git a/src/UI/ScriptViews/ScriptEditor.cc b/src/UI/ScriptViews/ScriptEditor.cc
index 80b88fcfdfaaa8bf24a6107c32b5413f92661bcd..4ba88645d892ba1848dd9e7d113b135b46a6d820 100644
--- a/src/UI/ScriptViews/ScriptEditor.cc
+++ b/src/UI/ScriptViews/ScriptEditor.cc
@@ -168,5 +168,5 @@ ScriptEditor::lineNumberAreaPaintEvent(QPaintEvent *event) noexcept
 void
 ScriptEditor::updateLastLuaError(int line, QString desc)
 {
-    qDebug() << "Update error on line" << line << "with description" << desc;
+    logDebug() << "Update error on line " << line << " with description " << desc;
 }
diff --git a/src/UI/ScriptViews/ScriptEditor.hh b/src/UI/ScriptViews/ScriptEditor.hh
index 5a68ef9c71bdd6afe31211179ae7c28dbeed5c8c..1a2391cf6a40495859c0be0eedf6ddb5dd1b58a6 100644
--- a/src/UI/ScriptViews/ScriptEditor.hh
+++ b/src/UI/ScriptViews/ScriptEditor.hh
@@ -4,6 +4,8 @@
 #error "This is a C++ header"
 #endif
 
+#include "../../VivyApplication.hh"
+#include "../../Lib/Log.hh"
 #include "../../Lib/Utils.hh"
 
 namespace Vivy
@@ -11,6 +13,7 @@ namespace Vivy
 class ScriptEditor final : public QPlainTextEdit {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(ScriptEditor)
+    VIVY_APP_LOGGABLE_OBJECT(ScriptEditor, logger)
 
     // Get the line numbers, private class
     class LineNumberArea final : public QWidget {
diff --git a/src/UI/VivyDocumentView.cc b/src/UI/VivyDocumentView.cc
index be7437916ed688742d5beeb24454295e5ea124b2..86e155afd60eac27964e0f270e8dba6f6d8ff4f8 100644
--- a/src/UI/VivyDocumentView.cc
+++ b/src/UI/VivyDocumentView.cc
@@ -6,6 +6,7 @@
 #include "DocumentViews/AssLinesModel.hh"
 #include "../VivyApplication.hh"
 #include "../Lib/Document/VivyDocument.hh"
+#include "../Lib/Document/VivyDocumentStore.hh"
 
 using namespace Vivy;
 
@@ -13,6 +14,9 @@ VivyDocumentView::VivyDocumentView(std::shared_ptr<VivyDocument> doc, QWidget *p
     : AbstractDocumentView(AbstractDocumentView::Type::Vivy, parent)
     , document(doc)
 {
+    VIVY_LOG_CTOR() << "Creating view for vivy document " << VIVY_LOG_QUOTED(doc->getName())
+                    << " with " << doc->getUuid() << "...";
+
     setDockNestingEnabled(true);
 
     loadAudioView();
@@ -30,17 +34,17 @@ VivyDocumentView::VivyDocumentView(std::shared_ptr<VivyDocument> doc, QWidget *p
     viewsActions.append(openPropertiesAct);
 
     // The separator
-    QAction *separator = new QAction(this);
+    QAction *const separator = new QAction(this);
     separator->setSeparator(true);
     viewsActions.append(separator);
+
+    VIVY_LOG_CTOR() << "View for " << doc->getUuid() << " done!";
 }
 
 VivyDocumentView::~VivyDocumentView() noexcept
 {
-    qDebug()
-        << "Deleting the document view: ref count on document" << document->getName() << "is"
-        << document.use_count() << "and" << (visualizer ? "has visualizer" : "without visualizer");
-
+    VIVY_LOG_DTOR() << "Deleting view for " << document->getUuid() << ", file name is "
+                    << VIVY_LOG_QUOTED(document->getName());
     deleteAllContent();
 }
 
@@ -121,7 +125,7 @@ VivyDocumentView::loadAudioView() noexcept
     AudioContext::StreamPtr stream                  = audioDocument->getDefaultStream();
 
     if (stream == nullptr) {
-        qCritical() << "Failed to get default audio stream";
+        logError() << "Failed to get default audio stream";
         return;
     }
 
@@ -145,9 +149,8 @@ VivyDocumentView::loadAudioView() noexcept
 void
 VivyDocumentView::closeDocument() noexcept
 {
-    qDebug() << "Closing the document:" << document->getName() << "( ref count is"
-             << document.use_count() << ")";
-    vivyApp->documentStore.closeDocument(document->getUuid());
+    logDebug() << "Closing document " << document->getUuid();
+    vivyApp->documentStore->closeDocument(document->getUuid());
     allowToCloseAllDocks();
 }
 
diff --git a/src/UI/VivyDocumentView.hh b/src/UI/VivyDocumentView.hh
index 0a6f13b4519362f6468632348a854051a44e4b64..a17d1c34c8a0cdbb61d9ae12807d10a96c383266 100644
--- a/src/UI/VivyDocumentView.hh
+++ b/src/UI/VivyDocumentView.hh
@@ -1,23 +1,25 @@
-#ifndef VIVY_DOCUMENTVIEW_H
-#define VIVY_DOCUMENTVIEW_H
+#pragma once
 
 #ifndef __cplusplus
 #error "This is a C++ header"
 #endif
 
-#include "AbstractDocumentView.hh"
+#include "../VivyApplication.hh"
+#include "../Lib/Log.hh"
 #include "../Lib/Document/VivyDocument.hh"
+#include "PropertyModel.hh"
+#include "UnclosableDockWidget.hh"
+#include "AbstractDocumentView.hh"
 #include "DocumentViews/VideoView.hh"
 #include "DocumentViews/AssLinesView.hh"
 #include "DocumentViews/AssLinesModel.hh"
-#include "PropertyModel.hh"
-#include "UnclosableDockWidget.hh"
 
 namespace Vivy
 {
 class VivyDocumentView final : public AbstractDocumentView {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(VivyDocumentView)
+    VIVY_APP_LOGGABLE_OBJECT(VivyDocumentView, logger)
 
     static inline constexpr QDockWidget::DockWidgetFeatures allDockFeatures =
         QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
@@ -53,7 +55,4 @@ private:
     UnclosableDockWidget *videoView{ nullptr };
     QAction *openPropertiesAct{ nullptr };
 };
-
 }
-
-#endif // VIVY_DOCUMENTVIEW_H
diff --git a/src/VivyApplication.cc b/src/VivyApplication.cc
index b6df260b646972708049f4ee0b98929c86516988..959fb498ee963830c45e371846a392004d9f194d 100644
--- a/src/VivyApplication.cc
+++ b/src/VivyApplication.cc
@@ -6,7 +6,7 @@ using namespace Vivy;
 VivyApplication::VivyApplication(int &argc, char **argv)
     : QApplication(argc, argv)
 {
-    VIVY_LOG_CTOR(VivyApplication) << "Construction is OK";
+    VIVY_LOG_CTOR() << "Construction is OK";
 }
 
 void
@@ -27,7 +27,7 @@ VivyApplication::setTheme(Theme theme) noexcept
         stylesheet.open(QFile::ReadOnly | QFile::Text);
         QTextStream stylesheetStream(&stylesheet);
         setStyleSheet(stylesheetStream.readAll());
-        logInfo() << "Theme set using " << sheet;
+        logInfo() << "Theme set using " << VIVY_LOG_QUOTED(sheet);
     }
 
     flushLogSink();
diff --git a/src/VivyApplication.hh b/src/VivyApplication.hh
index 85e715d85209eb49388a21ec2aa102a3bcf56288..4a9985a35c48c5ed41d87f2dc2087f74f5ef7840 100644
--- a/src/VivyApplication.hh
+++ b/src/VivyApplication.hh
@@ -29,13 +29,14 @@
 #define VIVY_MACOS
 #endif
 
-#include "Lib/Script/ScriptStore.hh"
-#include "Lib/Document/VivyDocumentStore.hh"
 #include "Lib/Log.hh"
 
 namespace Vivy
 {
 class MainWindow;
+class ScriptStore;
+class VivyDocumentStore;
+class AbstractDocument;
 
 // Vivy application class
 class VivyApplication : public QApplication {
@@ -45,8 +46,8 @@ class VivyApplication : public QApplication {
     VIVY_LOGGABLE_OBJECT(logSink, APPLICATION, logger)
 
 public:
-    VivyDocumentStore documentStore{};
-    ScriptStore scriptStore{};
+    std::shared_ptr<VivyDocumentStore> documentStore{};
+    std::shared_ptr<ScriptStore> scriptStore{};
 
     enum class Font {
         Monospace,