src/zm_logger.h
Rama master · réplica de lectura del repositorio original.
/*
* 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 <atomic>
#include <map>
#include <mutex>
#include <string>
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#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<Level, std::string> StringMap;
typedef std::map<Level, int> 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<bool> 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<int> 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