Added logging library and udp2file.
Updated README to reflect.
This commit is contained in:
321
Logging/logging_lib.c
Normal file
321
Logging/logging_lib.c
Normal file
@ -0,0 +1,321 @@
|
||||
//*============================================================================*/
|
||||
// Title: logging.c
|
||||
// Author: RSH
|
||||
// Description:
|
||||
// General purpose logging function with timestamping and choice of output
|
||||
// streams: stout, stderr, or a supplied file (opened in append mode).
|
||||
// TODO:
|
||||
// * Have a logging receive server
|
||||
// * provide iso compliant option
|
||||
// * test output options
|
||||
// * useful print/formatting outputs (lines, tabs, etc.)
|
||||
//TODO: would be nice if logger auto chunked at 80 chars
|
||||
/*============================================================================*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "logging_lib.h"
|
||||
|
||||
|
||||
// static const int tsBufLen = strlen("2025-01-01T01:50:01.234567890Z") + 1 ;
|
||||
// static const int tsTmpLen = strlen("2025-01-01T01:50:01.") + 1 ;
|
||||
|
||||
// #define LOG_MSG getTimestamp(tsBuf); snprintf(msgBuffer, tsBufLen+2, "%s: ", tsBuf); sprintf(msgBuffer + tsBufLen +1,
|
||||
// #define RP )
|
||||
|
||||
// static int getTimestamp(char * tsBuf)
|
||||
// {
|
||||
// struct timespec now;
|
||||
// struct tm tm;
|
||||
// int retval = clock_gettime(CLOCK_REALTIME, &now);
|
||||
// gmtime_r(&now.tv_sec, &tm);
|
||||
// strftime(tsBuf, tsTmpLen, "%Y-%m-%dT%H:%M:%S.", &tm);
|
||||
// sprintf(tsBuf + tsTmpLen -1, "%09luZ", now.tv_nsec);
|
||||
// return retval;
|
||||
// }
|
||||
|
||||
|
||||
// Initialise buffers for the different severity strings
|
||||
static char logDbg[] = DEBUG_PREFIX;
|
||||
static char logInfo[] = INFO_PREFIX;
|
||||
static char logWarn[] = WARN_PREFIX;
|
||||
static char logErr[] = ERR_PREFIX;
|
||||
|
||||
static char logInfoColour[] = COLOUR_INFO_PREFIX;
|
||||
static char logWarnColour[] = COLOUR_WARN_PREFIX;
|
||||
static char logErrColour[] = COLOUR_ERR_PREFIX;
|
||||
|
||||
// Constants for the lenght of various elements of the timestamp and function ID
|
||||
static const int tsTmpLen = strlen(STD_TIMESTAMP_SEC)+1;
|
||||
static const int tsBufLen = strlen(STD_TIMESTAMP_WHOLE)+1;
|
||||
static const int tsFunLen = strlen(FUNC_STR);
|
||||
|
||||
|
||||
static char logFileName[MAX_LOG_FILE_NAME_LEN] = "";
|
||||
static FILE *logOutput = NULL;
|
||||
static T_LogOutput outputState = notSet;
|
||||
static int envNoColour = 0;
|
||||
|
||||
/*============================================================================*/
|
||||
/* Logging_CheckInited */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
// This checks that the library has been inited and is called by other
|
||||
// logging functions - throws a warning and inits the library to stdout if it
|
||||
// has not already been initialised.
|
||||
/*----------------------------------------------------------------------------*/
|
||||
static void Logging_CheckInited(char *timestamp, const char *caller)
|
||||
{
|
||||
if ( (logOutput == NULL) || (outputState == notSet) )
|
||||
{
|
||||
logOutput = stdout;
|
||||
outputState = standardOut;
|
||||
fprintf(logOutput
|
||||
, "%s%sLogging function %s() called without logging library being initialised >>>> defaulting to stdout\n"
|
||||
, timestamp, logWarn, caller);
|
||||
}
|
||||
}
|
||||
/*============================================================================*/
|
||||
|
||||
/*============================================================================*/
|
||||
/* Logging_LogMsgTS */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
// Example intended usage:
|
||||
// #define LOG_SOURCE "FileName"
|
||||
// #define ME someFunction
|
||||
// int Verbose = 1 // could be set at runtime via arg (e.g. ./prog -v)
|
||||
// void someFunc (void)
|
||||
// {
|
||||
// Logging_LogMsgTS(LOG_SOURCE, ME, LOG_DEBUG, "This is some noisy info");
|
||||
// }
|
||||
// #undef
|
||||
// Output:
|
||||
// [ 2025-06-07 02:12:02.005 ] (DEBUG) | FileName in function: someFunction(): This is some noisy info
|
||||
//
|
||||
//
|
||||
// OR without custom defines:
|
||||
// void someFunc (void)
|
||||
// {
|
||||
// Logging_LogMsgTS(__FILE__, __func__, LOG_DEBUG, "This is some noisy info");
|
||||
// }
|
||||
// Output:
|
||||
// [ 2025-06-07 02:12:02.005 ] (DEBUG) | logTest.c in function: someFunc(): This is some noisy info
|
||||
/*----------------------------------------------------------------------------*/
|
||||
void Logging_LogMsgTS( const char * where
|
||||
, const char * caller
|
||||
, int severity
|
||||
, const char * message, ... )
|
||||
{
|
||||
char buffer[MAX_LOG_BUF_LEN];
|
||||
int callerLen = strlen(where) + strlen(caller) + tsFunLen;
|
||||
char *severityMsg = NULL;
|
||||
int severityLen = 0;
|
||||
int printfColour = 0;
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
|
||||
// first get the current time and insert at the front of the buffer
|
||||
struct timespec now;
|
||||
struct tm tm;
|
||||
if ( clock_gettime(CLOCK_REALTIME, &now) < 0)
|
||||
{
|
||||
// Output the error to the file if used
|
||||
if(outputState == logFile)
|
||||
{
|
||||
LOGMSG_N_ERR("Logging", "Logging_LogMsgTS", "Unable to get system time [errno: %d (%s)]"
|
||||
, errno, strerror(errno))
|
||||
}
|
||||
// Put error on stderr as well
|
||||
fprintf(logOutput
|
||||
, "%sLogging function Logging_LogMsgTS(): Unable to get system time [errno: %d (%s)]\n"
|
||||
, (envNoColour == 1) ? logErr : logErrColour , errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
gmtime_r(&now.tv_sec, &tm);
|
||||
strftime(buffer, tsTmpLen, "[ %Y-%m-%d %H:%M:%S.", &tm);
|
||||
sprintf( (buffer + tsTmpLen -1), "%03lu ] ", (now.tv_nsec/1000000) % 1000000);
|
||||
|
||||
|
||||
// check if the function has been called (making use of the pre-assembled timestamp)
|
||||
Logging_CheckInited(buffer, __func__);
|
||||
|
||||
// determine which string to use for the severity print out based on message
|
||||
// severity level, output stream and whether the environment has NO_COLOR
|
||||
// set [see Logging_Init()]
|
||||
printfColour = ( (envNoColour == 1) || (outputState == logFile) ) ? NO_COLOUR : PRINT_COLOUR;
|
||||
switch (severity)
|
||||
{
|
||||
case 0:
|
||||
severityMsg = logDbg;
|
||||
break;
|
||||
case 1:
|
||||
severityMsg = (printfColour == NO_COLOUR) ? logInfo : logInfoColour;
|
||||
break;
|
||||
case 2:
|
||||
severityMsg = (printfColour == NO_COLOUR) ? logWarn : logWarnColour;
|
||||
break;
|
||||
case 3:
|
||||
severityMsg = (printfColour == NO_COLOUR) ? logErr : logErrColour;
|
||||
break;
|
||||
default:
|
||||
severityMsg = (printfColour == NO_COLOUR) ? logInfo : logInfoColour;
|
||||
}
|
||||
severityLen = strlen(severityMsg);
|
||||
|
||||
// add the severity to the buffer
|
||||
sprintf( (buffer + tsBufLen), "%s", severityMsg);
|
||||
// then add the process/file name and function name to the output buffer
|
||||
sprintf( (buffer + tsBufLen + severityLen), "%s in function: %s(): ", where, caller);
|
||||
// finally add the supplied message and format with args
|
||||
vsprintf( (buffer + tsBufLen + severityLen + callerLen), message, args );
|
||||
// print the message to the output
|
||||
fprintf(logOutput, "%s\n", buffer);
|
||||
va_end(args);
|
||||
}
|
||||
/*============================================================================*/
|
||||
|
||||
/*============================================================================*/
|
||||
/* Logging_LogMsg*/
|
||||
/*----------------------------------------------------------------------------*/
|
||||
// Like Logging_LogMsgTS above, but does not insert a timestamp
|
||||
/*----------------------------------------------------------------------------*/
|
||||
void Logging_LogMsg( const char * where
|
||||
, const char * caller
|
||||
, int severity
|
||||
, const char * message, ... )
|
||||
{
|
||||
char buffer[MAX_LOG_BUF_LEN];
|
||||
int callerLen = strlen(where) + strlen(caller) + tsFunLen;
|
||||
char * severityMsg = NULL;
|
||||
int severityLen = 0;
|
||||
int printfColour = 0;
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
|
||||
// check if the function has been called (making use of the pre-assembled timestamp)
|
||||
Logging_CheckInited(NULL, __func__);
|
||||
|
||||
// determine which string to use for the severity print out based on message
|
||||
// severity level, output stream and whether the environment has NO_COLOR
|
||||
// set [see Logging_Init()]
|
||||
printfColour = ( (envNoColour == 1) || (outputState == logFile) ) ? NO_COLOUR : PRINT_COLOUR;
|
||||
switch (severity)
|
||||
{
|
||||
case 0:
|
||||
severityMsg = logDbg;
|
||||
break;
|
||||
case 1:
|
||||
severityMsg = (printfColour == NO_COLOUR) ? logInfo : logInfoColour;
|
||||
break;
|
||||
case 2:
|
||||
severityMsg = (printfColour == NO_COLOUR) ? logWarn : logWarnColour;
|
||||
break;
|
||||
case 3:
|
||||
severityMsg = (printfColour == NO_COLOUR) ? logErr : logErrColour;
|
||||
break;
|
||||
default:
|
||||
severityMsg = (printfColour == NO_COLOUR) ? logInfo : logInfoColour;
|
||||
}
|
||||
severityLen = strlen(severityMsg);
|
||||
|
||||
// add the severity to the buffer
|
||||
sprintf( buffer, "%s", severityMsg);
|
||||
// then add the process/file name and function name to the output buffer
|
||||
sprintf( (buffer + severityLen), "%s in function: %s(): ", where, caller);
|
||||
// finally add the supplied message and format with args
|
||||
vsprintf( (buffer + severityLen + callerLen), message, args );
|
||||
// print the message to the output
|
||||
fprintf(logOutput, "%s\n", buffer);
|
||||
va_end(args);
|
||||
}
|
||||
/*============================================================================*/
|
||||
|
||||
/*============================================================================*/
|
||||
/* Logging_Init */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
// It is not necessary to call Logging_Init, but it is strongly recommended.
|
||||
// If it is not called, the first call to a logging function will set the
|
||||
// output to stdout as default and throw a warning.
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#define ME "Logging_Init"
|
||||
int Logging_Init(T_LogOutput output, char * fileName)
|
||||
{
|
||||
// check if the ENV variable NO_COLOR is set (https://no-color.org/)
|
||||
char *colorEnv = getenv("NO_COLOR");
|
||||
if( (colorEnv != NULL) && (colorEnv[0] != '\0') )
|
||||
{
|
||||
envNoColour = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
envNoColour = 0;
|
||||
}
|
||||
|
||||
// if a file name has been supplied, attempt to open it,
|
||||
// else check if the user wants the output to be
|
||||
// stderr - otherwise the default output type is stdout
|
||||
if (fileName != NULL)
|
||||
{
|
||||
logOutput = fopen(fileName, "a");
|
||||
if (logOutput == NULL)
|
||||
{
|
||||
logOutput = stdout;
|
||||
Logging_LogMsg( __FILE__, ME, LOG_ERR,
|
||||
"Can't open file: %s [errno: %d (%s)] >>>>>>> defaulting to stdout"
|
||||
, fileName, errno, strerror(errno) );
|
||||
outputState = standardOut;
|
||||
//return a fail in case the caller wants to handle (retry, terminate, etc.)
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//logOuput already set by successful fopen()
|
||||
outputState = logFile;
|
||||
memcpy(logFileName, fileName, (strlen(fileName) * sizeof(char)));
|
||||
Logging_LogMsgTS(__FILE__, ME, LOG_INFO, "Set log output to file: %s", logFileName);
|
||||
}
|
||||
}
|
||||
else if (output == standardErr)
|
||||
{
|
||||
logOutput = stderr;
|
||||
outputState = standardErr;
|
||||
Logging_LogMsgTS(__FILE__, ME, LOG_INFO, "Set log output to stderr");
|
||||
}
|
||||
else
|
||||
{
|
||||
logOutput = stdout;
|
||||
outputState = standardOut;
|
||||
Logging_LogMsgTS(__FILE__, ME, LOG_INFO, "Set log output to stdout");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#undef ME
|
||||
/*============================================================================*/
|
||||
/*============================================================================*/
|
||||
/* Logging_Term */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
// If Logging_Init was not called prior, or the output stream is set to stdout
|
||||
// or stderr, then there is strictly no need to call Logging_Term though it is
|
||||
// strongly recommended.
|
||||
// It is necessary to call Logging_Term if the logging output is set to a file!
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#define ME "Logging_Term"
|
||||
void Logging_Term( void )
|
||||
{
|
||||
if (outputState == logFile)
|
||||
{
|
||||
fclose(logOutput);
|
||||
memset(logFileName, '\0', sizeof(logFileName));
|
||||
}
|
||||
logOutput = NULL;
|
||||
outputState = notSet;
|
||||
}
|
||||
#undef ME
|
||||
/*============================================================================*/
|
||||
118
Logging/logging_lib.h
Normal file
118
Logging/logging_lib.h
Normal file
@ -0,0 +1,118 @@
|
||||
#ifndef LOGGING_H
|
||||
#define LOGGING_H
|
||||
|
||||
#define MAX_LOG_BUF_LEN 1024
|
||||
#define MAX_LOG_FILE_NAME_LEN 400
|
||||
|
||||
// define severity levels for log message highlighting
|
||||
#define LOG_DEBUG 0
|
||||
#define LOG_INFO 1
|
||||
#define LOG_WARN 2
|
||||
#define LOG_ERR 3
|
||||
|
||||
// Macros for verbosity level
|
||||
#define LOG_VERBOSE 1
|
||||
#define LOG_QUIET 0
|
||||
|
||||
// Macros for colour console printing
|
||||
#define PRINT_COLOUR 1
|
||||
#define NO_COLOUR 0
|
||||
|
||||
#define DEBUG_PREFIX "(DEBUG) | "
|
||||
#define INFO_PREFIX "(INFO) | "
|
||||
#define WARN_PREFIX "(WARNING) | "
|
||||
#define ERR_PREFIX "(ERROR) | "
|
||||
|
||||
// \x1b[31m ERROR \x1b[0m : \x1b[31m sets string to red, we then print 'ERROR', then reset terminal so it doesn stay red!
|
||||
// ANSI escape code colouring for highlighting different messages
|
||||
#define COLOUR_INFO_PREFIX "(\x1b[36mINFO\x1b[0m) | "
|
||||
#define COLOUR_WARN_PREFIX "(\x1b[33mWARNING\x1b[0m) | "
|
||||
#define COLOUR_ERR_PREFIX "(\x1b[31mERROR\x1b[0m) | "
|
||||
|
||||
// Define the timstamp format strings and function caller ID string
|
||||
#define STD_TIMESTAMP_SEC "[ 2025-06-04 12:52:11."
|
||||
#define STD_TIMESTAMP_WHOLE "[ 2025-06-04 12:52:11.999 ]"
|
||||
#define FUNC_STR " in function: (): "
|
||||
|
||||
// Enum for use in determining current output stream
|
||||
typedef enum {
|
||||
notSet = 0,
|
||||
standardOut = 1,
|
||||
standardErr = 2,
|
||||
logFile = 3
|
||||
}T_LogOutput;
|
||||
|
||||
//*============================================================================*/
|
||||
// Macros for initialisation function
|
||||
//*----------------------------------------------------------------------------*/
|
||||
#define LOGGING_INIT_STDOUT Logging_Init(standardOut, NULL);
|
||||
|
||||
#define LOGGING_INIT_STDERR Logging_Init(standardErr, NULL);
|
||||
|
||||
#define LOGGING_INIT_FILE(FileName) Logging_Init(logFile, FileName);
|
||||
/*============================================================================*/
|
||||
|
||||
/*============================================================================*/
|
||||
// Useful variadic macros for logging functions - see https://gcc.gnu.org/onlinedocs/gcc-15.1.0/gcc/Variadic-Macros.html
|
||||
//*----------------------------------------------------------------------------*/
|
||||
// Timestamped logging with file and func preprocessors used to define 'where' and 'caller'
|
||||
//*----------------------------------------------------------------------------*/
|
||||
#define LOGMSG_TS_DBG(message, ...) if(Verbose == LOG_VERBOSE) { Logging_LogMsgTS(__FILE__, __func__, LOG_DEBUG, message, ##__VA_ARGS__);}
|
||||
|
||||
#define LOGMSG_TS_INFO(message, ...) Logging_LogMsgTS(__FILE__, __func__, LOG_INFO, message, ##__VA_ARGS__);
|
||||
|
||||
#define LOGMSG_TS_WARN(message, ...) Logging_LogMsgTS(__FILE__, __func__, LOG_WARN, message, ##__VA_ARGS__);
|
||||
|
||||
#define LOGMSG_TS_ERR(message, ...) Logging_LogMsgTS(__FILE__, __func__, LOG_ERR, message, ##__VA_ARGS__);
|
||||
//*----------------------------------------------------------------------------*/
|
||||
// Basic logging with file and func preprocessors used to define 'where' and 'caller'
|
||||
//*----------------------------------------------------------------------------*/
|
||||
#define LOGMSG_DBG(message, ...) if(Verbose == LOG_VERBOSE) { Logging_LogMsg(__FILE__, __func__, LOG_DEBUG, message, ##__VA_ARGS__);}
|
||||
|
||||
#define LOGMSG_INFO(message, ...) Logging_LogMsg(__FILE__, __func__, LOG_INFO, message, ##__VA_ARGS__);
|
||||
|
||||
#define LOGMSG_WARN(message, ...) Logging_LogMsg(__FILE__, __func__, LOG_WARN, message, ##__VA_ARGS__);
|
||||
|
||||
#define LOGMSG_ERR(message, ...) Logging_LogMsg(__FILE__, __func__, LOG_ERR, message, ##__VA_ARGS__);
|
||||
//*----------------------------------------------------------------------------*/
|
||||
// Timestamped logging with arguments used to define 'where' and 'caller' (N = name)
|
||||
//*----------------------------------------------------------------------------*/
|
||||
#define LOGMSG_TS_N_DBG(what, who, message, ...) if(Verbose == LOG_VERBOSE) {Logging_LogMsgTS(what, who, LOG_DEBUG, message, ##__VA_ARGS__);}
|
||||
|
||||
#define LOGMSG_TS_N_INFO(what, who, message, ...) Logging_LogMsgTS(what, who, LOG_INFO, message, ##__VA_ARGS__);
|
||||
|
||||
#define LOGMSG_TS_N_WARN(what, who, message, ...) Logging_LogMsgTS(what, who, LOG_WARN, message, ##__VA_ARGS__);
|
||||
|
||||
#define LOGMSG_TS_N_ERR(what, who, message, ...) Logging_LogMsgTS(what, who, LOG_ERR, message, ##__VA_ARGS__);
|
||||
//*----------------------------------------------------------------------------*/
|
||||
// Basic logging with arguments used to define 'where' and 'caller' (N = name)
|
||||
//*----------------------------------------------------------------------------*/
|
||||
#define LOGMSG_N_DBG(what, who, message, ...) if(Verbose == LOG_VERBOSE) { Logging_LogMsg(what, who, LOG_DEBUG, message, ##__VA_ARGS__);}
|
||||
|
||||
#define LOGMSG_N_INFO(what, who, message, ...) Logging_LogMsg(what, who, LOG_INFO, message, ##__VA_ARGS__);
|
||||
|
||||
#define LOGMSG_N_WARN(what, who, message, ...) Logging_LogMsg(what, who, LOG_WARN, message, ##__VA_ARGS__);
|
||||
|
||||
#define LOGMSG_N_ERR(what, who, message, ...) Logging_LogMsg(what, who, LOG_ERR, message, ##__VA_ARGS__);
|
||||
//*============================================================================*/
|
||||
|
||||
//*============================================================================*/
|
||||
// Prototypes for logging functions
|
||||
//*----------------------------------------------------------------------------*/
|
||||
|
||||
void Logging_LogMsgTS( const char * where
|
||||
, const char * caller
|
||||
, int severity
|
||||
, const char * message, ... );
|
||||
|
||||
void Logging_LogMsg( const char * where
|
||||
, const char * caller
|
||||
, int severity
|
||||
, const char * message, ... );
|
||||
|
||||
int Logging_Init(T_LogOutput output, char * fileName);
|
||||
|
||||
void Logging_Term( void );
|
||||
//*============================================================================*/
|
||||
|
||||
#endif //LOGGING_H
|
||||
Reference in New Issue
Block a user