461 lines
9.7 KiB
C++
461 lines
9.7 KiB
C++
/* Copyright (C) 2006 J.F.Dockes
|
|
* 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.,
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
#ifndef TEST_DEBUGLOG
|
|
|
|
#define __USE_GNU
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef INCLUDE_NEW_H
|
|
#include <new.h>
|
|
#endif
|
|
|
|
#include <string>
|
|
#include <set>
|
|
using std::set;
|
|
using std::string;
|
|
|
|
#include "debuglog.h"
|
|
#include "pathut.h"
|
|
#include "smallut.h"
|
|
|
|
#ifndef freeZ
|
|
#define freeZ(X) {if (X) {free(X);X=0;}}
|
|
#endif
|
|
|
|
#ifndef NO_NAMESPACES
|
|
using namespace std;
|
|
namespace DebugLog {
|
|
|
|
#endif // NO_NAMESPACES
|
|
|
|
bool DebugLog::isspecialname(const char *logname)
|
|
{
|
|
return !strcmp(logname, "stdout") || !strcmp(logname, "stderr");
|
|
}
|
|
|
|
class DebugLogWriter {
|
|
public:
|
|
virtual ~DebugLogWriter() {}
|
|
virtual int put(const char *s) = 0;
|
|
};
|
|
|
|
class DLFWImpl;
|
|
class DebugLogFileWriter : public DebugLogWriter {
|
|
DLFWImpl *impl;
|
|
public:
|
|
DebugLogFileWriter();
|
|
~DebugLogFileWriter();
|
|
virtual const char *getfilename();
|
|
virtual int setfilename(const char *fname, int trnc = 1);
|
|
virtual int put(const char *s);
|
|
};
|
|
|
|
class DLFWImpl {
|
|
char *filename;
|
|
FILE *fp;
|
|
int truncate;
|
|
public:
|
|
// Open output file if needed, return 0 if ok
|
|
void maybeopenfp() {
|
|
if (fp)
|
|
return;
|
|
if (filename == 0)
|
|
return;
|
|
if (!strcmp(filename, "stdout")) {
|
|
fp = stdout;
|
|
} else if (!strcmp(filename, "stderr")) {
|
|
fp = stderr;
|
|
} else {
|
|
fp = fopen(filename, (truncate) ? "w" : "a");
|
|
if (fp) {
|
|
setvbuf(fp, 0, _IOLBF, 0);
|
|
#ifdef O_APPEND
|
|
{
|
|
int flgs = 0;
|
|
fcntl(fileno(fp), F_GETFL, &flgs);
|
|
fcntl(fileno(fp), F_SETFL, flgs|O_APPEND);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void maybeclosefp() {
|
|
#ifdef DEBUGDEBUG
|
|
fprintf(stderr, "DebugLogImpl::maybeclosefp: filename %p, fp %p\n",
|
|
filename, fp);
|
|
#endif
|
|
// Close current file if open, and not stdout/stderr
|
|
if (fp && (filename == 0 ||
|
|
(strcmp(filename, "stdout") &&
|
|
strcmp(filename, "stderr")))) {
|
|
fclose(fp);
|
|
}
|
|
fp = 0;
|
|
freeZ(filename);
|
|
}
|
|
|
|
public:
|
|
|
|
DLFWImpl() : filename(0), fp(0), truncate(1) {
|
|
setfilename("stderr", 0);
|
|
}
|
|
~DLFWImpl() {
|
|
maybeclosefp();
|
|
}
|
|
int setfilename(const char *fn, int trnc) {
|
|
maybeclosefp();
|
|
filename = strdup(fn);
|
|
truncate = trnc;
|
|
return 0;
|
|
}
|
|
const char *getfilename() {
|
|
return filename;
|
|
}
|
|
int put(const char *s) {
|
|
maybeopenfp();
|
|
if (fp)
|
|
return fputs(s, fp);
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
DebugLogFileWriter::DebugLogFileWriter()
|
|
{
|
|
impl = new DLFWImpl;
|
|
}
|
|
|
|
DebugLogFileWriter::~DebugLogFileWriter()
|
|
{
|
|
delete impl;
|
|
}
|
|
|
|
int DebugLogFileWriter::setfilename(const char *fn, int trnc) {
|
|
return impl ? impl->setfilename(fn, trnc) : -1;
|
|
}
|
|
|
|
const char *DebugLogFileWriter::getfilename()
|
|
{
|
|
return impl ? impl->getfilename() : 0;
|
|
}
|
|
|
|
int DebugLogFileWriter::put(const char *s)
|
|
{
|
|
return impl ? impl->put(s) : -1;
|
|
};
|
|
|
|
static set<string> yesfiles;
|
|
static void initfiles()
|
|
{
|
|
const char *cp = getenv("DEBUGLOG_FILES");
|
|
if (!cp)
|
|
return;
|
|
vector<string> files;
|
|
stringToTokens(cp, files, ",");
|
|
yesfiles.insert(files.begin(), files.end());
|
|
}
|
|
static bool fileInFiles(const string& file)
|
|
{
|
|
string sf = path_getsimple(file);
|
|
if (yesfiles.find(sf) != yesfiles.end()) {
|
|
//fprintf(stderr, "Debug ON: %s \n", file.c_str());
|
|
return true;
|
|
}
|
|
//fprintf(stderr, "Debug OFF: %s \n", file.c_str());
|
|
return false;
|
|
}
|
|
|
|
#ifdef _WINDOWS
|
|
#include <windows.h>
|
|
static void datestring(char *d, int sz) {
|
|
SYSTEMTIME buf;
|
|
GetLocalTime(&buf);
|
|
int year = buf.wYear % 100;
|
|
|
|
snprintf(d, sz, "%02d%02d%02d%02d%02d%02d", year, int(buf.wMonth),
|
|
int(buf.wDay), int(buf.wHour), int(buf.wMinute), int(buf.wSecond));
|
|
}
|
|
#define vsnprintf _vsnprintf
|
|
|
|
#else // !WINDOWS ->
|
|
|
|
#include <time.h>
|
|
static void datestring(char *d, int sz)
|
|
{
|
|
struct tm *tmp;
|
|
time_t tim = time((time_t)0);
|
|
tmp = localtime(&tim);
|
|
int year = tmp->tm_year % 100;
|
|
snprintf(d, sz, "%02d%02d%02d%02d%02d%02d", year, tmp->tm_mon+1,
|
|
tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
|
|
}
|
|
|
|
#endif // !WINDOWS
|
|
|
|
void
|
|
DebugLog::prolog(int lev, const char *f, int line)
|
|
{
|
|
if (!writer)
|
|
return;
|
|
if (!yesfiles.empty() && !fileInFiles(f)) {
|
|
fileyes = false;
|
|
return;
|
|
} else {
|
|
fileyes = true;
|
|
}
|
|
if (dodate) {
|
|
char dts[100];
|
|
datestring(dts, 100);
|
|
writer->put(dts);
|
|
}
|
|
char buf[100];
|
|
sprintf(buf, ":%d:", lev);
|
|
writer->put(buf);
|
|
#if DEBUGLOG_SHOW_PID
|
|
sprintf(buf, "%d:", getpid());
|
|
writer->put(buf);
|
|
#endif
|
|
#if DEBUGLOG_SHOW_THREAD
|
|
sprintf(buf, "%lx:", (unsigned long)pthread_self());
|
|
writer->put(buf);
|
|
#endif
|
|
writer->put(f);
|
|
sprintf(buf, ":%d:", line);
|
|
writer->put(buf);
|
|
}
|
|
|
|
void
|
|
DebugLog::log(const char *s ...)
|
|
{
|
|
if (!writer || !fileyes)
|
|
return;
|
|
va_list ap;
|
|
va_start(ap,s);
|
|
|
|
#ifdef HAVE_VASPRINTF_nono // not sure vasprintf is really such a great idea
|
|
char *buf;
|
|
vasprintf(&buf, s, ap);
|
|
if (buf) {
|
|
#else
|
|
char buf[4096];
|
|
// It's possible that they also wouldn't have vsnprintf but what then ?
|
|
vsnprintf(buf, 4096, s, ap);
|
|
{
|
|
#endif
|
|
writer->put(buf);
|
|
}
|
|
|
|
#ifdef HAVE_VASPRINTF_nono
|
|
if (buf)
|
|
free(buf);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
DebugLog::setloglevel(int lev)
|
|
{
|
|
debuglevel = lev;
|
|
while (!levels.empty())
|
|
levels.pop();
|
|
pushlevel(lev);
|
|
}
|
|
|
|
void DebugLog::pushlevel(int lev)
|
|
{
|
|
debuglevel = lev;
|
|
levels.push(lev);
|
|
}
|
|
|
|
void DebugLog::poplevel()
|
|
{
|
|
if (levels.empty())
|
|
debuglevel = 0;
|
|
if (levels.size() > 1)
|
|
levels.pop();
|
|
debuglevel = levels.top();
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Global functions
|
|
//////////////////////////////////////
|
|
static DebugLogFileWriter lwriter;
|
|
static DebugLogFileWriter *theWriter = &lwriter;
|
|
const char *getfilename()
|
|
{
|
|
return theWriter ? theWriter->getfilename() : 0;
|
|
}
|
|
int setfilename(const char *fname, int trnc)
|
|
{
|
|
return theWriter ? theWriter->setfilename(fname, trnc) : -1;
|
|
}
|
|
|
|
#if DEBUGLOG_USE_THREADS
|
|
#include <pthread.h>
|
|
static pthread_key_t dbl_key;
|
|
static pthread_once_t key_once = PTHREAD_ONCE_INIT;
|
|
|
|
static void thrdatadel(void *data)
|
|
{
|
|
// fprintf(stderr, "DebugLog:: thrdatadel: %p\n", data);
|
|
DebugLog *dbl = (DebugLog *)data;
|
|
delete dbl;
|
|
pthread_setspecific(dbl_key, 0);
|
|
}
|
|
static void once_routine(void)
|
|
{
|
|
int status;
|
|
status = pthread_key_create(&dbl_key, thrdatadel);
|
|
if (status != 0) {
|
|
fprintf(stderr, "debuglog: cant initialize pthread "
|
|
"thread private storage key\n");
|
|
abort();
|
|
}
|
|
}
|
|
|
|
DebugLog *getdbl()
|
|
{
|
|
int status = pthread_once(&key_once, once_routine);
|
|
if (status != 0) {
|
|
fprintf(stderr, "debuglog: cant initialize pthread "
|
|
"thread private storage key (pthread_once)\n");
|
|
abort();
|
|
}
|
|
DebugLog *dbl;
|
|
if (!(dbl = (DebugLog *)pthread_getspecific(dbl_key))) {
|
|
if ((dbl = new DebugLog) == 0) {
|
|
fprintf(stderr, "debuglog: new DebugLog returned 0! ");
|
|
abort();
|
|
}
|
|
|
|
dbl->setwriter(theWriter);
|
|
initfiles();
|
|
status = pthread_setspecific(dbl_key, dbl);
|
|
if (status) {
|
|
fprintf(stderr, "debuglog: cant initialize pthread "
|
|
"thread private storage key (pthread_setspecific)\n");
|
|
abort();
|
|
}
|
|
}
|
|
return dbl;
|
|
}
|
|
|
|
#else // No threads ->
|
|
|
|
static DebugLog *dbl;
|
|
DebugLog *getdbl()
|
|
{
|
|
if (!dbl) {
|
|
dbl = new DebugLog;
|
|
dbl->setwriter(theWriter);
|
|
initfiles();
|
|
}
|
|
return dbl;
|
|
}
|
|
#endif
|
|
|
|
#ifndef NO_NAMESPACES
|
|
}
|
|
#endif // NO_NAMESPACES
|
|
|
|
////////////////////////////////////////// TEST DRIVER //////////////////
|
|
#else /* TEST_DEBUGLOG */
|
|
|
|
#include <stdio.h>
|
|
#include "debuglog.h"
|
|
|
|
#if DEBUGLOG_USE_THREADS
|
|
#define TEST_THREADS
|
|
#endif
|
|
|
|
#ifdef TEST_THREADS
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
const int iloop = 5;
|
|
void *thread_test(void *data)
|
|
{
|
|
const char *s = (const char *)data;
|
|
int lev = atoi(s);
|
|
DebugLog::getdbl()->setloglevel(DEBDEB);
|
|
for (int i = 1; i < iloop;i++) {
|
|
switch (lev) {
|
|
case 1: LOGFATAL(("Thread: %s count: %d\n", s, i));break;
|
|
case 2: LOGERR(("Thread: %s count: %d\n", s, i));break;
|
|
default:
|
|
case 3: LOGINFO(("Thread: %s count: %d\n", s, i));break;
|
|
}
|
|
sleep(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
#ifdef TEST_THREADS
|
|
pthread_t t1, t2, t3;
|
|
|
|
char name1[20];
|
|
strcpy(name1, "1");
|
|
pthread_create(&t1, 0, thread_test, name1);
|
|
|
|
char name2[20];
|
|
strcpy(name2, "2");
|
|
pthread_create(&t2, 0, thread_test, name2);
|
|
|
|
char name3[20];
|
|
strcpy(name3, "3");
|
|
pthread_create(&t3, 0, thread_test, name3);
|
|
|
|
DebugLog::getdbl()->setloglevel(DEBDEB);
|
|
for (int i = 1; i < iloop;i++) {
|
|
LOGINFO(("LOGGING FROM MAIN\n"));
|
|
sleep(1);
|
|
}
|
|
sleep(2);
|
|
exit(0);
|
|
#else
|
|
LOGFATAL(("FATAL\n","Val"));
|
|
DebugLog::getdbl()->logdate(1);
|
|
LOGERR(("ERR\n","Val"));
|
|
LOGINFO(("INFO\n","Val"));
|
|
LOGDEB0(("DEBUG %s\n","valeur"));
|
|
|
|
int lev;
|
|
printf("Testing push. Initial level: %d\n", DebugLog::getdbl()->getlevel());
|
|
for (lev = 0; lev < 4;lev++) {
|
|
DebugLog::getdbl()->pushlevel(lev);
|
|
printf("Lev now %d\n", DebugLog::getdbl()->getlevel());
|
|
}
|
|
printf("Testing pop\n");
|
|
for (lev = 0; lev < 7;lev++) {
|
|
DebugLog::getdbl()->poplevel();
|
|
printf("Lev now %d\n", DebugLog::getdbl()->getlevel());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#endif /* TEST_DEBUGLOG */
|