privacore-open-source-searc.../Log.cpp
2018-08-07 15:44:56 +02:00

534 lines
14 KiB
C++

#include "Log.h"
#include "Mem.h"
#if defined(__linux__)
#include <sys/types.h>
#include <sys/syscall.h>
#endif
#include "Loop.h"
#include "Conf.h"
#include "Process.h"
#include "Hostdb.h"
#include "File.h"
#include "fctypes.h"
#include "utf8_fast.h"
#include "GbMutex.h"
#include "ScopedLock.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
// a global class extern'd in Log.h
Log g_log;
//'s_lock' really should be a nice instance but due to the undefined destruction
//order of global objects we can easily end up using it after it has been destroyed
//so for now we just use a plain old pthread_mtuex_t and never destroy it.
//static GbMutex s_lock;
static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER;
Log::Log () {
m_fd = -1;
m_filename = NULL;
m_disabled = false;
m_logTimestamps = true;
m_logReadableTimestamps = true;
m_logPrefix = true;
}
Log::~Log () {
//reset(); }
if ( m_fd >= 0 ) ::close ( m_fd );
}
// close file if it's open
void Log::reset ( ) {
// comment this out otherwise we dont log the memleaks in Mem.cpp!!
//if ( m_fd >= 0 ) ::close ( m_fd );
}
// for example, RENAME log000 to log000-bak20131104-181932
static bool renameCurrentLogFile ( ) {
File f;
char tmp[16];
sprintf(tmp,"log%03" PRId32,g_hostdb.m_myHostId);
f.set ( g_hostdb.m_dir , tmp );
// make new filename like log000-bak20131104-181932
time_t now = time(NULL);
struct tm tm_buf;
tm *tm1 = gmtime_r(&now,&tm_buf);
char tmp2[64];
strftime(tmp2,64,"%Y%m%d-%H%M%S",tm1);
SafeBuf newName;
if ( ! newName.safePrintf ( "%slog%03" PRId32"-bak%s",
g_hostdb.m_dir,
g_hostdb.m_myHostId,
tmp2 ) ) {
fprintf(stderr,"log rename failed\n");
return false;
}
// rename log000 to log000-2013_11_04-18:19:32
if ( f.doesExist() ) {
//fprintf(stdout,"renaming file\n");
f.rename ( newName.getBufStart() );
}
return true;
}
bool Log::init ( const char *filename ) {
// init these
m_fd = -1;
m_disabled = false;
// is there a filename to log our errors to?
m_filename = filename;
if ( ! m_filename ) return true;
//
// RENAME log000 to log000-20131104-181932
//
if ( g_conf.m_logToFile ) {
// returns false on error
if ( ! renameCurrentLogFile() ) return false;
}
if ( strcmp(m_filename,"/dev/stderr") == 0 ) {
m_fd = STDERR_FILENO; // 2; // stderr
return true;
}
// open it for appending.
// create with -rw-rw-r-- permissions if it's not there.
m_fd = open ( m_filename ,
O_APPEND | O_CREAT | O_RDWR ,
getFileCreationFlags() );
if ( m_fd >= 0 ) return true;
// bitch to stderr and return false on error
fprintf(stderr,"could not open log file %s for appending\n",
m_filename);
return false;
}
static const char *getTypeString ( int32_t type ) ;
const char *getTypeString ( int32_t type ) {
switch ( type ) {
case LOG_INFO : return "INF";
case LOG_WARN : return "WRN";
case LOG_ERROR : return "ERR";
case LOG_TRACE : return "TRC";
case LOG_LOGIC : return "LOG";
case LOG_REMIND: return "REM";
case LOG_DEBUG : return "DBG";
case LOG_TIMING: return "TIM";
case LOG_INIT : return "INI";
case LOG_LIMIT : return "LIM";
default: return "UNK";
}
}
#define MAX_LINE_LEN 20048
bool Log::shouldLog ( int32_t type , const char *msg ) {
// always log errors/warnings/logic/trace (if trace gets through to here, it is enabled)
if ( (type == LOG_WARN) || (type == LOG_LOGIC) || (type == LOG_ERROR) || (type == LOG_TRACE)) {
return true;
}
if ( type == LOG_INFO ) return g_conf.m_logInfo;
if ( type == LOG_LIMIT ) return g_conf.m_logLimits;
if ( type == LOG_REMIND ) return g_conf.m_logReminders ;
if ( type == LOG_TIMING ) {
if ( msg[0] == 'a' && msg[2] == 'd' )
return g_conf.m_logTimingAddurl;
if ( msg[0] == 'a' && msg[2] == 'm' )
return g_conf.m_logTimingAdmin;
if ( msg[0] == 'b' ) return g_conf.m_logTimingBuild;
if ( msg[0] == 'd' ) return g_conf.m_logTimingDb;
if ( msg[0] == 'n' ) return g_conf.m_logTimingNet;
if ( msg[0] == 'q' ) return g_conf.m_logTimingQuery;
if ( msg[0] == 'r' ) return g_conf.m_logTimingRobots;
if ( msg[0] == 'l' ) return g_conf.m_logTimingLinkInfo;
return false;
}
if ( type != LOG_DEBUG ) return true;
if (msg[0]=='a'&&msg[2]=='d' ) return g_conf.m_logDebugAddurl ;
if (msg[0]=='a'&&msg[2]=='m' ) return g_conf.m_logDebugAdmin ;
if (msg[0]=='b'&&msg[1]=='u' ) return g_conf.m_logDebugBuild ;
if (msg[0]=='d'&&msg[1]=='b' ) return g_conf.m_logDebugDb ;
if (msg[0]=='d'&&msg[1]=='i' ) return g_conf.m_logDebugDisk ;
if (msg[0]=='d'&&msg[1]=='n' ) return g_conf.m_logDebugDns ;
if (msg[0]=='d'&&msg[1]=='o' ) return g_conf.m_logDebugDownloads;
if (msg[0]=='h'&&msg[1]=='t' ) return g_conf.m_logDebugHttp ;
if (msg[0]=='i'&&msg[1]=='m' ) return g_conf.m_logDebugImage ;
if (msg[0]=='l'&&msg[1]=='o' ) return g_conf.m_logDebugLoop ;
if (msg[0]=='l'&&msg[1]=='a' ) return g_conf.m_logDebugLang ;
if (msg[0]=='m'&&msg[2]=='m' ) return g_conf.m_logDebugMem ;
if (msg[0]=='m'&&msg[2]=='r' ) return g_conf.m_logDebugMerge ;
if (msg[0]=='n'&&msg[1]=='e' ) return g_conf.m_logDebugNet ;
if (msg[0]=='q'&&msg[1]=='u'&&msg[2]=='e' )
return g_conf.m_logDebugQuery ;
if (msg[0]=='r'&&msg[1]=='o' ) return g_conf.m_logDebugRobots ;
if (msg[0]=='s'&&msg[1]=='e' ) return g_conf.m_logDebugSEO;
if (msg[0]=='s'&&msg[2]=='e' ) return g_conf.m_logDebugSpeller;
if (msg[0]=='s'&&msg[2]=='a' ) return g_conf.m_logDebugStats ;
if (msg[0]=='s'&&msg[1]=='u' ) return g_conf.m_logDebugSummary;
if (msg[0]=='s'&&msg[2]=='i' ) return g_conf.m_logDebugSpider ;
if (msg[0]=='t'&&msg[1]=='a' ) return g_conf.m_logDebugTagdb ;
if (msg[0]=='t'&&msg[1]=='c' ) return g_conf.m_logDebugTcp ;
if (msg[0]=='t'&&msg[1]=='i' ) return g_conf.m_logDebugTitle ;
if (msg[0]=='r'&&msg[1]=='e'&&msg[2]=='p' ) return g_conf.m_logDebugRepair ;
if (msg[0]=='r'&&msg[1]=='e'&&msg[2]=='i' ) return g_conf.m_logDebugReindex ;
if (msg[0]=='u'&&msg[1]=='d' ) return g_conf.m_logDebugUdp ;
if (msg[0]=='u'&&msg[1]=='n' ) return g_conf.m_logDebugUnicode;
if (msg[0]=='t'&&msg[1]=='o'&&msg[3]=='D' )
return g_conf.m_logDebugTopDocs;
if (msg[0]=='d'&&msg[1]=='a' ) return g_conf.m_logDebugDate;
if (msg[0]=='d'&&msg[1]=='d' ) return g_conf.m_logDebugDetailed;
return true;
}
static bool g_loggingEnabled = true;
// 1GB max log file size
#define MAXLOGFILESIZE 1000000000
bool Log::logR ( int64_t now, int32_t type, const char *msg, bool forced ) {
if ( ! g_loggingEnabled ) {
return true;
}
// return true if we should not log this
if ( ! forced && ! shouldLog ( type , msg ) ) {
return true;
}
// get "msg"'s length
int32_t msgLen = strlen ( msg );
ScopedLock sl(s_lock);
// do a timestamp, too. use the time synced with host #0 because
// it is easier to debug because all log timestamps are in sync.
if ( now == 0 ) now = gettimeofdayInMilliseconds();
// chop off any spaces at the end of the msg.
while ( is_wspace_a ( msg [ msgLen - 1 ] ) && msgLen > 0 ) msgLen--;
// a tmp buffer
char tt [ MAX_LINE_LEN ];
char *p = tt;
if (m_logPrefix) {
if ( m_logTimestamps ) {
if( m_logReadableTimestamps ) {
time_t now_t = (time_t)(now / 1000);
struct tm tm_buf;
struct tm *stm = gmtime_r(&now_t,&tm_buf);
p += sprintf ( p , "%04d%02d%02d-%02d%02d%02d-%03d %04" PRId32" ", stm->tm_year+1900,stm->tm_mon+1,stm->tm_mday,stm->tm_hour,stm->tm_min,stm->tm_sec,(int)(now%1000), g_hostdb.m_myHostId );
} else {
if ( g_hostdb.getNumHosts() <= 999 )
p += sprintf ( p , "%" PRIu64 " %03" PRId32 " ", (uint64_t)now , g_hostdb.m_myHostId );
else if ( g_hostdb.getNumHosts() <= 9999 )
p += sprintf ( p , "%" PRIu64" %04" PRId32" ", (uint64_t)now , g_hostdb.m_myHostId );
else if ( g_hostdb.getNumHosts() <= 99999 )
p += sprintf ( p , "%" PRIu64" %05" PRId32" ", (uint64_t)now , g_hostdb.m_myHostId );
}
}
// Get thread id. pthread_self instead?
unsigned tid=(unsigned)syscall(SYS_gettid);
p += sprintf(p, "%06u ", tid);
// Log level
p += sprintf(p, "%s ", getTypeString(type));
}
// then message itself
const char *x = msg;
int32_t avail = (MAX_LINE_LEN) - (p - tt) - 1;
if ( msgLen > avail ) msgLen = avail;
if ( *x == ':' ) x++;
if ( *x == ' ' ) x++;
strncpy ( p , x , avail );
// capitalize for consistency. no, makes grepping log msgs harder.
//if ( is_alpha_a(*p) ) *p = to_upper_a(*p);
p += strlen(p);
// back up over spaces
while ( p[-1] == ' ' ) p--;
// end in period or ? or !
//if ( p[-1] != '?' && p[-1] != '.' && p[-1] != '!' )
// *p++ = '.';
*p ='\0';
// the total length, not including the \0
int32_t tlen = p - tt;
if ( m_fd >= 0 ) {
write ( m_fd , tt , tlen );
write ( m_fd , "\n", 1 );
} else {
// print it out for now
fprintf ( stderr, "%s\n", tt );
}
return false;
}
void Log::rotateLog(int fd, void *state) {
if (!g_conf.m_logToFile) {
return;
}
Log *that = static_cast<Log*>(state);
auto fileSize = getFileSize(that->m_filename);
// . if filesize would be too big then make a new log file
// . should make a new m_fd
if (fileSize > MAXLOGFILESIZE) {
that->makeNewLogFile();
}
}
bool Log::registerLogRotation() {
return g_loop.registerSleepCallback(10000, this, rotateLog, "Log::rotateLog");
}
bool Log::makeNewLogFile() {
ScopedLock sl(s_lock);
// prevent deadlock. don't log since we are in the middle of logging.
// otherwise, safebuf, which is used when renaming files, might
// call logR().
g_loggingEnabled = false;
// . rename old log file like log000 to log000-2013_11_04-18:19:32
// . returns false on error
bool status = renameCurrentLogFile();
// re-enable logging since nothing below should call logR() indirectly
g_loggingEnabled = true;
if (!status) {
return false;
}
// close old fd
if (m_fd >= 0) {
::close(m_fd);
}
// invalidate
m_fd = -1;
// open it for appending.
// create with -rw-rw-r-- permissions if it's not there.
m_fd = open(m_filename, O_APPEND | O_CREAT | O_RDWR, getFileCreationFlags());
if (m_fd >= 0) {
return true;
}
// bitch to stderr and return false on error
fprintf(stderr, "could not open new log file %s for appending\n", m_filename);
return false;
}
void log ( int32_t type , const char *formatString , ...) {
if ( g_log.m_disabled ) {
return;
}
// do not log it if we should not
if ( ! g_log.shouldLog ( type , formatString ) ) {
return;
}
// this is the argument list (variable list)
va_list ap;
// can we log if we're a sig handler? don't take changes
// print msg into this buf
char buf[1024*10];
// copy the error into the buffer space
va_start ( ap, formatString);
// print it into our buf now
vsnprintf ( buf , 1024*10 , formatString , ap );
va_end(ap);
// pass buf to g_log
g_log.logR ( 0, type, buf );
}
void log ( const char *formatString , ... ) {
if ( g_log.m_disabled ) {
return;
}
// do not log it if we should not
if ( ! g_log.shouldLog ( LOG_WARN , formatString ) ) {
return;
}
// this is the argument list (variable list)
va_list ap;
// can we log if we're a sig handler? don't take changes
// print msg into this buf
char buf[1024*10];
// copy the error into the buffer space
va_start ( ap, formatString);
// print it into our buf now
vsnprintf ( buf , 1024*10 , formatString , ap );
va_end(ap);
// pass buf to g_log
// ### BR 20151217: Default to DEBUG if no log level given
/// @todo ALC shouldn't this be LOG_WARN?
g_log.logR ( 0 , LOG_DEBUG , buf , false );
}
void logf ( int32_t type , const char *formatString , ...) {
if ( g_log.m_disabled ) {
return;
}
// this is the argument list (variable list)
va_list ap;
// can we log if we're a sig handler? don't take changes
// print msg into this buf
char buf[1024*10];
// copy the error into the buffer space
va_start ( ap, formatString);
// print it into our buf now
vsnprintf ( buf , 1024*10 , formatString , ap );
va_end(ap);
// pass buf to g_log
g_log.logR ( 0, type, buf, true );
}
static void hexdump(void const *data, const unsigned int len, char *dest, const int dest_len)
{
unsigned int i;
unsigned int r,c;
if (!data || len <= 0)
{
return;
}
int dest_used = 0;
char *destptr = dest;
char line[80]; // line length is actually 78 + null terminator
char *lptr;
for (r=0,i=0; (r<(len/16+(len%16!=0))) && (dest_len - (dest_used+80)>= 0); r++,i+=16)
{
lptr = line;
lptr += sprintf(lptr, "\n%04X: ",i);
for (c=i; c<i+8; c++) /* left half of hex dump */
{
if (c<len)
{
lptr += sprintf(lptr, "%02X ",((unsigned char const *)data)[c]);
}
else
{
lptr += sprintf(lptr, " "); /* pad if short line */
}
}
lptr += sprintf(lptr, " ");
for (c=i+8; c<i+16; c++) /* right half of hex dump */
{
if (c<len)
{
lptr += sprintf(lptr, "%02X ",((unsigned char const *)data)[c]);
}
else
{
lptr += sprintf(lptr, " "); /* pad if short line */
}
}
lptr += sprintf(lptr, " ");
for (c=i; c<i+16; c++) /* ASCII dump */
{
if (c<len)
{
if (((unsigned char const *)data)[c]>=32 &&
((unsigned char const *)data)[c]<127)
{
lptr += sprintf(lptr, "%c",((char const *)data)[c]);
}
else
{
lptr += sprintf(lptr, "."); /* put this for non-printables */
}
}
else
{
lptr += sprintf(lptr, " "); /* pad if short line */
}
}
destptr += sprintf(destptr, "%s", line);
dest_used = destptr - dest;
}
destptr += sprintf(destptr, "\n");
}
void loghex( int32_t type, void const *data, const unsigned int len, const char *formatString , ...) {
if ( g_log.m_disabled ) {
return;
}
// this is the argument list (variable list)
va_list ap;
// can we log if we're a sig handler? don't take changes
// print msg into this buf
char buf[1024*10];
// copy the error into the buffer space
va_start ( ap, formatString);
// print it into our buf now
vsnprintf ( buf , 1024*10 , formatString , ap );
va_end(ap);
int written = strlen(buf);
hexdump(data, len, &buf[written], (1024*10)-written);
// pass buf to g_log
g_log.logR ( 0 , type , buf );
}