/* * ZoneMinder Logger Interface * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef ZM_LOGGER_H #define ZM_LOGGER_H #include "zm_db.h" #include "zm_config.h" #include "zm_define.h" #include #include #include #include #ifdef HAVE_SYS_SYSCALL_H #include #endif class Logger { public: enum { NOOPT = -7, NOLOG, // -6 AUDIT, // -5 PANIC, // -4 FATAL, // -3 ERROR, // -2 WARNING, // -1 INFO, // 0 DEBUG1, DEBUG2, DEBUG3, DEBUG4, DEBUG5, DEBUG6, DEBUG7, DEBUG8, DEBUG9 }; typedef int Level; typedef std::map StringMap; typedef std::map IntMap; class Options { public: int mTerminalLevel; int mDatabaseLevel; int mFileLevel; int mSyslogLevel; std::string mLogPath; std::string mLogFile; Options( Level terminalLevel = NOOPT, Level databaseLevel = NOOPT, Level fileLevel = NOOPT, Level syslogLevel = NOOPT, const std::string &logPath = ".", const std::string &logFile = "" ) : mTerminalLevel(terminalLevel), mDatabaseLevel(databaseLevel), mFileLevel(fileLevel), mSyslogLevel(syslogLevel), mLogPath(logPath), mLogFile(logFile) { } }; private: static bool smInitialised; static Logger *smInstance; std::recursive_mutex log_mutex; static StringMap smCodes; static IntMap smSyslogPriorities; // Set by the SIGWINCH handler, acted on by the next logPrint. Reopening the // file is far too heavy to do in a signal handler. static std::atomic smDoLogRotate; // Net level change asked for by SIGUSR1 (+1) and SIGUSR2 (-1) since the // last log call. The handler only touches this; fetch() applies it. static std::atomic smLevelChange; // How many DatabaseLogPause are alive on this thread. static thread_local int smDatabaseLogPaused; bool mInitialised; std::string mId; std::string mIdRoot; std::string mIdArgs; Level mLevel; // Level that is currently in operation Level mTerminalLevel; // Maximum level output via terminal Level mDatabaseLevel; // Maximum level output via database Level mFileLevel; // Maximum level output via file Level mSyslogLevel; // Maximum level output via syslog Level mEffectiveLevel; // Level optimised to take account of maxima bool mDbConnected; std::string mLogPath; std::string mLogFile; FILE *mLogFileFP; bool mHasTerminal; bool mFlush; private: Logger(); ~Logger(); int limit(const int level) const { if ( level > DEBUG9 ) return DEBUG9; if ( level < NOLOG ) return NOLOG; return level; } bool boolEnv(const std::string &name, bool defaultValue = false); int intEnv(const std::string &name, bool defaultValue = 0); std::string strEnv( const std::string &name, const std::string &defaultValue = ""); char *getTargettedEnv(const std::string &name); void loadEnv(); static void usrHandler(int sig); void applyLevelChange(); public: friend void logInit(const char *name, const Options &options); friend void logReload(const char *name, const Options &options); friend void logTerm(); // While one of these is alive, lines logged on the thread that created it // are not written to the database. Every other thread keeps logging there. // // The database queue holds one around each insert into Logs: at a debug // database level, inserting a Logs row logs a line about that insert, which // would queue another insert, and so on without end. class DatabaseLogPause { public: DatabaseLogPause() { ++smDatabaseLogPaused; } ~DatabaseLogPause() { --smDatabaseLogPaused; } DatabaseLogPause(const DatabaseLogPause &) = delete; DatabaseLogPause &operator=(const DatabaseLogPause &) = delete; }; static Logger *fetch() { if ( !smInstance ) { smInstance = new Logger(); Options options; smInstance->initialise("undef", options); } if (smLevelChange.load(std::memory_order_relaxed)) smInstance->applyLevelChange(); return smInstance; } void initialise(const std::string &id, const Options &options); void reinitialise(const std::string &id, const Options &options); void terminate(); const std::string &id(const std::string &id); const std::string &id() const { return mId; } Level level() const { return mLevel; } Level level(Level = NOOPT); bool debugOn() const { return mEffectiveLevel >= DEBUG1; } Level terminalLevel(Level = NOOPT); Level databaseLevel(Level = NOOPT); Level fileLevel(Level = NOOPT); Level syslogLevel(Level = NOOPT); private: void logFile(const std::string &logFile); void openFile(); void closeFile(); void openSyslog(); void closeSyslog(); void closeDatabase(); public: void logPrint(bool hex, const char *filepath, int line, int level, const char *fstring, ...) __attribute__((format(printf, 6, 7))); }; void logInit( const char *name, const Logger::Options &options = Logger::Options() ); // For a configuration reload while other threads may be logging: keeps the // existing instance instead of deleting and recreating it as logInit() does. void logReload( const char *name, const Logger::Options &options = Logger::Options() ); void logTerm(); inline const std::string &logId() { return Logger::fetch()->id(); } inline Logger::Level logLevel() { return Logger::fetch()->level(); } inline Logger::Level logDebugging() { return Logger::fetch()->debugOn(); } #define logPrintf(logLevel, params...) \ do { \ Logger *log = Logger::fetch(); \ if (logLevel <= log->level()) { \ log->logPrint(false, __FILE__, __LINE__, logLevel, ##params); \ } \ } while (0) #define logHexdump(logLevel, data, len) \ do { \ Logger *log = Logger::fetch(); \ if (logLevel <= log->level()) { \ log->logPrint(true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len);\ } \ } while (0) /* Debug compiled out */ #ifndef DBG_OFF #define Debug(level, params...) logPrintf(level, ##params) #define Hexdump(level, data, len) logHexdump(level, data, len) #else #define Debug(level, params...) #define Hexdump(level, data, len) #endif /* Standard debug calls */ #define Info(params...) logPrintf(Logger::INFO, ##params) #define Warning(params...) logPrintf(Logger::WARNING, ##params) #define Error(params...) logPrintf(Logger::ERROR, ##params) #define Fatal(params...) logPrintf(Logger::FATAL, ##params) #define Panic(params...) logPrintf(Logger::PANIC, ##params) #define Audit(params...) logPrintf(Logger::AUDIT, ##params) #define Mark() Info("Mark/%s/%d", __FILE__, __LINE__) #define Log() Info("Log") #ifdef __GNUC__ #define Enter(level) logPrintf(level, ("Entering %s", __PRETTY_FUNCTION__)) #define Exit(level) logPrintf(level, ("Exiting %s", __PRETTY_FUNCTION__)) #else #define Enter(level) #define Exit(level) #endif #endif // ZM_LOGGER_H