libcw/cwLog.cpp

305 lines
7.3 KiB
C++
Raw Permalink Normal View History

//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
2019-12-19 03:24:12 +00:00
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
2019-12-19 03:24:12 +00:00
#include "cwMem.h"
#include "cwTime.h"
2019-12-19 03:24:12 +00:00
namespace cw
{
namespace log
2019-12-19 03:24:12 +00:00
{
typedef struct log_str
{
logOutputCbFunc_t outCbFunc;
void* outCbArg;
logFormatCbFunc_t fmtCbFunc;
void* fmtCbArg;
unsigned level;
unsigned flags;
} log_t;
handle_t __logGlobalHandle__;
idLabelPair_t logLevelLabelArray[] =
{
{ kPrint_LogLevel, "" },
{ kDebug_LogLevel, "debug" },
{ kInfo_LogLevel, "info" },
{ kWarning_LogLevel, "warn" },
{ kError_LogLevel, "error" },
{ kFatal_LogLevel, "fatal" },
{ kInvalid_LogLevel, "<invalid>" }
};
2019-12-19 03:24:12 +00:00
log_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,log_t>(h); }
}
2019-12-19 03:24:12 +00:00
}
cw::rc_t cw::log::create( handle_t& hRef, unsigned level, logOutputCbFunc_t outCbFunc, void* outCbArg, logFormatCbFunc_t fmtCbFunc, void* fmtCbArg )
2019-12-19 03:24:12 +00:00
{
rc_t rc;
if((rc = destroy(hRef)) != kOkRC)
2019-12-19 03:24:12 +00:00
return rc;
log_t* p = mem::allocZ<log_t>();
p->outCbFunc = outCbFunc == nullptr ? defaultOutput : outCbFunc;
2019-12-19 03:24:12 +00:00
p->outCbArg = outCbArg;
p->fmtCbFunc = fmtCbFunc == nullptr ? defaultFormatter : fmtCbFunc;
2019-12-19 03:24:12 +00:00
p->fmtCbArg = fmtCbArg;
p->level = level;
hRef.p = p;
return rc;
}
cw::log::logLevelId_t cw::log::levelFromString( const char* label )
{
for(unsigned i=0; logLevelLabelArray[i].id != kInvalid_LogLevel; ++i)
if( strcasecmp(label,logLevelLabelArray[i].label) == 0 )
return (logLevelId_t)logLevelLabelArray[i].id;
return kInvalid_LogLevel;
}
const char* cw::log::levelToString( logLevelId_t level )
{
for(unsigned i=0; logLevelLabelArray[i].id != kInvalid_LogLevel; ++i)
if( logLevelLabelArray[i].id == level )
return logLevelLabelArray[i].label;
return nullptr;
}
cw::rc_t cw::log::destroy( handle_t& hRef )
2019-12-19 03:24:12 +00:00
{
rc_t rc = kOkRC;
if( hRef.p == nullptr )
return rc;
mem::release( hRef.p );
2019-12-19 03:24:12 +00:00
return rc;
}
cw::rc_t cw::log::msg( handle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, va_list vl )
2019-12-19 03:24:12 +00:00
{
log_t* p = _handleToPtr(h);
2019-12-19 03:24:12 +00:00
va_list vl1;
va_copy(vl1,vl);
int n = vsnprintf(nullptr,0,fmt,vl);
cwAssert(n != -1);
if( n != -1 )
{
char msg[n+1]; // add 1 to allow space for the terminating zero
2019-12-19 03:24:12 +00:00
int m = vsnprintf(msg,n+1,fmt,vl1);
cwAssert(m==n);
p->fmtCbFunc( p->fmtCbArg, p->outCbFunc, p->outCbArg, p->flags, level, function, filename, line, systemErrorCode, rc, msg );
2019-12-19 03:24:12 +00:00
}
va_end(vl1);
return rc;
}
cw::rc_t cw::log::msg( handle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t returnCode, const char* fmt, ... )
2019-12-19 03:24:12 +00:00
{
rc_t rc = returnCode;
if( level >= _handleToPtr(h)->level || level == kPrint_LogLevel )
{
va_list vl;
va_start(vl,fmt);
rc = msg( h, level, function, filename, line, systemErrorCode, returnCode, fmt, vl );
va_end(vl);
}
2019-12-19 03:24:12 +00:00
return rc;
}
void cw::log::setLevel( handle_t h, unsigned level )
2019-12-19 03:24:12 +00:00
{
log_t* p = _handleToPtr(h);
2019-12-19 03:24:12 +00:00
p->level = level;
}
unsigned cw::log::level( handle_t h )
2019-12-19 03:24:12 +00:00
{
log_t* p = _handleToPtr(h);
2019-12-19 03:24:12 +00:00
return p->level;
}
void cw::log::set_flags( handle_t h, unsigned flags )
{
log_t* p = _handleToPtr(h);
p->flags = flags;
}
unsigned cw::log::flags( handle_t h )
{
log_t* p = _handleToPtr(h);
return p->flags;
}
void* cw::log::outputCbArg( handle_t h )
{
log_t* p = _handleToPtr(h);
return p->outCbArg;
}
cw::log::logOutputCbFunc_t cw::log::outputCb( handle_t h )
{
log_t* p = _handleToPtr(h);
return p->outCbFunc;
}
void* cw::log::formatCbArg( handle_t h )
{
log_t* p = _handleToPtr(h);
return p->fmtCbArg;
}
cw::log::logFormatCbFunc_t cw::log::formatCb( handle_t h )
{
log_t* p = _handleToPtr(h);
return p->fmtCbFunc;
}
void cw::log::setOutputCb( handle_t h, logOutputCbFunc_t outFunc, void* outCbArg )
{
log_t* p = _handleToPtr(h);
p->outCbFunc = outFunc;
p->outCbArg = outCbArg;
}
void cw::log::setFormatCb( handle_t h, logFormatCbFunc_t fmtFunc, void* fmtCbArg )
{
log_t* p = _handleToPtr(h);
p->fmtCbFunc = fmtFunc;
p->fmtCbArg = fmtCbArg;
}
2019-12-19 03:24:12 +00:00
const char* cw::log::levelToLabel( unsigned level )
{ return idToLabel(logLevelLabelArray,level,kInvalid_LogLevel); }
2019-12-19 03:24:12 +00:00
void cw::log::defaultOutput( void* arg, unsigned level, const char* text )
2019-12-19 03:24:12 +00:00
{
FILE* f = level >= kWarning_LogLevel ? stderr : stdout;
fprintf(f,"%s",text);
fflush(f);
2019-12-19 03:24:12 +00:00
}
void cw::log::defaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned flags, unsigned level, const char* function, const char* filename, unsigned lineno, int sys_errno, rc_t rc, const char* msg )
2019-12-19 03:24:12 +00:00
{
// TODO: This code is avoids the use of dynamic memory allocation but relies on stack allocation. It's a security vulnerability.
//
const char* systemLabel = sys_errno==0 ? "" : "System Error: ";
const char* systemMsg = sys_errno==0 ? "" : strerror(sys_errno);
const char* levelStr = levelToLabel(level);
2019-12-19 03:24:12 +00:00
const char* rcFmt = "rc:%i";
int rcn = snprintf(nullptr,0,rcFmt,rc);
char rcs[rcn+1];
int rcm = snprintf(rcs,rcn+1,rcFmt,rc);
cwAssert(rcn==rcm);
const char* rcStr = rcs;
const char* syFmt = "%s (%i) %s";
int syn = snprintf(nullptr,0,syFmt,systemLabel,sys_errno,systemMsg);
2019-12-19 03:24:12 +00:00
char sys[syn+1];
int sym = snprintf(sys,syn+1,syFmt,systemLabel,sys_errno,systemMsg);
2019-12-19 03:24:12 +00:00
cwAssert(syn==sym);
const char* syStr = sys;
const char* loFmt = "%s line:%i %s";
int lon = snprintf(nullptr,0,loFmt,function,lineno,filename);
char los[lon+1];
int lom = snprintf(los,lon+1,loFmt,function,lineno,filename);
cwAssert(lon==lom);
const char* loStr = los;
int tdn = 256;
char td[tdn];
td[0] = 0;
if( cwIsFlag(flags,kDateTimeFl) )
time::formatDateTime( td, (unsigned)tdn );
tdn = strlen(td);
2019-12-19 03:24:12 +00:00
// don't print the function,file,line when this is an 'info' msg.
if( level == kInfo_LogLevel || level == kPrint_LogLevel )
{
2019-12-19 03:24:12 +00:00
loStr = "";
syStr = "";
}
2019-12-19 03:24:12 +00:00
// dont' print the rc msg if this is info or debug
if( level < kWarning_LogLevel )
rcStr = "";
if( level == kPrint_LogLevel )
{
const char* fmt = "%s: %s";
int n = snprintf(nullptr,0,fmt,td,msg);
cwAssert(n != -1);
char s[n+1];
int m = snprintf(s,n+1,fmt,td,msg);
cwAssert(m==n);
outFunc(outCbArg,level,s);
}
else
{
// levelStr, msg,sys_msg, rc, function, lineno, filename
const char* fmt = "%s: %s: %s %s %s %s\n";
2019-12-19 03:24:12 +00:00
int n = snprintf(nullptr,0,fmt,levelStr,td,msg,syStr,rcStr,loStr);
cwAssert(n != -1);
char s[n+1];
int m = snprintf(s,n+1,fmt,levelStr,td,msg,syStr,rcStr,loStr);
cwAssert(m==n);
outFunc(outCbArg,level,s);
}
2019-12-19 03:24:12 +00:00
}
cw::rc_t cw::log::createGlobal( unsigned level, logOutputCbFunc_t outCb, void* outCbArg, logFormatCbFunc_t fmtCb, void* fmtCbArg )
2019-12-19 03:24:12 +00:00
{
handle_t h;
2019-12-19 03:24:12 +00:00
rc_t rc;
if((rc = create(h, level, outCb, outCbArg, fmtCb, fmtCbArg )) == kOkRC )
setGlobalHandle(h);
2019-12-19 03:24:12 +00:00
return rc;
}
cw::rc_t cw::log::destroyGlobal()
2019-12-19 03:24:12 +00:00
{
return destroy(__logGlobalHandle__);
2019-12-19 03:24:12 +00:00
}
void cw::log::setGlobalHandle( handle_t h )
2019-12-19 03:24:12 +00:00
{
__logGlobalHandle__ = h;
}
cw::log::handle_t cw::log::globalHandle()
2019-12-19 03:24:12 +00:00
{
return __logGlobalHandle__;
}