Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to log source file name, called function and line number where logging occurs? #235

Closed
pmalek opened this issue Jun 30, 2016 · 15 comments

Comments

@pmalek
Copy link

pmalek commented Jun 30, 2016

I am wondering whether it is possible source file name, called function and line number where logging occurs.

Something like:

2016-06-20 11:00:02 main.cpp:20 MyClass::MyClass() log message

Is it possible?

@gabime
Copy link
Owner

gabime commented Jun 30, 2016

You can use the SPDLOG_TRACE macro (and define SPDLOG_TRACE_ON in tweakme.h or before including spdlog.h)

SPDLOG_TRACE(file_logger , "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);

@pmalek
Copy link
Author

pmalek commented Jun 30, 2016

Ahh ok but as I see you use the following formatting:

#ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)  << " (" << __FILE__ << " #" << __LINE__ <<")";
#else
#define SPDLOG_DEBUG(logger, ...)
#endif

so the filename, line number and function names are logged after the passed in formatting/arguments.

Is it somehow possible to write it in the following format?

2016-06-20 11:00:02 main.cpp:MyFunctionName:124 log message on some important stuff

@pmalek
Copy link
Author

pmalek commented Jun 30, 2016

I have managed to write something like this

#define SPDLOG_DEBUG(logger, ...) \
  { \
  std::string s; \
  s.reserve(64); \
  s.append(__FILE__); \
  s.append("#"); \
  s.append(std::to_string(__LINE__)); \
  s.append(": "); \
  logger->debug(s + __VA_ARGS__); \
  } \

but this only works for regular strings without any formatting argument because of obvious reasons.

@gabime
Copy link
Owner

gabime commented Jun 30, 2016

right. there is no obvious way for this specific format

@gabime gabime closed this as completed Jul 9, 2016
@pmalek
Copy link
Author

pmalek commented Jul 11, 2016

Just as a reference I have managed to achieve this with the following macros:

#define SPDLOG_DEBUGF(logger, fmt, ...) \
{ \
logger->debug("{}::{}()#{}: " fmt, __FILE__ , __FUNCTION__, __LINE__, __VA_ARGS__); \
}

#define SPDLOG_DEBUG(logger, str) \
{ \
logger->debug("{}::{}()#{}: ", __FILE__ , __LINE__, str); \
}

@gabime
Copy link
Owner

gabime commented Jul 11, 2016

@pmalek looks good. i think i will replace the present macro implementation with this one..

@pmalek
Copy link
Author

pmalek commented Jul 11, 2016

Cool. If you decide you can let me know: I'll be more than happy to submit a PR for that :)

@gabime
Copy link
Owner

gabime commented Jul 11, 2016

sure. please submit a pr if you can

@pmalek
Copy link
Author

pmalek commented Jul 12, 2016

As a second thought I believe that the current solution:

#define SPDLOG_TRACE(logger, ...)  logger->trace(__FILE__ ## " line " ## SPDLOG_STR(__LINE__) ## ": " ## __VA_ARGS__);

is better than:

#define SPDLOG_TRACEF(logger, fmt, ...) logger->trace("{}::{}()#{}: " fmt, __FILE__ , __FUNCTION__, __LINE__, __VA_ARGS__);
#define SPDLOG_TRACE(logger, str) logger->trace("{}::{}()#{}: {}", __FILE__ , __FUNCTION__, __LINE__, str);

Since (not taking into account style/format):

  • there is only one macro for both cases: with and without formatting
  • it is possible to use formatting with it like in the example in the comment
SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2);

So I believe (if you want to keep the current formatting - e.g. not printing the __FUNCTION__) we can leave it as it is.

@gabime
Copy link
Owner

gabime commented Jul 12, 2016

ok. thanks for the update

@fala13
Copy link

fala13 commented Oct 10, 2018

This works while preserving formatting. Trick is to manually call the fmt::format on the original arguments:
#define SPDLOG_TRACE(logger, ...) logger->trace("{}::{}()#{}: ", __FILE__ , __FUNCTION__, __LINE__, fmt::format(__VA_ARGS__));

@gabime
Copy link
Owner

gabime commented Oct 10, 2018

Thanks. It's might be expensive though.. fmt::format(__VA_ARGS__) would be executed even if the log level is not trace..

@fala13
Copy link

fala13 commented Oct 10, 2018

