0

I have a fairly basic singleton logger class that wraps around spdlog, and that I intend to use throughout my code via the macros defined to log to files. The problem I am having is that I cannot get it to log to files. If I use the spdlog::stdout_color_mt it outputs to stdout as expected. But if I try either spdlog::basic_logger_mt or spdlog::basic_logger_mt<spdlog::async_factory> the log file is created, but is empty. If I call logger_->flush() after each use, all I get is the first line "Log start: ... " I also assume you shouldn't have to flush manually after every use.

The interesting thing is that if I test the logger with the sample usage below it all works except for the "log end" in the Logger class destructor. But when I use the exact same header in my project, the log file is empty.

My question is: is there something obvious I am missing?

Here is the logger class:

#pragma once

#include <string>
#include <chrono>
#include <iostream>

#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/async.h"


#define LOG_TRACE(format, ...) spdlog::trace(format, ##__VA_ARGS__)
#define LOG_DEBUG(format, ...) spdlog::debug(format, ##__VA_ARGS__)
#define LOG_INFO(format, ...) spdlog::info(format, ##__VA_ARGS__)
#define LOG_WARN(format, ...) spdlog::warn(format, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) spdlog::error(format, ##__VA_ARGS__)
#define LOG_CRITICAL(format, ...) spdlog::critical(format, ##__VA_ARGS__)

class Logger
{
public:
    ~Logger()
    {
        if (initialised_)
        {
            spdlog::info("Log end");
            spdlog::shutdown();
        }
    }

    static Logger& Instance()
    {
        static Logger instance;
        return instance;
    }

    void Initialise(const std::string& filename, spdlog::level::level_enum level)
    {
        if (initialised_) return;

        try
        {
            // logger_ = spdlog::stdout_color_mt("log");
            auto logger = spdlog::basic_logger_mt("log", filename);
            // spdlog::init_thread_pool(8192, 1);
            // logger_ = spdlog::basic_logger_mt<spdlog::async_factory>("log", filename);
            // logger_ = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "async_log.txt");
            spdlog::set_default_logger(logger);
            logger->set_pattern("[%H:%M:%S.%F] [%l] %v");
            logger->set_level(level);
            auto start = std::chrono::system_clock::now();
            std::time_t starttime = std::chrono::system_clock::to_time_t(start);
            spdlog::info("Log start: {}", std::ctime(&starttime));

            initialised_ = true;
        }
        catch (spdlog::spdlog_ex& e)
        {
            std::cout << "log init failed: " << e.what() << std::endl;
        }
    }

    Logger(const Logger&) = delete;
    void operator=(const Logger&) = delete;

private:
    Logger() {}

private:
    bool initialised_ {false};
};

And basic usage:

class LogUser
{
public:
    LogUser() { LOG_INFO("another class using logger"); }
    void LogUsername() { LOG_INFO("username"); }
};

void AnotherFunc()
{
    LOG_WARN("logging from another function");
}

int main(int argc, char** argv)
{
    Logger::Instance().Initialise("log.txt", spdlog::level::info);
    LOG_INFO("logging something");
    LogUser user;
    AnotherFunc();
    user.LogUsername();
    return 0;
}

Summary of what I have tried:

  • spdlog::stdout_color_mt in basic example above: works
  • spdlog::basic_logger_mt in basic example above: works
  • spdlog::stdout_color_mt in project: works
  • spdlog::basic_logger_mt in project: Doesn't work

Caveat for cases that work: "Log end" in the ~Logger() doesn't work.

  • *via the macros defined to log to files* can we invite you into the 21st century and trusting the compiler to inline functions? Usually a lot easier to debug. – user4581301 Apr 26 '23 at 22:24

0 Answers0