/* tabs = 4 */ /* Logger.h Dwight Tuinstra, July 2001. This code is contributed to the public domain. Do with it as you will. Try to do good. Not warranted to be secure, robust, or anything else. As the saying goes: if it breaks, you get to keep both pieces. Developed and tested using: * gcc version egcs-2.91.66 19990314 (egcs-1.1.2 release) * NetBSD 1.5.1 You may need to tweak the includes on other systems. TO DO: setting _opts to -1 is wrong approach. Fix this. TO DO: Clean up the vsnprintf stuff --- use something cleaner. TO DO: Other things as marked in code/comments below. */ #ifndef _LOGGER_H_ #define _LOGGER_H_ #include /* pid_t */ #include #include /* var arg stuff */ #include /* vsnprintf */ #include /* time, ctime */ #include /* gethostname, get_pid */ #include /* ofstream */ #include namespace logger { /* for the 'n' in vsnprintf */ const int MAX_STRLEN = 200; /* Wrap the openlog and syslog functions for logging via syslog, and emulate them for logging to an ostream. Usage (as syslog wrapper): 1) Declare a Logger object, with arguments in the order you would use in an openlog() call. Optionally, you can add a fourth parameter to indicate the default level to use (this defaults to LOG_WARNING). NOTE: The LOG_PID option is a no-op --- Logger uses this option whether or not it is specified. 2) Use the declared object as if it were a function. Give it either a format string and (a variable number of) args; or a level, a format strings, and args. As with syslog, newlines don't need to be supplied. They are automatically generated. Example: #include "logger.h" using logger::Logger; [or "using namespace logger;"] Logger log("fobObj", LOG_CONS, LOG_USER, LOG_WARNING); log("%s%d", "fob resources low. Still avail: ", fobcount); log(LOG_ALERT, "%s%d", "fobs exhausted. Deficit: ", fobiou); log(LOG_EMERG, "Fob credit exhausted!! Get out while you can!!"); Usage (log to ostream): 1) As above, but insert an additional first argument indicating an *already opened* ostream. Alternatively, use no arguments (ie, invoke default constructor), in which case logging is done to cerr. Logger objects mimic syslog when writing to the ostream: they write the time, hostname, prefix, and pid automatically with each message. The "syslog-specific" fields are handled as follows. options: ignored TO DO: implement LOG_PERROR and LOG_CONS. facility: ignored TO DO: if non-negative, the facility's name is written TO DO: just after the pid. level: ignored TO DO: if non-negative, the level's name is written just TO DO: after the facility name. 2) Use as with (2) above. Example: #include "logger.h" using logger::Logger; [or "using namespace logger;"] Logger logfile(myostream, "fobObj"); Logger logcerr(cerr, "fobObj"); logfile("%s%d", "fob resources low. Still avail: ", fobcount); logcerr("%s%d", "fobs exhausted. Deficit: ", fobiou); logcerr("Fob credit exhausted!! Get out while you can!!"); */ class Logger { public: enum log_dest { TO_SYSLOG, TO_OSTREAM }; Logger( /* log using syslog facility */ string prefix, int opts, int facility, int level = LOG_WARNING ): _os(cerr), _prefix(prefix), _opts(opts|LOG_PID), _facility(facility), _level(level), _dest(TO_SYSLOG) { /* empty body */ } Logger( /* log to an *already open* ostream */ ostream& os = cerr, string prefix = "", int opts = -1, int facility = -1, int level = -1 ): _os(os), _prefix(prefix), _opts(opts|LOG_PID), _facility(facility), _level(level), _dest(TO_OSTREAM) { /* empty body */ } Logger( /* copy, but prepend/append to the prefix string */ Logger & other, string prepend = "", string append = "" ): _os(other._os), _prefix(prepend + other._prefix + append), _opts(other._opts), _facility(other._facility), _level(other._level), _dest(other._dest) { /* empty body */ } void operator()(int priority, const char * fmt, ...) { va_list args; va_start(args, fmt); _do_log(priority, fmt, args); } void operator()(const char * fmt, ...) { va_list args; va_start(args, fmt); _do_log(_facility|_level, fmt, args); } protected: void _do_log(int priority, const char * fmt, va_list args) { switch(_dest) { case TO_SYSLOG: openlog(_prefix.c_str(), _opts, _facility); vsyslog(priority, fmt, args); closelog(); break; case TO_OSTREAM: /* get hostname. */ char hname[MAX_STRLEN]; if ( 0 != gethostname(hname, MAX_STRLEN)) strncpy(hname, "**Logger: gethostname FAILED**", MAX_STRLEN);; hname[MAX_STRLEN - 1] = '\0'; /* be safe */ string hstr(hname); int dotpos = hstr.find('.'); if( dotpos > 0 && string::npos != dotpos ) { /* found a dot in hstr, so get just hostname */ hstr = hstr.substr(0,dotpos); } /* construct a cstring from user-supplied args */ char str[MAX_STRLEN]; vsnprintf(str, MAX_STRLEN, fmt, args); int n = strlen(str) - 1; /* index of last char */ if ( n > 0 && '\n' == str[n]) /* if its a newline, */ str[n] = '\0'; /* remove it */ /* when are we? */ time_t now = time(NULL); char nowstr[26]; /* man ctime says its 26 */ ctime_r(&now, nowstr); nowstr[19] = '\0'; /* zap year and newline */ /* send everything to ostream */ _os << (nowstr + 4) << " " /* skip day of week */ << hstr << " " << _prefix << "[" << getpid() << "]: " << str << "\n"; break; } } private: string _prefix; int _opts; int _facility; int _level; ostream& _os; log_dest _dest; }; /* end class Logger */ #ifdef OBJ_SELF_TEST /* To use self test, do something like: #define OBJ_SELF_TEST #include "logger.h" int main() { logger::self_test(); return 0; } */ void _do_test(Logger& logger) { time_t now = time(NULL); char nowstr[26]; /* see ctime_r(3) */ ctime_r(&now, nowstr); nowstr[24] = ' '; /* zap newline */ logger("%s %s %c", "test", nowstr, 'X'); } void self_test() { ofstream ofs; ofs.open("test.out"); Logger flog1(ofs, "loggerOFST", LOG_CONS, LOG_USER, LOG_ERR); Logger flog2(flog1, "X", "Y"); Logger log1("loggerSYSL", LOG_CONS, LOG_USER, LOG_ERR); Logger log2(log1, "#", "*"); Logger deflog1(cerr, "loggerCERR"); Logger deflog2(deflog1, "*", "!"); _do_test(log1); _do_test(log2); _do_test(flog1); _do_test(flog2); _do_test(deflog1); _do_test(deflog2); sleep(3); _do_test(log1); _do_test(log2); _do_test(flog1); _do_test(flog2); _do_test(deflog1); _do_test(deflog2); } #endif /* OBJ_SELF_TEST */ } /* end namespace logger */ #endif /* !_LOGGER_H_ */