Yes, this is a concern. With such macros one can limit argument expansion/execution by adding an if on the log level. Otherwise such code bears unnecessary cost as well if log level is not trace:
SPDLOG_TRACE(logger, dumpWholeDbToString());

The previous version
#define SPDLOG_TRACE(logger, ...) logger->trace(__FILE__ ## " line " ## SPDLOG_STR(__LINE__) ## ": " ## __VA_ARGS__);
works only if __VA_ARGS__ starts with something that can be expanded by preprocessor to string literal.

So full answer could be
#define SPDLOG_TRACE(logger, ...) if (logger->should_log(level::trace)){logger->trace("{}::{}()#{}: ", __FILE__ , __FUNCTION__, __LINE__, fmt::format(__VA_ARGS__));}

The additional level check doesn't look nice, but at least is cheap and will pay for itself on a large project.

Leaving it here just for reference. I got here looking for this answer as well, so maybe it will help someone.

@eudoxos
Copy link
Contributor

eudoxos commented Nov 4, 2019

The last macro (thanks, useful!) does not behave intuitively when branching, like if(cond) SPDLOG_TRACE(...) else ... so I used ternary operator to make it one statement:

#define SPDLOG_TRACE(logger,...) (logger->should_log(level::trace))?logger->trace("{}::{}()#{}: ",__FILE__,__FUNCTION__,__LINE__,fmt::format(__VA_ARGS)):(void)0)

Maybe someone finds it useful.

@Jackrekirby
Copy link

Jackrekirby commented Sep 5, 2020

Macro overloading is a possible solution to the problem of requiring two different macros depending on whether formatting is used. E.g.

LOG_DEBUG_1(5);
LOG_DEBUG_2("{}", 5);

Below is a method to overload macros based on the number of input arguments, adapted from: https://rextester.com/ONP80107 . This solution currently works for up to 9 arguments, and could easily be expanded. E.g.

LOG_DEBUG("{} {} {} {} {} {} {} {} {}", 1, 2, 3, 4, 5, 6, 7, 8, 9);

Solution:

// General utility macro
#define PP_CAT( A, B ) A ## B
#define PP_EXPAND(...) __VA_ARGS__

// Macro overloading feature support
#define PP_VA_ARG_SIZE(...) PP_EXPAND(PP_APPLY_ARG_N((PP_ZERO_ARGS_DETECT(__VA_ARGS__), PP_RSEQ_N)))

#define PP_ZERO_ARGS_DETECT(...) PP_EXPAND(PP_ZERO_ARGS_DETECT_PREFIX_ ## __VA_ARGS__ ## _ZERO_ARGS_DETECT_SUFFIX)
#define PP_ZERO_ARGS_DETECT_PREFIX__ZERO_ARGS_DETECT_SUFFIX ,,,,,,,,,,,0

#define PP_APPLY_ARG_N(ARGS) PP_EXPAND(PP_ARG_N ARGS)
#define PP_ARG_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N,...) N
#define PP_RSEQ_N 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0

#define PP_OVERLOAD_SELECT(NAME, NUM) PP_CAT( NAME ## _, NUM)
#define PP_MACRO_OVERLOAD(NAME, ...) PP_OVERLOAD_SELECT(NAME, PP_VA_ARG_SIZE(__VA_ARGS__))(__VA_ARGS__)

// Usage of overloaded macro
#define LOG_DEBUG(...)       PP_MACRO_OVERLOAD(LOG_DEBUG, __VA_ARGS__)
#define LOG_DEBUG_0()        
#define LOG_DEBUG_1(...) logger->debug("{}:{} {}() {}", __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define LOG_DEBUG_2(fmt, ...) logger->debug("{}:{} {}() {}" fmt, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)

Examples:

LOG_DEBUG(5); //will call LOG_DEBUG_1()
LOG_DEBUG("a = {}", 5); //will call LOG_DEBUG_2()
LOG_DEBUG("a = {}, b = {}", 3, 7); //will call LOG_DEBUG_2()
...
LOG_DEBUG("{} {} {} {} {} {} {} {} {}", 1, 2, 3, 4, 5, 6, 7, 8, 9);  //will call LOG_DEBUG_2()
LOG_DEBUG("{} {} {} {} {} {} {} {} {} {}", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  //would fail as not implemented.

bachittle pushed a commit to bachittle/spdlog that referenced this issue Dec 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants