datactl/logging.cpp

datactl/logging.cpp

datactl/logging.cpp

Namespaces

Name
Syntalos
Syntalos::datactl
Syntalos::datactl::detail

Source code

/*
 * Copyright (C) 2025-2026 Matthias Klumpp <matthias@tenstral.net>
 *
 * Licensed under the GNU Lesser General Public License Version 3
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the license, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "logging.h"
#include "loginternal.h"

#include <cstdio>
#include <string>
#include <string_view>

namespace Syntalos::datactl
{

// Zero-initialized (constant initialization) before any dynamic init fires,
// so LogCategory constructors in other TUs can safely prepend to this list.
std::atomic<LogCategory *> g_categoryListHead{nullptr};

std::atomic<bool> g_handlerActive{true}; // default handler is always installed

LogCategory::LogCategory(const char *name_, LogSeverity defaultSeverity) noexcept
    : name(name_),
      threshold(static_cast<int>(defaultSeverity)),
      next(nullptr)
{
    // wait-free CAS prepend - safe even when multiple TUs register simultaneously
    auto head = g_categoryListHead.load(std::memory_order_relaxed);
    do {
        next = head;
    } while (
        !g_categoryListHead.compare_exchange_weak(head, this, std::memory_order_release, std::memory_order_relaxed));
}

namespace detail
{
static void defaultHandler(const LogMessage &m)
{
    auto out = (m.severity >= LogSeverity::Warning) ? stderr : stdout;
    std::fprintf(out, "%s: %.*s\n", m.category, static_cast<int>(m.message.size()), m.message.data());
}

static LogHandlerFn g_handler{defaultHandler};
} // namespace detail

void dispatchLog(
    const LogCategory &cat,
    LogSeverity sev,
    const char *file,
    int line,
    const char *function,
    const std::string &message)
{
    const LogMessage lm{sev, cat.name, file, line, function, std::string_view(message)};
    detail::g_handler(lm);
}

// ---- Public API ----

void setLogHandler(LogHandlerFn handler)
{
    detail::g_handler = std::move(handler);
    g_handlerActive.store(bool(detail::g_handler), std::memory_order_release);
}

void setLogSeverity(LogSeverity min)
{
    auto cat = g_categoryListHead.load(std::memory_order_acquire);
    while (cat) {
        cat->threshold.store(static_cast<int>(min), std::memory_order_relaxed);
        cat = cat->next;
    }
}

void setLogSeverity(const char *category, LogSeverity min)
{
    const std::string_view name(category);
    auto cat = g_categoryListHead.load(std::memory_order_acquire);
    while (cat) {
        if (std::string_view(cat->name) == name)
            cat->threshold.store(static_cast<int>(min), std::memory_order_relaxed);
        cat = cat->next;
    }
}

} // namespace Syntalos::datactl

Updated on 2026-04-24 at 06:25:18 +0000