You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-05 00:28:52 +02:00
Add log module.
This commit is contained in:
@ -62,7 +62,7 @@
|
|||||||
</release-item>
|
</release-item>
|
||||||
|
|
||||||
<release-item>
|
<release-item>
|
||||||
<p>Add <code>time</code> module.</p>
|
<p>Add <code>log</code> and <code>time</code> modules.</p>
|
||||||
</release-item>
|
</release-item>
|
||||||
|
|
||||||
<release-item>
|
<release-item>
|
||||||
|
@ -6,6 +6,7 @@ pgbackrest: \
|
|||||||
common/error.o \
|
common/error.o \
|
||||||
common/errorType.o \
|
common/errorType.o \
|
||||||
common/ini.o \
|
common/ini.o \
|
||||||
|
common/log.o \
|
||||||
common/memContext.o \
|
common/memContext.o \
|
||||||
common/regExp.o \
|
common/regExp.o \
|
||||||
common/time.o \
|
common/time.o \
|
||||||
@ -19,6 +20,7 @@ pgbackrest: \
|
|||||||
common/wait.o \
|
common/wait.o \
|
||||||
config/config.o \
|
config/config.o \
|
||||||
config/define.o \
|
config/define.o \
|
||||||
|
config/load.o \
|
||||||
config/parse.o \
|
config/parse.o \
|
||||||
perl/exec.o \
|
perl/exec.o \
|
||||||
storage/helper.o \
|
storage/helper.o \
|
||||||
@ -28,6 +30,7 @@ pgbackrest: \
|
|||||||
common/error.o \
|
common/error.o \
|
||||||
common/errorType.o \
|
common/errorType.o \
|
||||||
common/ini.o \
|
common/ini.o \
|
||||||
|
common/log.o \
|
||||||
common/memContext.o \
|
common/memContext.o \
|
||||||
common/regExp.o \
|
common/regExp.o \
|
||||||
common/time.o \
|
common/time.o \
|
||||||
@ -41,6 +44,7 @@ pgbackrest: \
|
|||||||
common/wait.o \
|
common/wait.o \
|
||||||
config/config.o \
|
config/config.o \
|
||||||
config/define.o \
|
config/define.o \
|
||||||
|
config/load.o \
|
||||||
config/parse.o \
|
config/parse.o \
|
||||||
perl/exec.o \
|
perl/exec.o \
|
||||||
storage/helper.o \
|
storage/helper.o \
|
||||||
|
176
src/common/log.c
Normal file
176
src/common/log.c
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/***********************************************************************************************************************************
|
||||||
|
Log Handler
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "common/error.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/time.h"
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Module variables
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
// Log levels
|
||||||
|
LogLevel logLevelStdOut = logLevelOff;
|
||||||
|
LogLevel logLevelStdErr = logLevelWarn;
|
||||||
|
|
||||||
|
// Log file handles
|
||||||
|
int logHandleStdOut = STDOUT_FILENO;
|
||||||
|
int logHandleStdErr = STDERR_FILENO;
|
||||||
|
|
||||||
|
// Is the timestamp printed in the log?
|
||||||
|
bool logTimestamp = false;
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Log buffer
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#define LOG_BUFFER_SIZE 32768
|
||||||
|
|
||||||
|
char logBuffer[LOG_BUFFER_SIZE];
|
||||||
|
char logFormat[LOG_BUFFER_SIZE];
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Convert log level to string and vice versa
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#define LOG_LEVEL_TOTAL 9
|
||||||
|
|
||||||
|
const char *logLevelList[LOG_LEVEL_TOTAL] =
|
||||||
|
{
|
||||||
|
"OFF",
|
||||||
|
"ASSERT",
|
||||||
|
"ERROR",
|
||||||
|
"PROTOCOL",
|
||||||
|
"WARN",
|
||||||
|
"INFO",
|
||||||
|
"DETAIL",
|
||||||
|
"DEBUG",
|
||||||
|
"TRACE",
|
||||||
|
};
|
||||||
|
|
||||||
|
LogLevel
|
||||||
|
logLevelEnum(const char *logLevel)
|
||||||
|
{
|
||||||
|
LogLevel result = logLevelOff;
|
||||||
|
|
||||||
|
// Search for the log level
|
||||||
|
for (; result < LOG_LEVEL_TOTAL; result++)
|
||||||
|
if (strcasecmp(logLevel, logLevelList[result]) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// If the log level was not found
|
||||||
|
if (result == LOG_LEVEL_TOTAL)
|
||||||
|
THROW(AssertError, "log level '%s' not found", logLevel);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
logLevelStr(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
if (logLevel >= LOG_LEVEL_TOTAL)
|
||||||
|
THROW(AssertError, "invalid log level '%d'", logLevel);
|
||||||
|
|
||||||
|
return logLevelList[logLevel];
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Initialize the log system
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void
|
||||||
|
logInit(LogLevel logLevelStdOutParam, LogLevel logLevelStdErrParam, bool logTimestampParam)
|
||||||
|
{
|
||||||
|
logLevelStdOut = logLevelStdOutParam;
|
||||||
|
logLevelStdErr = logLevelStdErrParam;
|
||||||
|
logTimestamp = logTimestampParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
General log function
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void
|
||||||
|
logInternal(LogLevel logLevel, const char *fileName, const char *functionName, int fileLine, int code, const char *format, ...)
|
||||||
|
{
|
||||||
|
// Should this entry be logged?
|
||||||
|
if (logLevel <= logLevelStdOut || logLevel <= logLevelStdErr)
|
||||||
|
{
|
||||||
|
int bufferPos = 0;
|
||||||
|
|
||||||
|
// Add time
|
||||||
|
if (logTimestamp && logLevel > logLevelStdErr)
|
||||||
|
{
|
||||||
|
TimeUSec logTimeUSec = timeUSec();
|
||||||
|
time_t logTime = (time_t)(logTimeUSec / USEC_PER_SEC);
|
||||||
|
|
||||||
|
bufferPos += strftime(logBuffer + bufferPos, LOG_BUFFER_SIZE - bufferPos, "%Y-%m-%d %H:%M:%S", localtime(&logTime));
|
||||||
|
bufferPos += snprintf(logBuffer + bufferPos, LOG_BUFFER_SIZE - bufferPos, ".%03d ", (int)(logTimeUSec / 1000 % 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add process and aligned log level
|
||||||
|
if (logLevel > logLevelStdErr)
|
||||||
|
bufferPos += snprintf(logBuffer + bufferPos, LOG_BUFFER_SIZE - bufferPos, "P00 %*s: ", 6, logLevelStr(logLevel));
|
||||||
|
// Else just the log level with no alignment
|
||||||
|
else
|
||||||
|
bufferPos += snprintf(logBuffer + bufferPos, LOG_BUFFER_SIZE - bufferPos, "%s: ", logLevelStr(logLevel));
|
||||||
|
|
||||||
|
// Add error code
|
||||||
|
if (code != 0)
|
||||||
|
bufferPos += snprintf(logBuffer + bufferPos, LOG_BUFFER_SIZE - bufferPos, "[%03d]: ", code);
|
||||||
|
|
||||||
|
// Add debug info
|
||||||
|
if (logLevelStdOut >= logLevelDebug)
|
||||||
|
{
|
||||||
|
bufferPos += snprintf(
|
||||||
|
logBuffer + bufferPos, LOG_BUFFER_SIZE - bufferPos, "%s:%s():%d: ", fileName, functionName, fileLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format message
|
||||||
|
va_list argumentList;
|
||||||
|
va_start(argumentList, format);
|
||||||
|
|
||||||
|
if (logLevel <= logLevelStdErr || strchr(format, '\n') == NULL)
|
||||||
|
bufferPos += vsnprintf(logBuffer + bufferPos, LOG_BUFFER_SIZE - bufferPos, format, argumentList);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vsnprintf(logFormat, LOG_BUFFER_SIZE, format, argumentList);
|
||||||
|
|
||||||
|
// Indent all lines after the first
|
||||||
|
const char *formatPtr = logFormat;
|
||||||
|
const char *linefeedPtr = strchr(logFormat, '\n');
|
||||||
|
int indentSize = 12;
|
||||||
|
|
||||||
|
while (linefeedPtr != NULL)
|
||||||
|
{
|
||||||
|
strncpy(logBuffer + bufferPos, formatPtr, linefeedPtr - formatPtr + 1);
|
||||||
|
bufferPos += linefeedPtr - formatPtr + 1;
|
||||||
|
|
||||||
|
formatPtr = linefeedPtr + 1;
|
||||||
|
linefeedPtr = strchr(formatPtr, '\n');
|
||||||
|
|
||||||
|
for (int indentIdx = 0; indentIdx < indentSize; indentIdx++)
|
||||||
|
logBuffer[bufferPos++] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(logBuffer + bufferPos, formatPtr);
|
||||||
|
bufferPos += strlen(formatPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(argumentList);
|
||||||
|
|
||||||
|
// Add linefeed
|
||||||
|
logBuffer[bufferPos++] = '\n';
|
||||||
|
logBuffer[bufferPos] = 0;
|
||||||
|
|
||||||
|
// Determine where to log the message based on log-level-stderr
|
||||||
|
int stream = logHandleStdOut;
|
||||||
|
|
||||||
|
if (logLevel <= logLevelStdErr)
|
||||||
|
stream = logHandleStdErr;
|
||||||
|
|
||||||
|
THROW_ON_SYS_ERROR(
|
||||||
|
write(stream, logBuffer, bufferPos) != bufferPos, FileWriteError, "unable to write log to stdout");
|
||||||
|
}
|
||||||
|
}
|
64
src/common/log.h
Normal file
64
src/common/log.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/***********************************************************************************************************************************
|
||||||
|
Log Handler
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#ifndef COMMON_LOG_H
|
||||||
|
#define COMMON_LOG_H
|
||||||
|
|
||||||
|
#include "common/type.h"
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Log types
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
logLevelOff,
|
||||||
|
logLevelAssert,
|
||||||
|
logLevelError,
|
||||||
|
logLevelProtocol,
|
||||||
|
logLevelWarn,
|
||||||
|
logLevelInfo,
|
||||||
|
logLevelDetail,
|
||||||
|
logLevelDebug,
|
||||||
|
logLevelTrace,
|
||||||
|
} LogLevel;
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Expose internal data for debugging/testing
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#ifndef NDEBUG
|
||||||
|
extern LogLevel logLevelStdOut;
|
||||||
|
extern LogLevel logLevelStdErr;
|
||||||
|
|
||||||
|
extern int logHandleStdOut;
|
||||||
|
extern int logHandleStdErr;
|
||||||
|
|
||||||
|
extern bool logTimestamp;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Functions
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void logInit(LogLevel logLevelStdOut, LogLevel logLevelStdErr, bool logTimestamp);
|
||||||
|
|
||||||
|
LogLevel logLevelEnum(const char *logLevel);
|
||||||
|
const char *logLevelStr(LogLevel logLevel);
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Macros
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#define LOG_ANY(logLevel, code, ...) \
|
||||||
|
logInternal(logLevel, __FILE__, __func__, __LINE__, code, __VA_ARGS__)
|
||||||
|
#define LOG_ERROR(code, ...) \
|
||||||
|
LOG_ANY(logLevelError, code, __VA_ARGS__)
|
||||||
|
#define LOG_INFO(...) \
|
||||||
|
LOG_ANY(logLevelInfo, 0, __VA_ARGS__)
|
||||||
|
#define LOG_WARN(...) \
|
||||||
|
LOG_ANY(logLevelWarn, 0, __VA_ARGS__)
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Internal Functions
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void logInternal(
|
||||||
|
LogLevel logLevel, const char *fileName, const char *functionName, int fileLine, int code, const char *format, ...);
|
||||||
|
|
||||||
|
#endif
|
39
src/config/load.c
Normal file
39
src/config/load.c
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/***********************************************************************************************************************************
|
||||||
|
Configuration Load
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common/memContext.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "config/config.h"
|
||||||
|
#include "config/parse.h"
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Load the configuration
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void
|
||||||
|
cfgLoad(int argListSize, const char *argList[])
|
||||||
|
{
|
||||||
|
MEM_CONTEXT_TEMP_BEGIN()
|
||||||
|
{
|
||||||
|
// Parse config from command line and config file
|
||||||
|
configParse(argListSize, argList);
|
||||||
|
|
||||||
|
// Initialize logging
|
||||||
|
LogLevel logLevelConsole = logLevelOff;
|
||||||
|
LogLevel logLevelStdErr = logLevelOff;
|
||||||
|
bool logTimestamp = true;
|
||||||
|
|
||||||
|
if (cfgOptionValid(cfgOptLogLevelConsole))
|
||||||
|
logLevelConsole = logLevelEnum(strPtr(cfgOptionStr(cfgOptLogLevelConsole)));
|
||||||
|
|
||||||
|
if (cfgOptionValid(cfgOptLogLevelStderr))
|
||||||
|
logLevelStdErr = logLevelEnum(strPtr(cfgOptionStr(cfgOptLogLevelStderr)));
|
||||||
|
|
||||||
|
if (cfgOptionValid(cfgOptLogTimestamp))
|
||||||
|
logTimestamp = cfgOptionBool(cfgOptLogTimestamp);
|
||||||
|
|
||||||
|
logInit(logLevelConsole, logLevelStdErr, logTimestamp);
|
||||||
|
}
|
||||||
|
MEM_CONTEXT_TEMP_END();
|
||||||
|
}
|
12
src/config/load.h
Normal file
12
src/config/load.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/***********************************************************************************************************************************
|
||||||
|
Configuration Load
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#ifndef CONFIG_LOAD_H
|
||||||
|
#define CONFIG_LOAD_H
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Functions
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void cfgLoad(int argListSize, const char *argList[]);
|
||||||
|
|
||||||
|
#endif
|
12
src/main.c
12
src/main.c
@ -5,8 +5,9 @@ Main
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
|
#include "common/log.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "config/parse.h"
|
#include "config/load.h"
|
||||||
#include "perl/exec.h"
|
#include "perl/exec.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
@ -14,10 +15,12 @@ int main(int argListSize, const char *argList[])
|
|||||||
{
|
{
|
||||||
TRY_BEGIN()
|
TRY_BEGIN()
|
||||||
{
|
{
|
||||||
// Parse command line
|
// Load the configuration
|
||||||
configParse(argListSize, argList);
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
|
cfgLoad(argListSize, argList);
|
||||||
|
|
||||||
// Display version
|
// Display version
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
if (!cfgCommandHelp() && cfgCommand() == cfgCmdVersion)
|
if (!cfgCommandHelp() && cfgCommand() == cfgCmdVersion)
|
||||||
{
|
{
|
||||||
printf(PGBACKREST_NAME " " PGBACKREST_VERSION "\n");
|
printf(PGBACKREST_NAME " " PGBACKREST_VERSION "\n");
|
||||||
@ -30,8 +33,7 @@ int main(int argListSize, const char *argList[])
|
|||||||
}
|
}
|
||||||
CATCH_ANY()
|
CATCH_ANY()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "ERROR [%03d]: %s\n", errorCode(), errorMessage());
|
LOG_ERROR(errorCode(), errorMessage());
|
||||||
fflush(stderr);
|
|
||||||
exit(errorCode());
|
exit(errorCode());
|
||||||
}
|
}
|
||||||
TRY_END();
|
TRY_END();
|
||||||
|
@ -442,7 +442,7 @@ full backup - invalid cmd line (db-master host)
|
|||||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --no-online --type=full --stanza=bogus backup
|
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --no-online --type=full --stanza=bogus backup
|
||||||
------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------
|
||||||
STDERR:
|
STDERR:
|
||||||
ERROR [037]: backup command requires option: db1-path
|
ERROR: [037]: backup command requires option: db1-path
|
||||||
HINT: does this stanza exist?
|
HINT: does this stanza exist?
|
||||||
|
|
||||||
stop all stanzas (db-master host)
|
stop all stanzas (db-master host)
|
||||||
|
@ -101,18 +101,28 @@ my $oTestDef =
|
|||||||
&TESTDEF_NAME => 'type',
|
&TESTDEF_NAME => 'type',
|
||||||
&TESTDEF_TOTAL => 2,
|
&TESTDEF_TOTAL => 2,
|
||||||
&TESTDEF_C => true,
|
&TESTDEF_C => true,
|
||||||
&TESTDEF_CDEF => '-DNO_ERROR',
|
&TESTDEF_CDEF => '-DNO_ERROR -DNO_LOG',
|
||||||
|
|
||||||
&TESTDEF_COVERAGE =>
|
&TESTDEF_COVERAGE =>
|
||||||
{
|
{
|
||||||
'common/type' => TESTDEF_COVERAGE_NOCODE,
|
'common/type' => TESTDEF_COVERAGE_NOCODE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
&TESTDEF_NAME => 'time',
|
||||||
|
&TESTDEF_TOTAL => 2,
|
||||||
|
&TESTDEF_C => true,
|
||||||
|
|
||||||
|
&TESTDEF_COVERAGE =>
|
||||||
|
{
|
||||||
|
'common/time' => TESTDEF_COVERAGE_FULL,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
&TESTDEF_NAME => 'error',
|
&TESTDEF_NAME => 'error',
|
||||||
&TESTDEF_TOTAL => 6,
|
&TESTDEF_TOTAL => 6,
|
||||||
&TESTDEF_C => true,
|
&TESTDEF_C => true,
|
||||||
&TESTDEF_CDEF => '-DNO_ERROR',
|
&TESTDEF_CDEF => '-DNO_ERROR -DNO_LOG',
|
||||||
|
|
||||||
&TESTDEF_COVERAGE =>
|
&TESTDEF_COVERAGE =>
|
||||||
{
|
{
|
||||||
@ -124,7 +134,7 @@ my $oTestDef =
|
|||||||
&TESTDEF_NAME => 'mem-context',
|
&TESTDEF_NAME => 'mem-context',
|
||||||
&TESTDEF_TOTAL => 6,
|
&TESTDEF_TOTAL => 6,
|
||||||
&TESTDEF_C => true,
|
&TESTDEF_C => true,
|
||||||
&TESTDEF_CDEF => '-DNO_MEM_CONTEXT',
|
&TESTDEF_CDEF => '-DNO_MEM_CONTEXT -DNO_LOG',
|
||||||
|
|
||||||
&TESTDEF_COVERAGE =>
|
&TESTDEF_COVERAGE =>
|
||||||
{
|
{
|
||||||
@ -132,13 +142,14 @@ my $oTestDef =
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
&TESTDEF_NAME => 'time',
|
&TESTDEF_NAME => 'log',
|
||||||
&TESTDEF_TOTAL => 2,
|
&TESTDEF_TOTAL => 3,
|
||||||
&TESTDEF_C => true,
|
&TESTDEF_C => true,
|
||||||
|
&TESTDEF_CDEF => '-DNO_LOG',
|
||||||
|
|
||||||
&TESTDEF_COVERAGE =>
|
&TESTDEF_COVERAGE =>
|
||||||
{
|
{
|
||||||
'common/time' => TESTDEF_COVERAGE_FULL,
|
'common/log' => TESTDEF_COVERAGE_FULL,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -433,6 +444,17 @@ my $oTestDef =
|
|||||||
'config/parse.auto' => TESTDEF_COVERAGE_NOCODE,
|
'config/parse.auto' => TESTDEF_COVERAGE_NOCODE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
&TESTDEF_NAME => 'load',
|
||||||
|
&TESTDEF_TOTAL => 1,
|
||||||
|
&TESTDEF_C => true,
|
||||||
|
&TESTDEF_CDEF => '-DNO_ERROR -DNO_LOG',
|
||||||
|
|
||||||
|
&TESTDEF_COVERAGE =>
|
||||||
|
{
|
||||||
|
'config/load' => TESTDEF_COVERAGE_FULL,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
&TESTDEF_NAME => 'unit',
|
&TESTDEF_NAME => 'unit',
|
||||||
&TESTDEF_TOTAL => 1,
|
&TESTDEF_TOTAL => 1,
|
||||||
|
@ -313,8 +313,9 @@ sub run
|
|||||||
' -Werror -Wfatal-errors -Wall -Wextra -Wwrite-strings -Wno-clobbered' .
|
' -Werror -Wfatal-errors -Wall -Wextra -Wwrite-strings -Wno-clobbered' .
|
||||||
($self->{oTest}->{&TEST_VM} ne VM_CO6 && $self->{oTest}->{&TEST_VM} ne VM_U12 ? ' -Wpedantic' : '') .
|
($self->{oTest}->{&TEST_VM} ne VM_CO6 && $self->{oTest}->{&TEST_VM} ne VM_U12 ? ' -Wpedantic' : '') .
|
||||||
" -I$self->{strBackRestBase}/src -I$self->{strBackRestBase}/test/src test.c" .
|
" -I$self->{strBackRestBase}/src -I$self->{strBackRestBase}/test/src test.c" .
|
||||||
" $self->{strBackRestBase}/test/src/common/harnessTest.c " .
|
" $self->{strBackRestBase}/test/src/common/harnessTest.c" .
|
||||||
join(' ', @stryCFile) . " -l crypto -o test";
|
" $self->{strBackRestBase}/test/src/common/logTest.c" .
|
||||||
|
' ' . join(' ', @stryCFile) . " -l crypto -o test";
|
||||||
|
|
||||||
executeTest(
|
executeTest(
|
||||||
'docker exec -i -u ' . TEST_USER . " ${strImage} bash -l -c '" .
|
'docker exec -i -u ' . TEST_USER . " ${strImage} bash -l -c '" .
|
||||||
|
57
test/src/common/logTest.c
Normal file
57
test/src/common/logTest.c
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/***********************************************************************************************************************************
|
||||||
|
Log Test Harness
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "common/harnessTest.h"
|
||||||
|
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/type.h"
|
||||||
|
#include "storage/helper.h"
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Name of file where logs are stored for testing
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
String *stdoutFile = NULL;
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Initialize log for testing
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void
|
||||||
|
testLogInit()
|
||||||
|
{
|
||||||
|
logInit(logLevelInfo, logLevelOff, false);
|
||||||
|
|
||||||
|
stdoutFile = strNewFmt("%s/stdout.log", testPath());
|
||||||
|
logHandleStdOut = open(strPtr(stdoutFile), O_WRONLY | O_CREAT | O_TRUNC, 0640);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Compare log to a static string
|
||||||
|
|
||||||
|
After the comparison the log is cleared so the next result can be compared.
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void
|
||||||
|
testLogResult(const char *expected)
|
||||||
|
{
|
||||||
|
String *actual = strTrim(strNewBuf(storageGet(storageLocal(), stdoutFile, false)));
|
||||||
|
|
||||||
|
if (!strEqZ(actual, expected))
|
||||||
|
THROW(AssertError, "\n\nexpected log:\n\n%s\n\nbut actual log was:\n\n%s\n\n", expected, strPtr(actual));
|
||||||
|
|
||||||
|
close(logHandleStdOut);
|
||||||
|
logHandleStdOut = open(strPtr(stdoutFile), O_WRONLY | O_CREAT | O_TRUNC, 0640);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Make sure nothing is left in the log after all tests have completed
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void
|
||||||
|
testLogFinal()
|
||||||
|
{
|
||||||
|
String *actual = strTrim(strNewBuf(storageGet(storageLocal(), stdoutFile, false)));
|
||||||
|
|
||||||
|
if (!strEqZ(actual, ""))
|
||||||
|
THROW(AssertError, "\n\nexpected log to be empty but actual log was:\n\n%s\n\n", strPtr(actual));
|
||||||
|
}
|
14
test/src/common/logTest.h
Normal file
14
test/src/common/logTest.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/***********************************************************************************************************************************
|
||||||
|
Log Test Harness
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#ifndef TEST_COMMON_LOG_H
|
||||||
|
#define TEST_COMMON_LOG_H
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Functions
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void testLogInit();
|
||||||
|
void testLogResult(const char *expected);
|
||||||
|
void testLogFinal();
|
||||||
|
|
||||||
|
#endif
|
95
test/src/module/common/logTest.c
Normal file
95
test/src/module/common/logTest.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/***********************************************************************************************************************************
|
||||||
|
Test Log Handler
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <common/regExp.h>
|
||||||
|
#include <storage/storage.h>
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Test Run
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void testRun()
|
||||||
|
{
|
||||||
|
// *****************************************************************************************************************************
|
||||||
|
if (testBegin("logLevelEnum() and logLevelStr()"))
|
||||||
|
{
|
||||||
|
TEST_ERROR(logLevelEnum(BOGUS_STR), AssertError, "log level 'BOGUS' not found");
|
||||||
|
TEST_RESULT_INT(logLevelEnum("OFF"), logLevelOff, "log level 'OFF' found");
|
||||||
|
TEST_RESULT_INT(logLevelEnum("info"), logLevelInfo, "log level 'info' found");
|
||||||
|
TEST_RESULT_INT(logLevelEnum("TRACE"), logLevelTrace, "log level 'TRACE' found");
|
||||||
|
|
||||||
|
TEST_ERROR(logLevelStr(999), AssertError, "invalid log level '999'");
|
||||||
|
TEST_RESULT_STR(logLevelStr(logLevelOff), "OFF", "log level 'OFF' found");
|
||||||
|
TEST_RESULT_STR(logLevelStr(logLevelInfo), "INFO", "log level 'INFO' found");
|
||||||
|
TEST_RESULT_STR(logLevelStr(logLevelTrace), "TRACE", "log level 'TRACE' found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// *****************************************************************************************************************************
|
||||||
|
if (testBegin("logInit()"))
|
||||||
|
{
|
||||||
|
TEST_RESULT_INT(logLevelStdOut, logLevelOff, "console logging is off");
|
||||||
|
TEST_RESULT_INT(logLevelStdErr, logLevelWarn, "stderr logging is warn");
|
||||||
|
TEST_RESULT_VOID(logInit(logLevelInfo, logLevelError, true), "init logging");
|
||||||
|
TEST_RESULT_INT(logLevelStdOut, logLevelInfo, "console logging is info");
|
||||||
|
TEST_RESULT_INT(logLevelStdErr, logLevelError, "stderr logging is error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// *****************************************************************************************************************************
|
||||||
|
if (testBegin("logInternal()"))
|
||||||
|
{
|
||||||
|
TEST_RESULT_VOID(logInit(logLevelWarn, logLevelOff, true), "init logging to warn (timestamp on)");
|
||||||
|
TEST_RESULT_VOID(logInternal(logLevelWarn, NULL, NULL, 0, 0, "TEST"), "log timestamp");
|
||||||
|
|
||||||
|
String *logTime = strNewN(logBuffer, 23);
|
||||||
|
TEST_RESULT_BOOL(
|
||||||
|
regExpMatchOne(
|
||||||
|
strNew("^20[0-9]{2}\\-[0-1][0-9]\\-[0-3][0-9] [0-2][0-9]\\:[0-5][0-9]\\:[0-5][0-9]\\.[0-9]{3}$"), logTime),
|
||||||
|
true, "check timestamp format: %s", strPtr(logTime));
|
||||||
|
|
||||||
|
// Redirect output to files
|
||||||
|
TEST_RESULT_VOID(logInit(logLevelWarn, logLevelOff, false), "init logging to warn (timestamp off)");
|
||||||
|
|
||||||
|
String *stdoutFile = strNewFmt("%s/stdout.log", testPath());
|
||||||
|
String *stderrFile = strNewFmt("%s/stderr.log", testPath());
|
||||||
|
|
||||||
|
logHandleStdOut = open(strPtr(stdoutFile), O_WRONLY | O_CREAT | O_TRUNC, 0640);
|
||||||
|
logHandleStdErr = open(strPtr(stderrFile), O_WRONLY | O_CREAT | O_TRUNC, 0640);
|
||||||
|
|
||||||
|
logBuffer[0] = 0;
|
||||||
|
TEST_RESULT_VOID(logInternal(logLevelWarn, NULL, NULL, 0, 0, "format %d", 99), "log warn");
|
||||||
|
TEST_RESULT_STR(logBuffer, "P00 WARN: format 99\n", " check log");
|
||||||
|
|
||||||
|
logBuffer[0] = 0;
|
||||||
|
TEST_RESULT_VOID(logInternal(logLevelError, NULL, NULL, 0, 25, "message"), "log error");
|
||||||
|
TEST_RESULT_STR(logBuffer, "P00 ERROR: [025]: message\n", " check log");
|
||||||
|
|
||||||
|
logBuffer[0] = 0;
|
||||||
|
TEST_RESULT_VOID(logInternal(logLevelError, NULL, NULL, 0, 25, "message1\nmessage2"), "log error with multiple lines");
|
||||||
|
TEST_RESULT_STR(logBuffer, "P00 ERROR: [025]: message1\n message2\n", " check log");
|
||||||
|
|
||||||
|
TEST_RESULT_VOID(logInit(logLevelDebug, logLevelDebug, false), "init logging to debug");
|
||||||
|
|
||||||
|
logBuffer[0] = 0;
|
||||||
|
TEST_RESULT_VOID(logInternal(logLevelError, "test.c", "test_func", 33, 25, "message"), "log error");
|
||||||
|
TEST_RESULT_STR(logBuffer, "ERROR: [025]: test.c:test_func():33: message\n", " check log");
|
||||||
|
|
||||||
|
// Check stdout
|
||||||
|
Storage *storage = storageNew(strNew(testPath()), 0750, 65536, NULL);
|
||||||
|
|
||||||
|
TEST_RESULT_STR(
|
||||||
|
strPtr(strNewBuf(storageGet(storage, stdoutFile, false))),
|
||||||
|
"P00 WARN: format 99\n"
|
||||||
|
"P00 ERROR: [025]: message\n"
|
||||||
|
"P00 ERROR: [025]: message1\n"
|
||||||
|
" message2\n",
|
||||||
|
"checkout stdout output");
|
||||||
|
|
||||||
|
// Check stderr
|
||||||
|
TEST_RESULT_STR(
|
||||||
|
strPtr(strNewBuf(storageGet(storage, stderrFile, false))),
|
||||||
|
"ERROR: [025]: test.c:test_func():33: message\n",
|
||||||
|
"checkout stderr output");
|
||||||
|
}
|
||||||
|
}
|
53
test/src/module/config/loadTest.c
Normal file
53
test/src/module/config/loadTest.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/***********************************************************************************************************************************
|
||||||
|
Test Configuration Load
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
#include "common/log.h"
|
||||||
|
|
||||||
|
/***********************************************************************************************************************************
|
||||||
|
Test run
|
||||||
|
***********************************************************************************************************************************/
|
||||||
|
void testRun()
|
||||||
|
{
|
||||||
|
// *****************************************************************************************************************************
|
||||||
|
if (testBegin("cfgLoad()"))
|
||||||
|
{
|
||||||
|
StringList *argList = strLstNew();
|
||||||
|
strLstAdd(argList, strNew("pgbackrest"));
|
||||||
|
strLstAdd(argList, strNew("archive-get"));
|
||||||
|
|
||||||
|
TEST_ERROR(
|
||||||
|
cfgLoad(strLstSize(argList), strLstPtr(argList)), OptionRequiredError,
|
||||||
|
"archive-get command requires option: stanza");
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST_RESULT_INT(logLevelStdOut, logLevelOff, "console logging is off");
|
||||||
|
TEST_RESULT_INT(logLevelStdErr, logLevelWarn, "stderr logging is warn");
|
||||||
|
|
||||||
|
argList = strLstNew();
|
||||||
|
strLstAdd(argList, strNew("pgbackrest"));
|
||||||
|
strLstAdd(argList, strNew("--host-id=1"));
|
||||||
|
strLstAdd(argList, strNew("--process=1"));
|
||||||
|
strLstAdd(argList, strNew("--command=backup"));
|
||||||
|
strLstAdd(argList, strNew("--stanza=db"));
|
||||||
|
strLstAdd(argList, strNew("--type=backup"));
|
||||||
|
strLstAdd(argList, strNew("local"));
|
||||||
|
|
||||||
|
TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load local config");
|
||||||
|
|
||||||
|
TEST_RESULT_INT(logLevelStdOut, logLevelOff, "console logging is off");
|
||||||
|
TEST_RESULT_INT(logLevelStdErr, logLevelWarn, "stderr logging is warn");
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
|
argList = strLstNew();
|
||||||
|
strLstAdd(argList, strNew("pgbackrest"));
|
||||||
|
strLstAdd(argList, strNew("--stanza=db"));
|
||||||
|
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||||
|
strLstAdd(argList, strNew("--repo-path=/path/to/repo"));
|
||||||
|
strLstAdd(argList, strNew("stanza-create"));
|
||||||
|
|
||||||
|
TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load local config");
|
||||||
|
|
||||||
|
TEST_RESULT_INT(logLevelStdOut, logLevelWarn, "console logging is off");
|
||||||
|
TEST_RESULT_INT(logLevelStdErr, logLevelOff, "stderr logging is off");
|
||||||
|
}
|
||||||
|
}
|
@ -301,6 +301,8 @@ void testRun()
|
|||||||
TEST_RESULT_INT(cfgOptionSource(cfgOptOnline), cfgSourceParam, " online is source default");
|
TEST_RESULT_INT(cfgOptionSource(cfgOptOnline), cfgSourceParam, " online is source default");
|
||||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptCompress), true, " compress is set");
|
TEST_RESULT_BOOL(cfgOptionBool(cfgOptCompress), true, " compress is set");
|
||||||
TEST_RESULT_INT(cfgOptionSource(cfgOptCompress), cfgSourceDefault, " compress is source default");
|
TEST_RESULT_INT(cfgOptionSource(cfgOptCompress), cfgSourceDefault, " compress is source default");
|
||||||
|
TEST_RESULT_INT(cfgOptionInt(cfgOptBufferSize), 4194304, " buffer-size is set");
|
||||||
|
TEST_RESULT_INT(cfgOptionSource(cfgOptBufferSize), cfgSourceDefault, " buffer-size is source default");
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
argList = strLstNew();
|
argList = strLstNew();
|
||||||
|
@ -86,7 +86,7 @@ void testRun()
|
|||||||
TEST_RESULT_PTR(storageList(storage, strNew(BOGUS_STR), NULL, true), NULL, "ignore missing dir");
|
TEST_RESULT_PTR(storageList(storage, strNew(BOGUS_STR), NULL, true), NULL, "ignore missing dir");
|
||||||
|
|
||||||
TEST_RESULT_VOID(storagePut(storage, strNew("aaa.txt"), bufNewStr(strNew("aaa"))), "write aaa.text");
|
TEST_RESULT_VOID(storagePut(storage, strNew("aaa.txt"), bufNewStr(strNew("aaa"))), "write aaa.text");
|
||||||
TEST_RESULT_STR(strPtr(strLstJoin(storageList(storage, NULL, NULL, false), ", ")), "aaa.txt", "dir list");
|
TEST_RESULT_STR(strPtr(strLstJoin(storageList(storage, NULL, NULL, false), ", ")), "aaa.txt, stdout.log", "dir list");
|
||||||
|
|
||||||
TEST_RESULT_VOID(storagePut(storage, strNew("bbb.txt"), bufNewStr(strNew("bbb"))), "write bbb.text");
|
TEST_RESULT_VOID(storagePut(storage, strNew("bbb.txt"), bufNewStr(strNew("bbb"))), "write bbb.text");
|
||||||
TEST_RESULT_STR(strPtr(strLstJoin(storageList(storage, NULL, strNew("^bbb"), false), ", ")), "bbb.txt", "dir list");
|
TEST_RESULT_STR(strPtr(strLstJoin(storageList(storage, NULL, strNew("^bbb"), false), ", ")), "bbb.txt", "dir list");
|
||||||
|
@ -5,7 +5,9 @@ This wrapper runs the the C unit tests.
|
|||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#ifndef NO_ERROR
|
#ifndef NO_ERROR
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
@ -13,6 +15,10 @@ This wrapper runs the the C unit tests.
|
|||||||
|
|
||||||
#include "common/harnessTest.h"
|
#include "common/harnessTest.h"
|
||||||
|
|
||||||
|
#ifndef NO_LOG
|
||||||
|
#include "common/logTest.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef NO_MEM_CONTEXT
|
#ifndef NO_MEM_CONTEXT
|
||||||
#include "common/memContext.h"
|
#include "common/memContext.h"
|
||||||
#endif
|
#endif
|
||||||
@ -36,9 +42,17 @@ main - run the tests
|
|||||||
int
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
|
// Set neutral umask for testing
|
||||||
|
umask(0000);
|
||||||
|
|
||||||
// Set globals
|
// Set globals
|
||||||
testPathSet("{[C_TEST_PATH]}");
|
testPathSet("{[C_TEST_PATH]}");
|
||||||
|
|
||||||
|
#ifndef NO_LOG
|
||||||
|
// Initialize logging
|
||||||
|
testLogInit();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Initialize tests
|
// Initialize tests
|
||||||
// run, selected
|
// run, selected
|
||||||
{[C_TEST_LIST]}
|
{[C_TEST_LIST]}
|
||||||
@ -47,18 +61,26 @@ main(void)
|
|||||||
TRY_BEGIN()
|
TRY_BEGIN()
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
// Run the tests
|
// Run the tests
|
||||||
testRun();
|
testRun();
|
||||||
|
|
||||||
// End test run and make sure all tests completed
|
#ifndef NO_LOG
|
||||||
testComplete();
|
// Make sure there is nothing untested left in the log
|
||||||
|
testLogFinal();
|
||||||
|
#endif
|
||||||
|
|
||||||
printf("\nTESTS COMPLETED SUCCESSFULLY\n");
|
// End test run and make sure all tests completed
|
||||||
|
testComplete();
|
||||||
|
|
||||||
|
printf("\nTESTS COMPLETED SUCCESSFULLY\n");
|
||||||
|
fflush(stdout);
|
||||||
#ifndef NO_ERROR
|
#ifndef NO_ERROR
|
||||||
}
|
}
|
||||||
CATCH_ANY()
|
CATCH_ANY()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "TEST FAILED WITH %s, \"%s\" at %s:%d\n", errorName(), errorMessage(), errorFileName(), errorFileLine());
|
fprintf(stderr, "TEST FAILED WITH %s, \"%s\" at %s:%d\n", errorName(), errorMessage(), errorFileName(), errorFileLine());
|
||||||
|
fflush(stderr);
|
||||||
|
exit(errorCode());
|
||||||
}
|
}
|
||||||
#ifndef NO_MEM_CONTEXT
|
#ifndef NO_MEM_CONTEXT
|
||||||
FINALLY()
|
FINALLY()
|
||||||
|
Reference in New Issue
Block a user