bitvis/src/util/log.cpp

197 lines
5.2 KiB
C++

/*
* boblight
* Copyright (C) Bob 2009
*
* boblight 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 3 of the License, or
* (at your option) any later version.
*
* boblight 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, see <http://www.gnu.org/licenses/>.
*/
#include <cstdarg>
#include <cstdio>
#include <fstream>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <sys/stat.h>
#include <time.h>
#include <sstream>
#include <vector>
#include <sys/time.h>
#include "log.h"
#include "mutex.h"
#include "lock.h"
#include "misc.h"
using namespace std;
bool g_logtostderr = true;
bool g_printlogtofile = true;
bool g_printdebuglevel = false;
string g_logfilename;
//as pointers, so we can allocate these after the locked memory is set up
static CMutex* g_logmutex;
static ofstream* g_logfile;
static int g_logbuffsize; //size of the buffer
static char* g_logbuff; //buffer for vsnprintf
//returns hour:minutes:seconds:microseconds
string GetStrTime()
{
struct timeval tv;
struct tm time;
time_t now;
//get current time
gettimeofday(&tv, NULL);
now = tv.tv_sec; //seconds since EPOCH
localtime_r(&now, &time); //convert to hours, minutes, seconds
char buff[16];
snprintf(buff, sizeof(buff), "%02i:%02i:%02i.%06i", time.tm_hour, time.tm_min, time.tm_sec, (int)tv.tv_usec);
return buff;
}
bool InitLog(string directory, string filename, ofstream& logfile)
{
string homepath;
if (!GetHomePath(homepath))
{
PrintError("Unable to get home directory path");
return false;
}
directory = homepath + directory;
string fullpath = directory + "/" + filename;
//try to make the directory the log goes in
if (mkdir(directory.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
{
//if it already exists we're ok
if (errno != EEXIST)
{
PrintError("unable to make directory " + directory + ":\n" + GetErrno());
return false;
}
}
//we keep around 5 old logfiles
for (int i = 4; i > 0; i--)
rename(string(fullpath + ".old." + ToString(i)).c_str(), string(fullpath + ".old." + ToString(i + 1)).c_str());
rename(fullpath.c_str(), string(fullpath + ".old.1").c_str());
//open the logfile in append mode
logfile.open(fullpath.c_str());
if (logfile.fail())
{
PrintError("unable to open " + fullpath + ":\n" + GetErrno());
return false;
}
Log("successfully set up logfile %s", fullpath.c_str());
g_logfilename = fullpath;
return true;
}
//we only want the function name and the namespace, so we search for '(' and get the string before that
string PruneFunction(string function)
{
size_t parenpos = function.find('(');
size_t spacepos = function.rfind(' ', parenpos);
if (parenpos == string::npos)
return function;
else if (spacepos == string::npos) //when called from a constructor, there's no return value, thus no space
return function.substr(0, parenpos);
else
return function.substr(spacepos + 1, parenpos - spacepos - 1);
}
void SetLogFile(const char* directory, const char* filename)
{
if (!g_logmutex)
g_logmutex = new CMutex;
CLock lock(*g_logmutex);
if (!g_logfile)
g_logfile = new ofstream;
//deallocate, so it will be allocated in PrintLog gain, but this time in locked memory
free(g_logbuff);
g_logbuff = NULL;
g_logbuffsize = 0;
if (!InitLog(directory, filename, *g_logfile))
g_printlogtofile = false;
}
void PrintLog (const char* fmt, const char* function, LogLevel loglevel, ...)
{
if (loglevel == LogLevelDebug && !g_printdebuglevel)
return;
if (g_logmutex)
g_logmutex->Lock();
string logstr;
string funcstr;
va_list args;
int nrspaces;
va_start(args, loglevel);
//print to the logbuffer and check if our buffer is large enough
int neededbuffsize = vsnprintf(g_logbuff, g_logbuffsize, fmt, args);
if (neededbuffsize + 1 > g_logbuffsize)
{
g_logbuffsize = neededbuffsize + 1;
g_logbuff = (char*)(realloc(g_logbuff, g_logbuffsize)); //resize the buffer to the needed size
va_end(args); //need to reinit or we will crash
va_start(args, loglevel);
vsnprintf(g_logbuff, g_logbuffsize, fmt, args); //write to the buffer again
}
va_end(args);
if (loglevel == LogLevelError)
logstr += "ERROR: ";
else if (loglevel == LogLevelDebug)
logstr += "DEBUG: ";
if (g_logbuff)
logstr += g_logbuff;
funcstr = "(" + PruneFunction(function) + ")";
nrspaces = 34 - funcstr.length();
if (nrspaces > 0)
funcstr.insert(funcstr.length(), nrspaces, ' ');
//write the string to the logfile
if (g_logfile && g_logfile->is_open() && g_printlogtofile)
*g_logfile << GetStrTime() << " " << funcstr << " " << logstr << '\n' << flush;
//print to stdout when requested
if (g_logtostderr)
cerr << funcstr << logstr << '\n' << flush;
if (g_logmutex)
g_logmutex->Unlock();
}