14

I am trying to use spdlog in a project involving a library under windows. I create two loggers. One for the app using the library, one for the library itself. The library's logger is created from the app but when the library want to add a message, it crash.

Following is an simplified example.

The library

libclass.h

#ifndef LIBCLASS_H
#define LIBCLASS_H

#include <spdlog/spdlog.h>

#ifdef WIN32
#  ifdef BUILD_APPLIB_SHARED
#    define APPLIB_EXPORT __declspec(dllexport)
#  else
#    define APPLIB_EXPORT
#  endif //BUILD_APPLIB_SHARED
#else
#  define APPLIB_EXPORT
#endif // WIN32

class APPLIB_EXPORT LibClass
{
public:
    LibClass();
    ~LibClass();

    static std::string loggerName();

    void testLog();

private:
    std::shared_ptr<spdlog::logger> m_logger;
};

#endif //LIBCLASS_H

libclass.cpp

#include "libclass.h"

const std::string myLoggerName = "lib_logger";

LibClass::LibClass()
{
    m_logger = spdlog::get(myLoggerName);
}

LibClass::~LibClass()
{ }

std::string LibClass::loggerName()
{
    return myLoggerName;
}

void LibClass::testLog()
{
    m_logger->info("Log from library");
}

The application

main.cpp

#include <spdlog/spdlog.h>
#include <applib/libclass.h>

void logtest()
{
    auto logger = spdlog::get("app_logger");
    logger->info("Log from application");
}

int main(int argc, char *argv[])
{
    // create loggers
    auto appLogger = spdlog::stdout_logger_mt("app_logger");
    auto libLogger = spdlog::stdout_logger_mt(LibClass::loggerName());

    // log from app
    logtest();

    // log from lib
    LibClass lc;
    lc.testLog();

    return 0;
}
vitaut
  • 49,672
  • 25
  • 199
  • 336
AlexandreP
  • 410
  • 1
  • 5
  • 17

2 Answers2

8

Spdlog uses a singleton registry for keeping track of available loggers. You get one instance of that registry per dll and exe though.

When you create the logger in the exe, it is added to the exe's registry but not the dlls. When you use spdlog::get(myLoggerName) in your dll, you are querying the registry of the dll which does not contain the "lib_logger" and thus you are getting a null shared_ptr.

Possible solutions are: create the lib_logger in the lib instead of the exe if you are only using it in the dll anyway, or pass the logger from the exe to the dll and call spdlog::register_logger with it inside the dll before calling spdlog::get(myLoggerName). Then you can use the same logger from both the exe and the dll.

An example how one can register loggers across dll boundaries can be found at https://github.com/gabime/spdlog/wiki/How-to-use-spdlog-in-DLLs

Or an example using your code:

The library

libclass.h

#ifndef LIBCLASS_H
#define LIBCLASS_H

#include <spdlog/spdlog.h>

#ifdef WIN32
#  ifdef BUILD_APPLIB_SHARED
#    define APPLIB_EXPORT __declspec(dllexport)
#  else
#    define APPLIB_EXPORT
#  endif //BUILD_APPLIB_SHARED
#else
#  define APPLIB_EXPORT
#endif // WIN32

class APPLIB_EXPORT LibClass
{
public:
  LibClass();
  ~LibClass();

  static std::string loggerName();

  void testLog();

private:
  std::shared_ptr<spdlog::logger> m_logger;
};

APPLIB_EXPORT void registerLogger(std::shared_ptr<spdlog::logger> logger);

#endif //LIBCLASS_H

libclass.cpp

#include "libclass.h"

const std::string myLoggerName = "lib_logger";

LibClass::LibClass()
{
  m_logger = spdlog::get(myLoggerName);
}

LibClass::~LibClass()
{ }

std::string LibClass::loggerName()
{
  return myLoggerName;
}

void LibClass::testLog()
{
  m_logger->info("Log from library");
}

APPLIB_EXPORT void registerLogger(std::shared_ptr<spdlog::logger> logger)
{
  spdlog::register_logger(logger);
}

The application main.cpp

#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_sinks.h>

#include <applib/libclass.h>

void logtest()
{
  auto logger = spdlog::get("app_logger");
  logger->info("Log from application");
}

int main(int argc, char* argv[])
{
  // create loggers
  auto appLogger = spdlog::stdout_logger_mt("app_logger");
  auto libLogger = spdlog::stdout_logger_mt(LibClass::loggerName());

  registerLogger(libLogger);

  // log from app
  logtest();

  // log from lib
  LibClass lc;
  lc.testLog();

  return 0;
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Stefan v.K.
  • 356
  • 2
  • 8
1

Your example works fine on the current version of spdlog:

$ g++ -std=c++11 main.cpp libclass.cpp  -I .
$ ./a.out 
[2015-08-24 07:29:04.502] [app_logger] [info] Log from application
[2015-08-24 07:29:04.502] [lib_logger] [info] Log from library

Same when using a shared library:

# CMake config:
project(TEST)
include_directories(.)
add_library(class SHARED libclass.cpp)
target_compile_options(class PUBLIC -std=c++11)
add_executable(main main.cpp)
target_link_libraries(main class)

Results:

$ cmake .
$ make 
$ ldd main
    ...
    libclass.so => ...
$ ./main 
[2015-08-25 08:57:51.864] [app_logger] [info] Log from application
[2015-08-25 08:57:51.864] [lib_logger] [info] Log from library

Make sure that you link with a DLL runtime on Windows.

vitaut
  • 49,672
  • 25
  • 199
  • 336
  • 3
    Your example is working because everything is under the same binary. If you build libclass in a shared library and main apart linking with the shared lib, it does not work. – AlexandreP Aug 25 '15 at 15:16