How can i use spdlog in C environment, can someone have ideas about how to pack it in <C&C++> environment
mandy-yan opened this issue · comments
my project has C and C++ files, How i can pack spdlog that i can use it in C and C++ files
Thanks!
This is a general question for a mixed C and C++ project.
Expose functions that are compatible with C with extern "C"
. And call the C++ library within that function.
spdlog logging function is defined as a C++ template function.
Of course, template functions cannot be defined in C, so if you want to pass variable number of arguments., you have to define wrapper functions for all patterns.
Unfortunately, the only way to do this is to define all functions that accept the necessary arguments.
Please provide the code that is failing to compile.
You can call the spdlog function from a C++ function that is compatible with the C.
I think you have a problem with the source code failing to compile, but I cannot investigate the cause as it is not provided.
Please also provide compile error messages.
I create small repo https://github.com/tt4g/spdlog_issue_2301, it's works for me.
I create small repo https://github.com/tt4g/spdlog_issue_2301, it's works for me.
I have four files in logs directory within the project, they are "Cspdlog.cpp Cspdlog.h CspdlogWrapper.cpp CsplogWrapper.h"
the main problem is here:
i can`t pass the "..." into the spdlog library from the CsplogWrapper.h
void nclog_debug_fmt(const char *fmt, ...)
{
auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get();
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::debug, __VA_ARGS__);
}
void nclog_info_fmt(const char *fmt, ...)
{
auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get();
va_list args;
va_start(args, fmt);
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::info, va_arg(fmt, args));
va_end(args);
}
And the function "nclog_debug_fmt and nclog_info_fmt" is defined as this in CsplogWrapper.h:
#pragma once
#define NCLOG_DEBUG(fmt, log_arg...) nclog_debug_fmt(fmt, ##log_arg)
#define NCLOG_INFO(fmt, log_arg...) nclog_info_fmt(fmt, ##log_arg)
#define NCLOG_WARN(fmt, log_arg...) nclog_warn_fmt(fmt, ##log_arg)
#define CLOG_DEBUG(fmt, log_arg...) clog_debug_fmt(fmt, ##log_arg)
#define CLOG_INFO(fmt, log_arg...) clog_info_fmt(fmt,##log_arg)
#define CLOG_WARN(fmt, log_arg...) clog_warn_fmt(fmt,##log_arg)
Please also provide compile error messages.
the errors is below:
mipsel-openwrt-linux-uclibc-g++ -std=c++11 -I. -I./src -I./src/c-log/src -I./src/join_manager/ -I./src/jsoncpp-master/include -I./src/jsoncpp-master/src/lib_json -I./src/simple-timing-wheel -Wp,-MT,out/target/RiCiDb.o -Wp,-MMD,out/target/RiCiDb.o.d -g -Wall -Wno-unused -Wno-format -O2 -c -o out/target/RiCiDb.o ./src/RiCiDb.cpp
mipsel-openwrt-linux-uclibc-g++ -std=c++11 -I. -I./src -I./src/c-log/src -I./src/join_manager/ -I./src/jsoncpp-master/include -I./src/jsoncpp-master/src/lib_json -I./src/simple-timing-wheel -Wp,-MT,out/target/CspdlogWrapper.o -Wp,-MMD,out/target/CspdlogWrapper.o.d -g -Wall -Wno-unused -Wno-format -O2 -c -o out/target/CspdlogWrapper.o ./src/c-log/src/CspdlogWrapper.cpp
./src/c-log/src/CspdlogWrapper.cpp:10:98: warning: __VA_ARGS__ can only appear in the expansion of a C99 variadic macro [enabled by default]
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::debug, __VA_ARGS__);
^
./src/c-log/src/CspdlogWrapper.cpp: In function 'void nclog_debug_fmt(const char*, ...)':
./src/c-log/src/CspdlogWrapper.cpp:10:98: error: '__VA_ARGS__' was not declared in this scope
In file included from ./src/c-log/src/CspdlogWrapper.cpp:1:0:
./src/c-log/src/CspdlogWrapper.cpp: In function 'void nclog_info_fmt(const char*, ...)':
./src/c-log/src/CspdlogWrapper.cpp:19:109: error: expected type-specifier before 'args'
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::info, va_arg(fmt, args));
^
./src/c-log/src/CspdlogWrapper.cpp:19:109: error: expected ')' before 'args'
./src/c-log/src/CspdlogWrapper.cpp:19:114: error: expected ';' before ')' token
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::info, va_arg(fmt, args));
^
In file included from ./src/c-log/src/CspdlogWrapper.cpp:1:0:
./src/c-log/src/CspdlogWrapper.cpp: In function 'void nclog_warn_fmt(const char*, ...)':
./src/c-log/src/CspdlogWrapper.cpp:28:109: error: expected type-specifier before 'args'
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::warn, va_arg(fmt, args));
^
./src/c-log/src/CspdlogWrapper.cpp:28:109: error: expected ')' before 'args'
./src/c-log/src/CspdlogWrapper.cpp:28:114: error: expected ';' before ')' token
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::warn, va_arg(fmt, args));
^
In file included from ./src/c-log/src/CspdlogWrapper.cpp:1:0:
./src/c-log/src/CspdlogWrapper.cpp: In function 'void mlog_warn_fmt(const char*, ...)':
./src/c-log/src/CspdlogWrapper.cpp:114:109: error: expected type-specifier before 'args'
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::warn, va_arg(fmt, args));
^
./src/c-log/src/CspdlogWrapper.cpp:114:109: error: expected ')' before 'args'
./src/c-log/src/CspdlogWrapper.cpp:114:114: error: expected ';' before ')' token
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::warn, va_arg(fmt, args));
^
Makefile:147: recipe for target 'out/target/CspdlogWrapper.o' failed
I create small repo https://github.com/tt4g/spdlog_issue_2301, it's works for me.
my use is a little diffrent with your repo. Below is my use, and i don`t know how to reslove the problem.
the Cspdlog.cpp and Csplog.h i use the single instance C++ to wrap the spdlog, it just like this:
Cspdlog.h :
#pragma once
#include <iostream>
#include <vector>
#include "platform.h"
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"
namespace SLog {
//CLogger向STDOUT输出内容, 最低等级Debug
class CLogger
{
public:
static CLogger *getInstance();
std::shared_ptr<spdlog::logger> getConsoleLogger();
std::shared_ptr<spdlog::logger> getNConsoleLogger();
private:
CLogger();
~CLogger();
CLogger(const CLogger&)=delete;
CLogger& operator=(const CLogger&)=delete;
public:
void setPattern(const char *pattern);
void setLevel(spdlog::level::level_enum level);
public:
spdlog::sink_ptr nconsole_sink;
spdlog::sink_ptr console_sink;
private:
std::shared_ptr<spdlog::logger> console;
std::shared_ptr<spdlog::logger> npattern_console;
};
}
Cspdlog.cpp:
#include "Cspdlog.h"
#include "platform.h"
/*-----------------CLogger----------------------*/
CLogger::CLogger()
{
try
{
nconsole_sink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
nconsole_sink->set_level(spdlog::level::debug); //NConsole默认为Debug
nconsole_sink->set_pattern("%v"); //NConsole格式为空
console_sink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
console_sink->set_level(spdlog::level::debug); //Console默认为Debug
console_sink->set_pattern("[%D %T %!:%#][%L] %v"); //Console默认为"%D %T %!:%#][%L] %v"
console = std::make_shared<spdlog::logger>("console", console_sink);
npattern_console = std::make_shared<spdlog::logger>("npatternconsole", nconsole_sink);
npattern_console->set_level(spdlog::level::debug); //不带格式化输出数据, 在指定处使用
npattern_console->set_pattern("%v");
spdlog::register_logger(npattern_console);
console->set_level(spdlog::level::debug); //带格式化输出数据
console->set_pattern("[%D %T %!:%#][%l] %v");
spdlog::register_logger(console);
spdlog::set_default_logger(console);
}
catch(const spdlog::spdlog_ex& ex)
{
std::cout << "spdLog initialization failed: " << ex.what() << std::endl;
}
}
CLogger::~CLogger()
{
spdlog::drop("console");
spdlog::drop("npatternconsole");
}
CLogger *CLogger::getInstance()
{
static CLogger clogger;
return &clogger;
}
std::shared_ptr<spdlog::logger> CLogger::getConsoleLogger()
{
return console;
}
I don't know whether I have described clearly, if you have something unclearly, pls. told me
many thanks to you.
C++ template arguments must be passed type information.
You cannot use va_list
and va_args
because they do not provide type information.
Don't use va_list
and va_args
. and the only way is to define a different C-compatible function for each type of logging argument.
I create small repo https://github.com/tt4g/spdlog_issue_2301, it's works for me.
I have created the above repository as sample code.
There are two rules that must be understood:
- Cannot use
va_list
andva_args
for logging functions - There is no function overloading in C
This means that the logging macros cannot be used (such as NCLOG_DEBUG
and NCLOG_INFO
).
Define all the differently named functions as follows, with only the required argument types:
void nclog_debug_fmt_char(const char *fmt, const char *extra)
{
auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get();
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::debug, fmt, extra);
}
void nclog_debug_fmt_int(const char *fmt, int extra)
{
auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get();
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::debug, fmt, extra);
}
void nclog_info_char(const char *fmt, const char *extra)
{
auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get();
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::info, fmt, extra);
}
void nclog_info_int(const char *fmt, int extra)
{
auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get();
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::info, fmt, extra);
}
'nclog_debug_fmt_char' means just print the string?
'nclog_debug_fmt_int' means just print the integer?
But if i want print variable parameters, How it use? ex:
spdlog::debug("an example to print string {}, print {}", "test",100);
'nclog_debug_fmt_char' means just print the string?
'nclog_debug_fmt_int' means just print the integer?
Yes.
But if i want print variable parameters, How it use?
As written previously, define all functions that correspond to the argument types you want to output to the log.
Example:
void nclog_debug_fmt_char_int(const char *fmt, const char *str, int num)
{
auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get();
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::debug, fmt, str, num);
}
is this the only solution?
Yes.
This is because the C++ template function performs type checking.
Learning the C++ template will help you understand why.
This is because the C++ template function performs type checking. Learning the C++ template will help you understand why.
if the function 'nclog_debug_fmt_char' works, then below codes maybe works too:
void nclog_info_fmt(const char *fmt, ...)
{
char *buffer = NULL;
buffer = (char *)malloc(1024);
if(buffer == NULL)
return;
auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get();
va_list args;
va_start(args, fmt);
vsprintf(buffer,fmt, args);
(logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::info, fmt, buffer);
va_end(args);
free(buffer);
}
Since printf
is a function that infers the type of its argument from the format string (first argument), it is a source of vulnerability. It is not a secure function.
spdlog does not support the printf
style. If you need such a function, you must implement it at your own risk.
is there some function in spdlog can let me pass the variable parameter into the function 'log' in spdlog like the function vsprintf
is there some function in spdlog can let me pass the variable parameter into the function 'log' in spdlog like the function vsprintf
No.
This is because the C++ template function performs type checking. Learning the C++ template will help you understand why.
if the function 'nclog_debug_fmt_char' works, then below codes maybe works too:
void nclog_info_fmt(const char *fmt, ...) { char *buffer = NULL; buffer = (char *)malloc(1024); if(buffer == NULL) return; auto logger = SLog::CLogger::getInstance()->getNConsoleLogger().get(); va_list args; va_start(args, fmt); vsprintf(buffer,fmt, args); (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::info, fmt, buffer); va_end(args); free(buffer); }
pls, this way does it can work?, and if i use this way, i must use ‘%d %s’ instead of ‘{}’ yes?
this way does it can work?, and if i use this way, i must use ‘%d %s’ instead of ‘{}’ yes?
If fmt
contains {}
, buffer
will be output, but buffer
is empty. And va_args
will not work.
That would not be the behavior you expected.
As I have told you several times, it is impossible to pass logging arguments to spdlog using variable parameter in C.
It is recommended that you develop a deeper understanding of C++.
OK. I know. thanks you
template<typename... Args>
using format_string_t = fmt::format_string<Args...>;
template<typename... Args>
inline void info(int level, const char* tag, const char* file, const char* func, const long line, format_string_t<Args...> fmt, Args &&... args)
{
std::string info = fmt::format("{} {} {} {} {}", tag, file, func, line, fmt::format(fmt, std::forward(args)...));
your_log_api(level, info);
}
这样试试?
#define elog_critical(tag, ...)
info(ELOG_LVL_CRITICAL, tag, FILE, FUNCTION, LINE, VA_ARGS)
- In this way, the performance is a little slower, with about 800000 disks falling per second.