2024-12-01 19:35:24 +00:00
|
|
|
//| 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"
|
2024-05-29 16:36:57 +00:00
|
|
|
#include "cwTest.h"
|
2019-12-19 03:24:12 +00:00
|
|
|
#include "cwMem.h"
|
2023-05-20 01:21:35 +00:00
|
|
|
#include "cwTime.h"
|
2019-12-19 03:24:12 +00:00
|
|
|
|
|
|
|
namespace cw
|
|
|
|
{
|
2019-12-27 21:52:45 +00:00
|
|
|
namespace log
|
2019-12-19 03:24:12 +00:00
|
|
|
{
|
2019-12-27 21:52:45 +00:00
|
|
|
typedef struct log_str
|
|
|
|
{
|
|
|
|
logOutputCbFunc_t outCbFunc;
|
|
|
|
void* outCbArg;
|
|
|
|
logFormatCbFunc_t fmtCbFunc;
|
|
|
|
void* fmtCbArg;
|
|
|
|
unsigned level;
|
2023-09-12 21:38:44 +00:00
|
|
|
unsigned flags;
|
2019-12-27 21:52:45 +00:00
|
|
|
} log_t;
|
|
|
|
|
|
|
|
|
|
|
|
handle_t __logGlobalHandle__;
|
|
|
|
|
|
|
|
idLabelPair_t logLevelLabelArray[] =
|
|
|
|
{
|
2020-09-01 19:46:21 +00:00
|
|
|
{ kPrint_LogLevel, "" },
|
2019-12-27 21:52:45 +00:00
|
|
|
{ kDebug_LogLevel, "debug" },
|
2023-11-14 12:53:43 +00:00
|
|
|
{ kInfo_LogLevel, "info" },
|
|
|
|
{ kWarning_LogLevel, "warn" },
|
2019-12-27 21:52:45 +00:00
|
|
|
{ kError_LogLevel, "error" },
|
2023-05-20 01:21:35 +00:00
|
|
|
{ kFatal_LogLevel, "fatal" },
|
2019-12-27 21:52:45 +00:00
|
|
|
{ kInvalid_LogLevel, "<invalid>" }
|
|
|
|
};
|
2019-12-19 03:24:12 +00:00
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
log_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,log_t>(h); }
|
|
|
|
}
|
2019-12-19 03:24:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-12-27 21:52:45 +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;
|
2019-12-27 21:52:45 +00:00
|
|
|
if((rc = destroy(hRef)) != kOkRC)
|
2019-12-19 03:24:12 +00:00
|
|
|
return rc;
|
|
|
|
|
2019-12-28 02:51:28 +00:00
|
|
|
log_t* p = mem::allocZ<log_t>();
|
2019-12-27 21:52:45 +00:00
|
|
|
p->outCbFunc = outCbFunc == nullptr ? defaultOutput : outCbFunc;
|
2019-12-19 03:24:12 +00:00
|
|
|
p->outCbArg = outCbArg;
|
2019-12-27 21:52:45 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-11-14 12:53:43 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
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;
|
|
|
|
|
2019-12-28 02:51:28 +00:00
|
|
|
mem::release( hRef.p );
|
2019-12-19 03:24:12 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
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
|
|
|
{
|
2019-12-27 21:52:45 +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 )
|
|
|
|
{
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
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);
|
|
|
|
|
2023-09-12 21:38:44 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
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
|
|
|
{
|
2023-11-14 12:53:43 +00:00
|
|
|
rc_t rc = returnCode;
|
2024-05-10 01:54:39 +00:00
|
|
|
if( level >= _handleToPtr(h)->level || level == kPrint_LogLevel )
|
2023-11-14 12:53:43 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
void cw::log::setLevel( handle_t h, unsigned level )
|
2019-12-19 03:24:12 +00:00
|
|
|
{
|
2019-12-27 21:52:45 +00:00
|
|
|
log_t* p = _handleToPtr(h);
|
2019-12-19 03:24:12 +00:00
|
|
|
p->level = level;
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
unsigned cw::log::level( handle_t h )
|
2019-12-19 03:24:12 +00:00
|
|
|
{
|
2019-12-27 21:52:45 +00:00
|
|
|
log_t* p = _handleToPtr(h);
|
2019-12-19 03:24:12 +00:00
|
|
|
return p->level;
|
|
|
|
}
|
|
|
|
|
2023-09-12 21:38:44 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-05-10 01:54:39 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-09-12 21:38:44 +00:00
|
|
|
|
2021-12-30 02:35:10 +00:00
|
|
|
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
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
const char* cw::log::levelToLabel( unsigned level )
|
2024-05-06 19:41:54 +00:00
|
|
|
{ return idToLabel(logLevelLabelArray,level,kInvalid_LogLevel); }
|
2019-12-19 03:24:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2019-12-27 21:52:45 +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);
|
2020-04-17 00:09:13 +00:00
|
|
|
fflush(f);
|
2019-12-19 03:24:12 +00:00
|
|
|
}
|
|
|
|
|
2023-09-12 21:38:44 +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);
|
2019-12-27 21:52:45 +00:00
|
|
|
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;
|
|
|
|
|
2022-11-14 23:32:52 +00:00
|
|
|
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];
|
2022-11-14 23:32:52 +00:00
|
|
|
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;
|
|
|
|
|
2023-05-20 01:21:35 +00:00
|
|
|
int tdn = 256;
|
|
|
|
char td[tdn];
|
2023-09-12 21:38:44 +00:00
|
|
|
td[0] = 0;
|
|
|
|
if( cwIsFlag(flags,kDateTimeFl) )
|
|
|
|
time::formatDateTime( td, (unsigned)tdn );
|
2023-05-20 01:21:35 +00:00
|
|
|
tdn = strlen(td);
|
|
|
|
|
|
|
|
|
2019-12-19 03:24:12 +00:00
|
|
|
// don't print the function,file,line when this is an 'info' msg.
|
2024-05-10 01:54:39 +00:00
|
|
|
if( level == kInfo_LogLevel || level == kPrint_LogLevel )
|
2022-12-16 16:30:41 +00:00
|
|
|
{
|
2019-12-19 03:24:12 +00:00
|
|
|
loStr = "";
|
2022-12-16 16:30:41 +00:00
|
|
|
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 = "";
|
|
|
|
|
2020-09-01 19:46:21 +00:00
|
|
|
if( level == kPrint_LogLevel )
|
|
|
|
{
|
2023-05-20 01:21:35 +00:00
|
|
|
const char* fmt = "%s: %s";
|
|
|
|
int n = snprintf(nullptr,0,fmt,td,msg);
|
2020-09-01 19:46:21 +00:00
|
|
|
cwAssert(n != -1);
|
|
|
|
char s[n+1];
|
2023-05-20 01:21:35 +00:00
|
|
|
int m = snprintf(s,n+1,fmt,td,msg);
|
2020-09-01 19:46:21 +00:00
|
|
|
cwAssert(m==n);
|
|
|
|
outFunc(outCbArg,level,s);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// levelStr, msg,sys_msg, rc, function, lineno, filename
|
2023-05-20 01:21:35 +00:00
|
|
|
const char* fmt = "%s: %s: %s %s %s %s\n";
|
2019-12-19 03:24:12 +00:00
|
|
|
|
2023-05-20 01:21:35 +00:00
|
|
|
int n = snprintf(nullptr,0,fmt,levelStr,td,msg,syStr,rcStr,loStr);
|
2020-09-01 19:46:21 +00:00
|
|
|
cwAssert(n != -1);
|
|
|
|
char s[n+1];
|
2023-05-20 01:21:35 +00:00
|
|
|
int m = snprintf(s,n+1,fmt,levelStr,td,msg,syStr,rcStr,loStr);
|
2020-09-01 19:46:21 +00:00
|
|
|
cwAssert(m==n);
|
|
|
|
outFunc(outCbArg,level,s);
|
|
|
|
}
|
|
|
|
|
2019-12-19 03:24:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-27 21:52:45 +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
|
|
|
{
|
2019-12-27 21:52:45 +00:00
|
|
|
handle_t h;
|
2019-12-19 03:24:12 +00:00
|
|
|
rc_t rc;
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
if((rc = create(h, level, outCb, outCbArg, fmtCb, fmtCbArg )) == kOkRC )
|
|
|
|
setGlobalHandle(h);
|
2019-12-19 03:24:12 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
cw::rc_t cw::log::destroyGlobal()
|
2019-12-19 03:24:12 +00:00
|
|
|
{
|
2019-12-27 21:52:45 +00:00
|
|
|
return destroy(__logGlobalHandle__);
|
2019-12-19 03:24:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
void cw::log::setGlobalHandle( handle_t h )
|
2019-12-19 03:24:12 +00:00
|
|
|
{
|
|
|
|
__logGlobalHandle__ = h;
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
cw::log::handle_t cw::log::globalHandle()
|
2019-12-19 03:24:12 +00:00
|
|
|
{
|
|
|
|
return __logGlobalHandle__;
|
|
|
|
}
|