Logging is a complex issue and what it does/how you use it depends greatly on the needs of your application.
In small applications, I tend to use either injected std::ostream references, or custom code for logging specifically. I also stay away from the C formatted printing APIs (and define my own operator<<
for things I need to trace).
In large applications, if you have complex tracing needs (rotate log files between executions, logs per category and configurable from outside the application, automatic log formatting, high-throughput/performance logging, and so on) use an external library (like log4cpp).
I also tend to use/define macros, only after all my code can be written without them.
Example implementation with injected logging stream:
#include<iosfwd>
class http_server {
public:
http_server(std::string server, std::uint16_t listening_port,
std::ostream& log = cnull); // cnull is as defined at
// http://stackoverflow.com/a/6240980/186997
private:
std::ostream& log_;
};
Main (console) code:
int main(...) {
http_server("localhost", 8080, std::clog);
}
Unit test code:
std::ostringstream server_log;
http_server("localhost", 8080, server_log);
// assert on the contents of server_log
Basically, I consider that if I need tracing, it is a part of the API interface and not an afterthought, something I would disable through macros, or something to hardcode.
If I need formatted logging, I would consider specializing a std::ostream, or (most probably) wrapping it into a specialized formatting class and injecting that around.
Macros for tracing code are usually reserved for performance-critical code (where you cannot afford to call stuff if it doesn't do anything).