aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichał Łyszczek <michal.lyszczek@bofc.pl>2018-04-23 00:30:25 +0200
committerMichał Łyszczek <michal.lyszczek@bofc.pl>2018-04-23 00:30:25 +0200
commitabae42af13181ebb130cd570e1acf37f763dbde0 (patch)
treebaa99fa0de7752bbb4b1368e45145c0a814e1adb
parentf27539bc6456930bd7e76eb894e8953e31cc122c (diff)
parent3ab8e312386833c1d3afbb46503661f596c406bb (diff)
downloadembedlog-abae42af13181ebb130cd570e1acf37f763dbde0.tar.gz
embedlog-abae42af13181ebb130cd570e1acf37f763dbde0.tar.bz2
embedlog-abae42af13181ebb130cd570e1acf37f763dbde0.zip
Merge branch 'nuttx-support'
-rw-r--r--configure.ac109
-rw-r--r--embedlog-sources.mk7
l---------examples/el-decode-number.c1
l---------examples/el-encode-number.c1
l---------examples/el-pbinary.c1
l---------examples/el-ts.c1
-rw-r--r--examples/print-options.c39
-rw-r--r--examples/print-to-file.c2
-rw-r--r--examples/print-tty.c2
-rw-r--r--include/embedlog.h80
-rw-r--r--man/el_init.32
-rw-r--r--man/el_option.3152
-rw-r--r--man/el_print.3126
-rw-r--r--readme.md6
-rw-r--r--src/Makefile.am7
-rw-r--r--src/el-decode-number.c86
-rw-r--r--src/el-encode-number.c112
-rw-r--r--src/el-file.c362
-rw-r--r--src/el-file.h15
-rw-r--r--src/el-options.c94
-rw-r--r--src/el-options.h18
-rw-r--r--src/el-pbinary.c224
-rw-r--r--src/el-perror.c9
-rw-r--r--src/el-pmemory.c5
-rw-r--r--src/el-print.c278
-rw-r--r--src/el-private.h (renamed from src/config-priv.h)153
-rw-r--r--src/el-puts.c83
-rw-r--r--src/el-syslog.c170
-rw-r--r--src/el-ts.c300
-rw-r--r--src/el-tty.c46
-rw-r--r--src/el-tty.h16
-rw-r--r--src/snprintf.c2
-rwxr-xr-xtest-compilation.sh6
-rw-r--r--tst/Makefile.am2
l---------tst/el-decode-number.c1
l---------tst/el-encode-number.c1
l---------tst/el-pbinary.c1
l---------tst/el-ts.c1
-rw-r--r--tst/main.c2
-rw-r--r--tst/test-el-file.c643
-rw-r--r--tst/test-el-options.c141
-rw-r--r--tst/test-el-pbinary.c725
-rw-r--r--tst/test-el-print.c363
-rw-r--r--tst/test-group-list.h1
44 files changed, 3843 insertions, 553 deletions
diff --git a/configure.ac b/configure.ac
index 1328e3d..163be32 100644
--- a/configure.ac
+++ b/configure.ac
@@ -78,7 +78,6 @@ AC_ARG_ENABLE([out-stderr],
AS_IF([test "x$enable_out_stderr" = "xyes"],
[
AC_DEFINE([ENABLE_OUT_STDERR], [1], [Enable printing to stderr])
- AC_CHECK_FUNCS(access)
AC_CHECK_FUNCS([fputs],,
AC_MSG_ERROR(not found, needed by --enable-out-stderr))
])
@@ -97,6 +96,7 @@ AM_CONDITIONAL([ENABLE_OUT_FILE], [test "x$enable_out_file" = "xyes"])
AS_IF([test "x$enable_out_file" = "xyes"],
[
AC_DEFINE([ENABLE_OUT_FILE], [1], [Enable printing to file])
+ AC_CHECK_FUNCS(access stat fsync fileno)
AC_CHECK_FUNCS([fopen fclose fwrite remove rename],,
AC_MSG_ERROR(not found, needed by --enable-out-file))
])
@@ -115,6 +115,7 @@ AM_CONDITIONAL([ENABLE_OUT_TTY], [test "x$enable_out_tty" = "xyes"])
AS_IF([test "x$enable_out_tty" = "xyes"],
[
AC_DEFINE([ENABLE_OUT_TTY], [1], [Enable printing to tty])
+ AC_CHECK_HEADERS(termios.h)
AC_CHECK_FUNCS([open tcgetattr cfsetispeed tcsetattr close],,
AC_MSG_ERROR(not found, needed by --enable-out-tty))
])
@@ -150,12 +151,29 @@ AC_ARG_ENABLE([timestamp],
AS_IF([test "x$enable_timestamp" = "xyes"],
[
AC_DEFINE([ENABLE_TIMESTAMP], [1], [Enable printing timestamp with log])
- AC_CHECK_FUNCS([clock time],,
+ AC_CHECK_FUNCS([clock time sprintf],,
AC_MSG_ERROR(not found, needed by --enable-timestamp))
])
###
+# --enable-fractions
+#
+
+
+AC_ARG_ENABLE([fractions],
+ AS_HELP_STRING([--enable-fractions], [Enable printing fractions with log]),
+ [], [enable_fractions="yes"])
+
+AS_IF([test "x$enable_fractions" = "xyes"],
+[
+ AC_DEFINE([ENABLE_FRACTIONS], [1], [Enable printing fractions with log])
+ AC_CHECK_FUNCS([sprintf],,
+ AC_MSG_ERROR(not found, needed by --enable-fractions))
+])
+
+
+###
# --enable-realtime
#
@@ -200,6 +218,63 @@ AS_IF([test "x$enable_monotonic" = "xyes"],
###
+# --enable-clock
+#
+
+
+AC_ARG_ENABLE([clock],
+ AS_HELP_STRING([--enable-clock], [Enable using clock() as tme source in log]),
+ [], [enable_clock="yes"])
+
+AS_IF([test "x$enable_clock" = "xyes"],
+[
+ AS_IF([test "x$enable_timestamp" != "xyes"],
+ [
+ AC_MSG_WARN(--enable-clock without --enable-timestamp has no effect)
+ ])
+
+ AC_DEFINE([ENABLE_CLOCK], [1], [Enable using clock() as time source in log])
+ AC_CHECK_FUNCS([clock],,
+ AC_MSG_ERROR(not found, needed by --enable-clock))
+])
+
+
+###
+# --enable-binary
+#
+
+
+AC_ARG_ENABLE([binary-logs],
+ AS_HELP_STRING([--enable-binary-logs], [Enable printing binary logs]),
+ [], [enable_binary_logs="no"])
+
+AM_CONDITIONAL([ENABLE_BINARY_LOGS], [test "x$enable_binary_logs" = "xyes"])
+AS_IF([test "x$enable_binary_logs" = "xyes"],
+[
+ AC_DEFINE([ENABLE_BINARY_LOGS], [1], [Enable printing binary logs])
+ AC_CHECK_FUNCS([memcpy],,
+ AC_MSG_ERROR(not found, needed by --enable-binary-logs))
+])
+
+
+###
+# --enable-prefix
+#
+
+
+AC_ARG_ENABLE([prefix],
+ AS_HELP_STRING([--enable-prefix], [Enable printing prefix to each message]),
+ [], [enable_prefix="yes"])
+
+AS_IF([test "x$enable_prefix" = "xyes"],
+[
+ AC_DEFINE([ENABLE_PREFIX], [1], [Enable printing prefix to each message])
+ AC_CHECK_FUNCS([strncat strlen],,
+ AC_MSG_ERROR(not found, needed by --enable-prefix))
+])
+
+
+###
# --enable-finfo
#
@@ -234,6 +309,21 @@ AS_IF([test "x$enable_colors" = "xyes"],
###
+# --enable-colors-extended
+#
+
+
+AC_ARG_ENABLE([colors-extended],
+ AS_HELP_STRING([--enable-colors-extended], [Enable more colors in logs]),
+ [], [enable_colors_extended="no"])
+
+AS_IF([test "x$enable_colors_extended" = "xyes"],
+[
+ AC_DEFINE([ENABLE_COLORS_EXTENDED], [1], [Enable more colors in logs])
+])
+
+
+###
# --enable-reentrant
#
@@ -254,6 +344,15 @@ AS_IF([test "x$enable_reentrant" = "xyes"],
# VARIABLES=value options
#
+###
+# EL_PREFIX_MAX
+#
+
+
+AC_ARG_VAR([EL_PREFIX_MAX], [Maximum size of prefix])
+AS_IF([test "x$EL_PREFIX_MAX" = x], [EL_PREFIX_MAX="128"])
+AC_DEFINE_UNQUOTED([EL_PREFIX_MAX], [$EL_PREFIX_MAX], [Maximum size of prefix])
+
###
# EL_LOG_MAX
@@ -317,12 +416,18 @@ echo "print to file............... : $enable_out_file"
echo "print to tty................ : $enable_out_tty"
echo "print to custom routine..... : $enable_out_custom"
echo "print timestamp enabled..... : $enable_timestamp"
+echo "print fractions of seconds.. : $enable_fractions"
echo "clock_realtime timestamp.... : $enable_realtime"
echo "clock_monotonic timestamp... : $enable_monotonic"
+echo "clock() timestamp........... : $enable_clock"
+echo "print prefix with each msg.. : $enable_prefix"
echo "print file info with log.... : $enable_finfo"
echo "force portable snprintf......: $enable_portable_snprintf"
+echo "binary logs..................: $enable_binary_logs"
echo "colorize output............. : $enable_colors"
+echo "extended colors............. : $enable_colors_extended"
echo "reentrant functions......... : $enable_reentrant"
+echo "maximum prefix length....... : $EL_PREFIX_MAX"
echo "maximum file length......... : $EL_FLEN_MAX"
echo "maximum log message......... : $EL_LOG_MAX"
echo "maximum path length......... : $MAX_PATH"
diff --git a/embedlog-sources.mk b/embedlog-sources.mk
index eb779c0..591111e 100644
--- a/embedlog-sources.mk
+++ b/embedlog-sources.mk
@@ -3,6 +3,9 @@ embedlog_sources = el-options.c \
el-pmemory.c \
el-print.c \
el-puts.c \
+ el-ts.c \
+ el-decode-number.c \
+ el-encode-number.c \
snprintf.c
if ENABLE_OUT_FILE
@@ -12,3 +15,7 @@ endif
if ENABLE_OUT_TTY
embedlog_sources += el-tty.c
endif
+
+if ENABLE_BINARY_LOGS
+embedlog_sources += el-pbinary.c
+endif
diff --git a/examples/el-decode-number.c b/examples/el-decode-number.c
new file mode 120000
index 0000000..268c6a8
--- /dev/null
+++ b/examples/el-decode-number.c
@@ -0,0 +1 @@
+../src/el-decode-number.c \ No newline at end of file
diff --git a/examples/el-encode-number.c b/examples/el-encode-number.c
new file mode 120000
index 0000000..c90c2c0
--- /dev/null
+++ b/examples/el-encode-number.c
@@ -0,0 +1 @@
+../src/el-encode-number.c \ No newline at end of file
diff --git a/examples/el-pbinary.c b/examples/el-pbinary.c
new file mode 120000
index 0000000..3e46c13
--- /dev/null
+++ b/examples/el-pbinary.c
@@ -0,0 +1 @@
+../src/el-pbinary.c \ No newline at end of file
diff --git a/examples/el-ts.c b/examples/el-ts.c
new file mode 120000
index 0000000..478d0cd
--- /dev/null
+++ b/examples/el-ts.c
@@ -0,0 +1 @@
+../src/el-ts.c \ No newline at end of file
diff --git a/examples/print-options.c b/examples/print-options.c
index c9e02c0..2c619cb 100644
--- a/examples/print-options.c
+++ b/examples/print-options.c
@@ -5,13 +5,17 @@
#include "embedlog.h"
+#define EL_OPTIONS_OBJECT &opts
+
int main(void)
{
+ struct el_options opts;
+
el_init();
el_option(EL_OUT, EL_OUT_STDERR);
el_option(EL_PRINT_LEVEL, 0);
- el_print(ELI, "We can disable information about log level");
+ el_print(ELI, "We can disable information about log level\b");
el_print(ELF, "message still will be filtered by log level");
el_print(ELA, "but there is no way to tell what level message is");
el_print(ELD, "like this message will not be printed");
@@ -28,11 +32,21 @@ int main(void)
el_print(ELF, "if higher precision is needed we can use CLOCK_REALTIME");
el_option(EL_TS, EL_TS_SHORT);
el_print(ELF, "we can also mix REALTIME with short format");
+ el_option(EL_TS_FRACT, EL_TS_FRACT_OFF);
+ el_print(ELF, "and iff you don't need high resolution");
+ el_print(ELF, "you can disable fractions of seconds to save space!");
+ el_option(EL_TS_FRACT, EL_TS_FRACT_MS);
+ el_print(ELF, "or enable only millisecond resolution");
+ el_option(EL_TS_FRACT, EL_TS_FRACT_US);
+ el_print(ELF, "or enable only microsecond resolution");
+ el_option(EL_TS_FRACT, EL_TS_FRACT_NS);
+ el_print(ELF, "or enable only nanosecond resolution");
el_option(EL_TS, EL_TS_LONG);
el_option(EL_TS_TM, EL_TS_TM_CLOCK);
el_print(ELF, "or long with clock() if you desire");
el_option(EL_TS, EL_TS_OFF);
el_print(ELF, "no time information, if your heart desire it");
+ el_option(EL_TS_FRACT, EL_TS_FRACT_NS);
el_option(EL_FINFO, 1);
el_print(ELF, "log location is very usefull for debuging");
@@ -43,6 +57,12 @@ int main(void)
el_print(ELF, "Different scenarios need different options");
el_print(ELA, "So we can mix options however we want");
+ el_option(EL_PRINT_NL, 0);
+ el_print(ELF, "you can also remove printing new line ");
+ el_puts("to join el_print and el_puts in a single ");
+ el_puts("long line as needed\n");
+ el_option(EL_PRINT_NL, 1);
+
el_option(EL_COLORS, 1);
el_option(EL_LEVEL, EL_DBG);
el_print(ELD, "And if we have");
@@ -54,5 +74,22 @@ int main(void)
el_print(ELA, "glance into");
el_print(ELF, "log file");
+ el_option(EL_PREFIX, "embedlog: ");
+ el_print(ELI, "you can also use prefixes");
+ el_print(ELI, "to every message you send");
+
+ el_option(EL_PREFIX, NULL);
+ el_print(ELI, "set prefix to null to disable it");
+
el_cleanup();
+
+
+ el_oinit(&opts);
+ el_ooption(&opts, EL_OUT, EL_OUT_STDERR);
+ el_oprint(ELI, &opts, "you can do same thing as about with custom");
+ el_oprint(ELI, &opts, "options object for two or more logger.");
+ el_oprint(OELE, "and if you define EL_OPTIONS_OBJECT you will be");
+ el_oprint(OELF, "able to print messages without passing options");
+ el_oprint(OELW, "each time to print functions");
+ el_ocleanup(&opts);
}
diff --git a/examples/print-to-file.c b/examples/print-to-file.c
index aa2418e..961b73e 100644
--- a/examples/print-to-file.c
+++ b/examples/print-to-file.c
@@ -26,7 +26,7 @@ int main(void)
goto error;
}
- if (el_option(EL_FNAME, WORKDIR"/log") != 0)
+ if (el_option(EL_FPATH, WORKDIR"/log") != 0)
{
/*
* embedlog will try to open file now, this may fail for various of
diff --git a/examples/print-tty.c b/examples/print-tty.c
index a0c53af..e38cf57 100644
--- a/examples/print-tty.c
+++ b/examples/print-tty.c
@@ -27,7 +27,7 @@ int main(void)
* 8N1 by default. Baudrate should be taken from termios (3).
*/
- if (el_option(EL_TTY_DEV, "/dev/ttyUSB1", B115200) != 0)
+ if (el_option(EL_TTY_DEV, "/dev/ttyUSB1", B9600) != 0)
{
perror("tty set failed");
return 1;
diff --git a/include/embedlog.h b/include/embedlog.h
index fe22fe3..8615020 100644
--- a/include/embedlog.h
+++ b/include/embedlog.h
@@ -11,6 +11,10 @@
#include <stdarg.h>
#include <stdio.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifdef NOFINFO
# define ELF NULL, 0, EL_FATAL
# define ELA NULL, 0, EL_ALERT
@@ -20,6 +24,15 @@
# define ELN NULL, 0, EL_NOTICE
# define ELI NULL, 0, EL_INFO
# define ELD NULL, 0, EL_DBG
+
+# define OELF NULL, 0, EL_FATAL, EL_OPTIONS_OBJECT
+# define OELA NULL, 0, EL_ALERT, EL_OPTIONS_OBJECT
+# define OELC NULL, 0, EL_CRIT, EL_OPTIONS_OBJECT
+# define OELE NULL, 0, EL_ERROR, EL_OPTIONS_OBJECT
+# define OELW NULL, 0, EL_WARN, EL_OPTIONS_OBJECT
+# define OELN NULL, 0, EL_NOTICE, EL_OPTIONS_OBJECT
+# define OELI NULL, 0, EL_INFO, EL_OPTIONS_OBJECT
+# define OELD NULL, 0, EL_DBG, EL_OPTIONS_OBJECT
#else
# define ELF __FILE__, __LINE__, EL_FATAL
# define ELA __FILE__, __LINE__, EL_ALERT
@@ -29,6 +42,15 @@
# define ELN __FILE__, __LINE__, EL_NOTICE
# define ELI __FILE__, __LINE__, EL_INFO
# define ELD __FILE__, __LINE__, EL_DBG
+
+# define OELF __FILE__, __LINE__, EL_FATAL, EL_OPTIONS_OBJECT
+# define OELA __FILE__, __LINE__, EL_ALERT, EL_OPTIONS_OBJECT
+# define OELC __FILE__, __LINE__, EL_CRIT, EL_OPTIONS_OBJECT
+# define OELE __FILE__, __LINE__, EL_ERROR, EL_OPTIONS_OBJECT
+# define OELW __FILE__, __LINE__, EL_WARN, EL_OPTIONS_OBJECT
+# define OELN __FILE__, __LINE__, EL_NOTICE, EL_OPTIONS_OBJECT
+# define OELI __FILE__, __LINE__, EL_INFO, EL_OPTIONS_OBJECT
+# define OELD __FILE__, __LINE__, EL_DBG, EL_OPTIONS_OBJECT
#endif
#if (__STDC_VERSION__ >= 199901L)
@@ -70,14 +92,19 @@ enum el_option
EL_COLORS,
EL_TS,
EL_TS_TM,
+ EL_TS_FRACT,
EL_PRINT_LEVEL,
+ EL_PRINT_NL,
EL_FINFO,
EL_CUSTOM_PUTS,
EL_TTY_DEV,
+ EL_PREFIX,
- EL_FNAME,
+ EL_FPATH,
EL_FROTATE_NUMBER,
EL_FROTATE_SIZE,
+ EL_FILE_SYNC_EVERY,
+ EL_FILE_SYNC_LEVEL,
EL_OPT_ERROR /* internal use only */
};
@@ -93,33 +120,51 @@ enum el_option_timestamp
enum el_option_timestamp_timer
{
- EL_TS_TM_CLOCK,
EL_TS_TM_TIME,
+ EL_TS_TM_CLOCK,
EL_TS_TM_REALTIME,
EL_TS_TM_MONOTONIC,
EL_TS_TM_ERROR /* internal use only */
};
+enum el_option_timestamp_fractions
+{
+ EL_TS_FRACT_OFF,
+ EL_TS_FRACT_MS,
+ EL_TS_FRACT_US,
+ EL_TS_FRACT_NS,
+
+ EL_TS_FRACT_ERROR /* internal use onsly */
+};
+
typedef int (*el_custom_puts)(const char *s);
struct el_options
{
- int outputs;
- int level;
- int colors;
- int timestamp;
- int timestamp_timer;
- int print_log_level;
+ unsigned int outputs:6;
+ unsigned int colors:1;
+ unsigned int timestamp:2;
+ unsigned int timestamp_timer:3;
+ unsigned int timestamp_fractions:2;
+ unsigned int print_log_level:1;
+ unsigned int print_newline:1;
+ unsigned int finfo:1;
+ unsigned int level:3;
+ unsigned int level_current_msg:3;
+ unsigned int file_sync_level:3;
int serial_fd;
- int finfo;
- int frotate_number;
- int fcurrent_rotate;
- long frotate_size;
- long fpos;
+ unsigned int frotate_number;
+ unsigned int fcurrent_rotate;
+ unsigned long frotate_size;
+ unsigned long file_sync_every;
+ unsigned long written_after_sync;
+ unsigned long fpos;
FILE *file;
+ char *current_log;
const char *fname;
+ const char *prefix;
el_custom_puts custom_puts;
};
@@ -135,7 +180,8 @@ int el_pmemory(const char *file, size_t line, enum el_level level,
const void *memory, size_t mlen);
int el_perror(const char *file, size_t line, enum el_level level,
const char *fmt, ...);
-
+int el_putb(const void *memory, size_t mlen);
+int el_pbinary(enum el_level level, const void *memory, size_t mlen);
int el_oinit(struct el_options *options);
int el_ocleanup(struct el_options *options);
@@ -149,6 +195,12 @@ int el_opmemory(const char *file, size_t line, enum el_level level,
struct el_options *options, const void *memory, size_t mlen);
int el_operror(const char *file, size_t line, enum el_level level,
struct el_options *options, const char *fmt, ...);
+int el_oputb(struct el_options *options, const void *memory, size_t mlen);
+int el_opbinary(enum el_level level, struct el_options *options,
+ const void *memory, size_t mlen);
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/man/el_init.3 b/man/el_init.3
index 18552b8..dca5c64 100644
--- a/man/el_init.3
+++ b/man/el_init.3
@@ -76,7 +76,7 @@ Note: error handling has been ommited for clarity sake
/* make opts to print to file */
el_ooption(&opts, EL_OUT, EL_OUT_STDERR);
- el_ooption(&opts, EL_FNAME, "/tmp/test.log");
+ el_ooption(&opts, EL_FPATH, "/tmp/test.log");
/* print messages */
el_print(ELI, "will print to stderr");
diff --git a/man/el_option.3 b/man/el_option.3
index b961e31..c94a340 100644
--- a/man/el_option.3
+++ b/man/el_option.3
@@ -29,7 +29,7 @@ Value inside parenthesis determines argument types of variadic arguments
sets what the current logging level shall be.
Altough it accepts
. B enum el_level
-type, this can be whatever number from range <0, INT_MAX>.
+type, this can be whatever number from range <0, 7>.
The higher the level, the lower priority of the message.
All messages that have lower priority (higher number) then currently set
. I level
@@ -54,34 +54,14 @@ There are 8 predefined levels, sorted by priority (highest first):
. B EL_DBG
. RE
. PP
-If 8 print levels are not enough, one can define new levels, knowing that
-. B EL_FATAL
-has int value of 0, and
-. B EL_DBG
-has int value of 7.
-User can also use debug level and add an integer like:
-. B EL_DBG
-+
-. I n
-- where
-. I n
-can be any number from range <0, INT_MAX - 8>.
-. PP
-If messages are printed with lower priority than
-. B EL_DBG
-they all will be treated as messages printed with level
-. BR EL_DBG ,
-that means message printed with level
-. B EL_DBG
-and
-. BR "EL_DBG + 3" ,
-will be printed exactly the same, so same color (if colors are enabled) and if
-log level printig is enabled, such messages will have same 'd/' prefix no matter
-the level.
-. PP
. B ERRORS
. RS
-Setting log level cannot fail
+. B EINVAL
+. RS
+specified
+. I level
+is invalid (bigger than EL_DBG).
+. RE
. RE
.RE
.PP
@@ -119,7 +99,7 @@ POSIX.1-2001 and any syslog daemon
. RS
Messages will be printed to file.
To work user should set output file with
-. BR EL_FNAME .
+. BR EL_FPATH .
Log rotation can be configured with
. B EL_FROTATE_NUMBER
and
@@ -184,6 +164,32 @@ is not implemented on current system (support was not compiled into library)
. RE
.RE
.PP
+.BI "EL_PREFIX (" const " " char " " * "prefix)"
+.RS
+You can configure logger to add
+. I prefix
+to every message you print.
+. I prefix
+will be printed between log level info "i/" and you message.
+No spaces are added.
+If you set your
+. I prefix
+to simple
+. BR PREFIX ,
+printed message will be
+. BR "i/PREFIXmy log message" ,
+so you might want to add space there.
+If
+. I prefix
+is bigger than
+. B EL_PREFIX_LEN
+prefix will be truncated.
+To disable prefixing, simply set
+. I prefix
+to
+. BR NULL .
+.RE
+.PP
.BI "EL_COLORS (" int " colors)"
.RS
If this is set to 1, output will be enriched with ANSI colors depending on the
@@ -310,6 +316,26 @@ Specified timer source was not compiled in and is not available
. RE
.RE
.PP
+.BI "EL_TS_FRACT (" enum " " el_option_timestamp_fraction " fraction)
+.RS
+This options controls how to display fractions of seconds.
+If high resolution is not needed or not supported, it's best to set this to
+lowest resolution possible.
+Table will show exacly what this is about.
+Example uses long timestamp, interesting part is after date after dot '.'.
+.PP
+.EX
+ +-----------------+-------------------------------+
+ | value | resulting timestamp string |
+ +-----------------+-------------------------------+
+ | EL_TS_FRACT_OFF | 2018-04-17 22:02:57 |
+ | EL_TS_FRACT_MS | 2018-04-17 22:02:57.070 |
+ | EL_TS_FRACT_US | 2018-04-17 22:02:57.070518 |
+ | EL_TS_FRACT_NS | 2018-04-17 22:02:57.070518782 |
+ +-----------------+-------------------------------+
+.EE
+.RE
+.PP
.BI "EL_PRINT_LEVEL (" int " print)"
.RS
If this is set to 1, each log will have log level information prefix in format
@@ -369,7 +395,7 @@ It is still mandatory to enable custom printing with
. BR el_option (3)
.RE
.PP
-.BI "EL_FNAME (" const " " char " " * "path)"
+.BI "EL_FPATH (" const " " char " " * "path)"
.RS
Sets the
.I path
@@ -407,7 +433,7 @@ specified file without size limit.
The only size limit is the one presented by the filesystem and architecture.
. PP
If this value is bigger than 0, file rotation will be enabled. All files will
-have suffixes added to name set in EL_FNAME. For example,
+have suffixes added to name set in EL_FPATH. For example,
. IR program.log.0 .
Files are enumareted from
. I .0
@@ -428,7 +454,7 @@ will be renamed to
. I log.2
will be renamed to
. I log.1
-and so on.
+and so on).
. PP
User can also pass 1 here, but if file reaches its size limit, it will be
deleted and printing will continue from the empty file
@@ -465,6 +491,64 @@ Value is less than 1
. RE
.RE
.PP
+.BI "EL_FILE_SYNC_EVERY (" long " size)"
+.RS
+Simply writing data to file descriptor is not enough, metadata also has to be
+synced or you are facing data loose on unexpected power loose.
+Yes, data will be on block device, but entry to it in file system will not be
+updated, thus system will think file did not change.
+To prevent that one must sync data and metadata periodically.
+Data will be synced every
+.I size
+of bytes written.
+It basically translates to 'how much data am I ready to loose?'.
+Set this too high and you may loose a significant ammout of data on power lose.
+Set this too low and your performance goes out of the window.
+. PP
+. B ERRORS
+. RS
+. B EINVAL
+. RS
+Value is less than 0
+. RE
+. RE
+.RE
+.PP
+.BI "EL_FILE_SYNC_LEVEL (" enum " " el_level " level)"
+.RS
+If level of printed message is
+. I level
+or less (that is higher priority), it will be synced to disk every single time
+regardless of
+. B EL_FILE_SYNC_EVERY
+option.
+Messages with level
+. B EL_FATAL
+(that is also default value for level sync) are synced always regardless of any
+options set.
+Messages printed with functions that don't take log level, will be treated as
+messages with
+. B EL_DBG
+level.
+If
+. I level
+is set to
+. B EL_DBG
+every message printed with any function will be immediately synced to drive
+(careful with that, this will impact performance a great deal if you print a lot
+of debug messages).
+. PP
+. B ERRORS
+. RS
+. B EINVAL
+. RS
+specified
+. I level
+is invalid (bigger than EL_DBG).
+. RE
+. RE
+.RE
+.PP
.BI "EL_TTY_DEV (" const\ char\ * "dev, " speed_t " speed)"
.RS
Tells
@@ -479,6 +563,12 @@ Logger uses only transmit pin (TX) and will translate all
.B LF
into
.BR CR-LF .
+If
+.I speed
+is configured to
+.BR B0,
+serial port settings will not be altered, library will simply open port and
+will happily work on current serial settings.
. PP
. B ERRORS
. RS
diff --git a/man/el_print.3 b/man/el_print.3
index c7d6c78..3249ecd 100644
--- a/man/el_print.3
+++ b/man/el_print.3
@@ -13,6 +13,8 @@
.PP
.BI "int el_puts(const char *" message ")"
.br
+.BI "int el_putb(const void *" memory ", size_t " mlen ")"
+.br
.BI "int el_print(const char *" file ", size_t " line ", \
enum el_level " level ", const char *" fmt ", " ... ")"
.br
@@ -24,9 +26,15 @@ enum el_level " level ", const char *" fmt ", " ... ")"
.br
.BI "int el_pmemory(const char *" file ", size_t " line ", \
enum el_level " level ", const void *" memory ", size_t " mlen ")
+.br
+.BI "int el_pbinary(enum el_level " level ", const void *" memory", \
+size_t " mlen ")
.PP
.BI "int el_oputs(struct el_options *" options ", const char *" message ")"
.br
+.BI "int el_putb(struct el_options *" options ", const void *" memory ", \
+size_t " mlen ")"
+.br
.BI "int el_oprint(const char *" file ", size_t " line ", \
enum el_level " level ", struct el_options *" options ", \
const char *" fmt ", " ... ")"
@@ -42,6 +50,9 @@ const char *" fmt ", " ... ")"
.BI "int el_pmemory(const char *" file ", size_t " line ", \
enum el_level " level ", struct el_options *" options ", \
const void *" memory ", size_t " mlen ")"
+.br
+.BI "int el_pbinary(enum el_level " level ", struct el_options *" options ", \
+const void *" memory ", size_t " mlen ")"
.PP
.BI "#define ELF " __FILE__ ", " __LINE__ ", " EL_FATAL
.br
@@ -70,7 +81,7 @@ __ISOC99_SOURCE
.SH DESCRIPTION
.PP
Functions print log into configured outputs.
-
+.PP
.BR el_puts (3)
function prints
.I message
@@ -81,6 +92,23 @@ log nor timestamp is printed - even if they are enabled.
Logs printed with this functions are not filtered by log level, and will always
be printed. Prints can still be suppressed by disabling output.
.PP
+.BR el_putb (3)
+function works same as
+.BR el_puts (3)
+but binary data can be passed.
+That binary data will not be printed in ASCII like it it with
+.BR el_pmemory (3).
+If
+.I memory
+contains
+.B NULL
+characters, they will be stored to file.
+If
+.I memory
+is for example
+.B char buf[] = { 0xb0, 0xfc, 0x13, 0x37 }
+then exactly these bytes will be stored into file.
+.PP
.BR el_print (3)
function behaves similar to standard
.BR printf ()
@@ -121,6 +149,11 @@ only information about errno will be printed.
It is similar to
.BR perror ()
function.
+It is guaranteed that
+.BR el_perror (3)
+functions will not modify
+.B errno
+value.
.PP
.BR el_pmemory (3)
is designed to print memory location in a nice hex+ascii format.
@@ -153,6 +186,95 @@ values.
------ ----------------------------------------------- ----------------
.EE
.PP
+.BR el_pbinary (3)
+is used to log binary data into block device.
+Both metadata and data itself are treated as binary data.
+Such files cannot be read with text tools like
+.BR less ,
+but rather needs special program that will parse the file.
+This may be usefull when you need, for example to log every message on CAN bus.
+If there are a lot of messages, it may be better to store them in binary format
+as this may save *a lot* of space.
+.PP
+.BR el_pbinary (3)
+uses specific format to store metadata to save as much space as possible.
+All numerical values in metadata are variadic in size. Order of fields are as
+follows:
+.PP
+.EX
+ +-------+------------+--------------+-------------+------+
+ | flags | ts_seconds | ts_fractions | data_length | data |
+ +-------+------------+--------------+-------------+------+
+.EE
+.PP
+The only mandatory fields are
+.BR flags ", " data_length " and " data .
+.B flags
+determin what fields are present.
+.B flags
+field is always 1 byte in size and its format is
+.PP
+.EX
+ +------+-------+-----------------------------------------------------------+
+ | bits | value | description |
+ +------+-------+-----------------------------------------------------------+
+ | 0 | 0 | both ts_seconds and ts_fraction will not appear |
+ | +-------+-----------------------------------------------------------+
+ | | 1 | at least ts_seconds will appear, ts_fraction appearance |
+ | | | depends on 1..2 bits values |
+ +------+-------+-----------------------------------------------------------+
+ | 1..2 | 0 | ts_fractions will not appear |
+ | +-------+-----------------------------------------------------------+
+ | | 1 | ts_fractions will hold milliseconds value |
+ | +-------+-----------------------------------------------------------+
+ | | 2 | ts_fractions will hold microseconds value |
+ | +-------+-----------------------------------------------------------+
+ | | 3 | ts_fractions will hold nanoseconds value |
+ +------+-------+-----------------------------------------------------------+
+ | 3..5 | 0..7 | severity of the log, 0 is the highest and 7 is the lowest |
+ +------+-------+-----------------------------------------------------------+
+ | 6..7 | 0..3 | reserved |
+ +------+-------+-----------------------------------------------------------+
+.EE
+.PP
+.BR ts_seconds ", " ts_fractions " and " data_length
+are numerical values with dynamic size.
+Each byte of a numerical value can hold number up to 127 and oldest bit is used
+as continuation bit, if that bit is set, program should treat next byte as next
+part of the same numerical value.
+Below is table with example decimal values and it's encoded counterpart.
+.PP
+.EX
+ +---------------+--------------------------+
+ | decimal value | encoded hex value |
+ +---------------+--------------------------+
+ | 0 | 0x00 |
+ | 1 | 0x01 |
+ | 2 | 0x02 |
+ | 127 | 0x7f |
+ | 128 | 0x80 0x81 |
+ | 129 | 0x81 0x01 |
+ | 255 | 0xff 0x01 |
+ | 256 | 0x80 0x02 |
+ | 257 | 0x81 0x02 |
+ | 16383 | 0xff 0x7f |
+ | 16384 | 0x80 0x80 0x01 |
+ | 16385 | 0x81 0x80 0x01 |
+ | 438478374 | 0xa6 0xcc 0x8a 0xd1 0x01 |
+ | 2147483647 | 0xff 0xff 0xff 0xff 0x07 |
+ | 4294967295 | 0xff 0xff 0xff 0xff 0x0f |
+ +---------------+--------------------------+
+.EE
+.PP
+Encoded number are always
+.BR little-endian ,
+that is first byte is always the least significant byte.
+.PP
+.B data
+is be whatever you want of any size.
+.BR el_pbinary (3)
+uses only timestamp and log level, rest of the options are simply ignores.
+.PP
.BR el_print (3),
.BR el_vprint (3),
.BR el_perror (3)
@@ -244,7 +366,7 @@ Any of the input parameters is invalid.
.TP
.B EBADF
Loggig to file is enabled and filename was not set with
-.B EL_FNAME
+.B EL_FPATH
option
.TP
.B EBADF
diff --git a/readme.md b/readme.md
index e05d337..9e62f45 100644
--- a/readme.md
+++ b/readme.md
@@ -11,8 +11,10 @@ systems, it is general **c/c++** logger that can be used in any application.
Implemented features are:
* printing to different outputs (simultaneously) like:
+ * syslog
+ * directly to serial device (like /dev/ttyS0)
* standard error (stderr)
- * file (with optional log rotate)
+ * file (with optional log rotate, and syncing to prevent data loss)
* custom routine - can be anything **embedlog** just calls custom function
with string to print
* appending timestamp to every message
@@ -21,7 +23,7 @@ Implemented features are:
* CLOCK_REALTIME (requires POSIX)
* CLOCK_MONOTONIC (requires POSIX)
* print location of printed log (file and line)
-* 8 predefinied log levels and (sizeof(int) - 8) custom log levels ;-)
+* 8 predefinied log levels (total rip off from syslog(2))
* colorful output (ansi colors) for easy error spotting
* print memory block in wireshark-like output
diff --git a/src/Makefile.am b/src/Makefile.am
index 4de0eaf..b42a7cc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,10 +3,7 @@ include $(top_srcdir)/embedlog-sources.mk
lib_LTLIBRARIES = libembedlog.la
libembedlog_la_SOURCES = $(embedlog_sources)
-libembedlog_la_SOURCES += config-priv.h \
- el-file.h \
- el-options.h \
- el-tty.h \
+libembedlog_la_SOURCES += el-private.h \
valid.h
libembedlog_la_LDFLAGS = -version-info 2:2:2
libembedlog_la_CFLAGS = -I$(top_srcdir)/include
@@ -20,7 +17,7 @@ MOSTLYCLEANFILES = $(analyze_plists)
$(analyze_plists): %.plist: %.c
@echo " CCSA " $@
- @clang --analyze -I.. $< -o $@
+ @clang --analyze -I.. -DHAVE_CONFIG_H=1 $< -o $@
analyze: $(analyze_plists)
diff --git a/src/el-decode-number.c b/src/el-decode-number.c
new file mode 100644
index 0000000..5a8be82
--- /dev/null
+++ b/src/el-decode-number.c
@@ -0,0 +1,86 @@
+/* ==========================================================================
+ Licensed under BSD 2clause license See LICENSE file for more information
+ Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
+ ==========================================================================
+ _ __ __ ____ _ __
+ (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____
+ / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/
+ / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ )
+ /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/
+
+ ========================================================================== */
+
+
+#include <limits.h>
+
+#include "el-private.h"
+
+
+/* ==========================================================================
+ __ __ _
+ ____ __ __ / /_ / /(_)_____
+ / __ \ / / / // __ \ / // // ___/
+ / /_/ // /_/ // /_/ // // // /__
+ / .___/ \__,_//_.___//_//_/ \___/
+ /_/
+ ____ __ _
+ / __/__ __ ____ _____ / /_ (_)____ ____ _____
+ / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/
+ / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ )
+ /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/
+
+ ========================================================================== */
+
+
+/* ==========================================================================
+ Decodes encoded 'number' and stores value into 'out'. Check description
+ of el_encode_number for more information.
+
+ returns number of bytes processed from 'number'
+ ========================================================================== */
+
+
+size_t el_decode_number
+(
+ const void *number, /* number to decode */
+#ifdef LLONG_MAX
+ unsigned long long *out /* decoded number */
+#else
+ unsigned long *out /* decoded number */
+#endif
+)
+{
+ const unsigned char *n; /* just a 'number' as unsigned cahr */
+ size_t i; /* temporary index */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ i = 0;
+ *out = 0;
+ n = number;
+
+ do
+ {
+ /*
+ * multiple stuff is happening in this one-liner
+ *
+ * - first we take 7 bits of number
+ * - calculate weigth of current number
+ * - set current number into right position of out
+ */
+
+ *out |= (n[i] & 0x7f) << (i * 7);
+
+ /*
+ * we do this until number lacks of continuation bit, which means
+ * we are done
+ */
+ }
+ while (n[i++] & 0x80);
+
+ /*
+ * return number of bytes processed from 'number'
+ */
+
+ return i;
+}
diff --git a/src/el-encode-number.c b/src/el-encode-number.c
new file mode 100644
index 0000000..82a81ac
--- /dev/null
+++ b/src/el-encode-number.c
@@ -0,0 +1,112 @@
+/* ==========================================================================
+ Licensed under BSD 2clause license See LICENSE file for more information
+ Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
+ ==========================================================================
+ _ __ __ ____ _ __
+ (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____
+ / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/
+ / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ )
+ /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/
+
+ ========================================================================== */
+
+
+#include <limits.h>
+
+#include "el-private.h"
+
+
+/* ==========================================================================
+ __ __ _
+ ____ __ __ / /_ / /(_)_____
+ / __ \ / / / // __ \ / // // ___/
+ / /_/ // /_/ // /_/ // // // /__
+ / .___/ \__,_//_.___//_//_/ \___/
+ /_/
+ ____ __ _
+ / __/__ __ ____ _____ / /_ (_)____ ____ _____
+ / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/
+ / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ )
+ /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/
+
+ ========================================================================== */
+
+
+/* ==========================================================================
+ This will code 'number' to into as small buffer as possible. Each byte
+ of encoded value can hold 7 bits of data and 1 bit (oldest) of
+ continuation information. If continuation bit is set, that means next
+ byte contains next 7bit of number information. This allows us to store
+ value up to 127 in one byte and values up to 16383 in two bytes and so
+ on. 'out' must be long enough to hold enough encoded bytes or undefined
+ behaviour will occur.
+
+ returns number of bytes stored into 'out' buffer.
+ ========================================================================== */
+
+
+size_t el_encode_number
+(
+#ifdef LLONG_MAX
+ unsigned long long number, /* value to encode */
+#else
+ unsigned long number, /* value to encode */
+#endif
+ void *out /* memory where encoded value will be set */
+)
+{
+ unsigned char *o; /* just 'out' as unsigned char */
+ size_t n; /* number of bytes stored in 'out' */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ o = out;
+ n = 0;
+
+ do
+ {
+ /*
+ * put only youngest 7 bits into out - that's a max number single
+ * byte can hold
+ */
+
+ o[n] = number & 0x7f;
+
+ /*
+ * remove those 7 bits from number, they are used and no longer
+ * needed
+ */
+
+ number >>= 7;
+
+ /*
+ * if we didn't process whole number, set oldest bit to 1, this is
+ * continuation bit, it will tell decoder that next bytes will hold
+ * next 7bits of number
+ */
+
+ if (number)
+ {
+ o[n] |= 0x80;
+ }
+
+ /*
+ * increment n to indicate we store a byte in 'out' buffer
+ */
+
+ ++n;
+
+ /*
+ * do this until there is anyting else left in number
+ */
+ }
+ while (number);
+
+ /*
+ * after job's done, return number of bytes stored in 'out'
+ */
+
+ return n;
+}
+
+
+
diff --git a/src/el-file.c b/src/el-file.c
index 1f61600..cafa331 100644
--- a/src/el-file.c
+++ b/src/el-file.c
@@ -39,22 +39,57 @@
========================================================================== */
#if HAVE_CONFIG_H
-#include "config.h"
+# include "config.h"
#endif
-#include "el-options.h"
+#include "el-private.h"
#if HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif
+
+#if HAVE_STAT
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
/* ==========================================================================
+ __ __ __
+ ____ _ / /____ / /_ ____ _ / /
+ / __ `// // __ \ / __ \ / __ `// /
+ / /_/ // // /_/ // /_/ // /_/ // /
+ \__, //_/ \____//_.___/ \__,_//_/
+ /____/
+ _ __ __
+ _ __ ____ _ _____ (_)____ _ / /_ / /___ _____
+ | | / // __ `// ___// // __ `// __ \ / // _ \ / ___/
+ | |/ // /_/ // / / // /_/ // /_/ // // __/(__ )
+ |___/ \__,_//_/ /_/ \__,_//_.___//_/ \___//____/
+
+ ========================================================================== */
+
+
+#ifdef RUN_TESTS
+
+/*
+ * there is no easy way to check if file has been fsynced to disk or not,
+ * so we introduce this helper variable to know if we executed syncing
+ * code or not
+ */
+extern int file_synced;
+
+#endif
+
+
+/* ==========================================================================
_ __
____ _____ (_)_ __ ____ _ / /_ ___
/ __ \ / ___// /| | / // __ `// __// _ \
@@ -80,8 +115,6 @@
#endif
-static char current_log[PATH_MAX + 1]; /* full path to current log file */
-
/* ==========================================================================
_ __
@@ -101,15 +134,40 @@ static char current_log[PATH_MAX + 1]; /* full path to current log file */
static int el_file_exists
(
- void
+ const char *path /* file to check */
)
{
#if HAVE_ACCESS
- return access(current_log, F_OK) == 0;
+ /*
+ * access is the fastest
+ */
+
+ return access(path, F_OK) == 0;
+
+#elif HAVE_STAT
+ /*
+ * but if we don't have access (some embedded systems like nuttx don't
+ * have users and will always return OK when access is called - wrongly
+ */
+
+ struct stat st;
+ /*
+ * if stat return 0 we are sure file exists, -1 is returned for when
+ * file doesn't exist or there is other error, in any case we assume
+ * file doesn't exist
+ */
+
+ return stat(path, &st) == 0;
+
#else
+ /*
+ * slowest, worst but highly portable solution, for when there is
+ * nothing left but you still want to work
+ */
+
FILE *f;
- if ((f = fopen(current_log, "r")) == NULL)
+ if ((f = fopen(path, "r")) == NULL)
{
return 0;
}
@@ -134,7 +192,7 @@ static int el_file_rotate
struct el_options *options
)
{
- int i; /* simple iterator for loop */
+ unsigned int i; /* simple iterator for loop */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@@ -212,9 +270,10 @@ skip_rotate:
* passes there, it will pass here as well
*/
- sprintf(current_log, "%s.%d", options->fname, options->fcurrent_rotate);
+ sprintf(options->current_log, "%s.%d",
+ options->fname, options->fcurrent_rotate);
- if ((options->file = fopen(current_log, "w")) == NULL)
+ if ((options->file = fopen(options->current_log, "w")) == NULL)
{
options->fcurrent_rotate--;
return -1;
@@ -225,6 +284,7 @@ skip_rotate:
return 0;
}
+
/* ==========================================================================
__ __ _
____ __ __ / /_ / /(_)_____
@@ -252,12 +312,29 @@ int el_file_open
struct el_options *options /* options with file information */
)
{
+ if (options->current_log == NULL)
+ {
+ /*
+ * yes, we need to dynamically allocate memory here. It's because we
+ * need to keep current log privately in each of el_options objects
+ * or there will be problems in embedded systems that use flat
+ * memory. Since when working with files, OS will always make some
+ * dynamic allocation, we will be doing one too.
+ */
+
+ if ((options->current_log = malloc(PATH_MAX + 1)) == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+
if (options->file)
{
/*
* to prevent any memory leak in case of double open, we first
* close already opened file Such situation may happen when library
- * user changes file name using EL_FNAME option,
+ * user changes file name using EL_FPATH option,
*/
fclose(options->file);
@@ -282,7 +359,7 @@ int el_file_open
for (i = options->frotate_number - 1; i >= 0; --i)
{
- pathl = snprintf(current_log, PATH_MAX + 1, "%s.%d",
+ pathl = snprintf(options->current_log, PATH_MAX + 1, "%s.%d",
options->fname, i);
if (pathl > PATH_MAX)
@@ -293,12 +370,90 @@ int el_file_open
* could result in some data lose on the disk.
*/
- current_log[0] = '\0';
+ options->current_log[0] = '\0';
errno = ENAMETOOLONG;
return -1;
}
- if ((f = fopen(current_log, "a")) == NULL)
+#if HAVE_STAT
+
+ if (i != 0)
+ {
+ struct stat st;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ /*
+ * if i is 0, then this is last file to check (there are no
+ * logs from embed log in directory) and there is no need to
+ * check if file exists or not - we open it unconditionally,
+ * thus this path is not taken in such case.
+ */
+
+ if (el_file_exists(options->current_log) == 0)
+ {
+ /*
+ * current log file does not exist, this is not the file
+ * you are looking for
+ */
+
+ continue;
+ }
+
+ if (stat(options->current_log, &st) != 0)
+ {
+ /*
+ * error while stating file, probably don't have access
+ * to that file, or directory, or whatever, something
+ * bad has happend and we don't want to pursue this,
+ * exit with error from stat.
+ */
+
+ options->current_log[0] = '\0';
+ return -1;
+ }
+
+ if (st.st_size == 0)
+ {
+ /*
+ * there is an empty file, maybe we created it and then
+ * crashed? Or maybe someone is trying to pull our legs
+ * and drops bombs under our feets. Either way, this is
+ * definitely not oldest file. We also remove it so it
+ * doesn't botter us later
+ */
+
+ remove(options->current_log);
+ continue;
+ }
+ }
+
+ /*
+ * we got our file, let's open it for writing and let's call it
+ * a day
+ */
+
+ if ((f = fopen(options->current_log, "a")) == NULL)
+ {
+ /*
+ * couldn't open file, probably directory doesn't exist, or
+ * we have no permissions to create file here
+ */
+
+ return -1;
+ }
+
+ /*
+ * we need to check for currently opened file size once again,
+ * as if i equal 0 here, we never called stat() on our file and
+ * we don't know the size of it
+ */
+
+ fseek(f, 0, SEEK_END);
+ fsize = ftell(f);
+
+#else /* HAVE_STAT */
+
+ if ((f = fopen(options->current_log, "a")) == NULL)
{
/*
* if we cannot open file, that means there is some kind of
@@ -306,7 +461,7 @@ int el_file_open
* it's pointless to continue
*/
- current_log[0] = '\0';
+ options->current_log[0] = '\0';
return -1;
}
@@ -342,10 +497,12 @@ int el_file_open
*/
fclose(f);
- remove(current_log);
+ remove(options->current_log);
continue;
}
+#endif /* HAVE_STAT */
+
/*
* oldest file found, file is already opened so we simply return
* from the function
@@ -364,19 +521,27 @@ int el_file_open
if (strlen(options->fname) > PATH_MAX)
{
- current_log[0] = '\0';
+ options->current_log[0] = '\0';
errno = ENAMETOOLONG;
return -1;
}
- strcpy(current_log, options->fname);
+ strcpy(options->current_log, options->fname);
- if ((options->file = fopen(current_log, "a")) == NULL)
+ if ((options->file = fopen(options->current_log, "a")) == NULL)
{
- current_log[0] = '\0';
+ /*
+ * we couldn't open file, but don't set clear options->current_log,
+ * we will try to reopen this file each time we print to file. This
+ * is usefull when user tries to open log file in not-yet existing
+ * directory. Error is of course returned to user is aware of this
+ * whole situation
+ */
+
return -1;
}
+ fseek(options->file, 0, SEEK_END);
options->fpos = ftell(options->file);
return 0;
}
@@ -387,31 +552,37 @@ int el_file_open
========================================================================== */
-int el_file_puts
+int el_file_putb
(
- struct el_options *options, /* printing options */
- const char *s /* string to 'put' into file */
+ struct el_options *options, /* printing options */
+ const void *mem, /* memory to 'put' into file */
+ size_t mlen /* size of buffer 'mlen' */
+
)
{
- int sl; /* size of the s message */
- /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+ VALID(EINVAL, mem);
+ VALID(EINVAL, mlen);
+ VALID(EINVAL, options);
+ VALID(EBADF, options->current_log);
+ VALID(EBADF, options->current_log[0] != '\0');
- if (current_log[0] == '\0')
- {
- /*
- * file has not been opened, prevent segfault
- */
+ /*
+ * we need to reopen our file if it wasn't opened yet, or was removed
+ * due to error or deliberate acion of user
+ */
- errno = EBADF;
- return -1;
+ if (el_file_exists(options->current_log) == 0 || options->file == NULL)
+ {
+ if (el_file_open(options) != 0)
+ {
+ return -1;
+ }
}
- sl = strlen(s);
-
if (options->frotate_number)
{
- if (options->fpos != 0 && options->fpos + sl > options->frotate_size)
+ if (options->fpos != 0 && options->fpos + mlen > options->frotate_size)
{
/*
* we get here only when frotate is enabled, and writing to
@@ -427,7 +598,7 @@ int el_file_puts
}
}
- if (sl > options->frotate_size)
+ if (mlen > options->frotate_size)
{
/*
* we can't fit message even in an empty file, in such case we
@@ -435,51 +606,118 @@ int el_file_puts
* configured frotate_size
*/
- sl = options->frotate_size;
+ mlen = options->frotate_size;
}
}
- if (el_file_exists() == 0)
+ if (fwrite(mem, mlen, 1, options->file) != 1)
+ {
+ return -1;
+ }
+
+ options->fpos += mlen;
+ options->written_after_sync += mlen;
+
+ if (options->written_after_sync >= options->file_sync_every ||
+ options->level_current_msg <= options->file_sync_level)
{
/*
- * file doesn't exist, it may happen when someone unlinks currently
- * opened file. Even tough user unlinked our log file, we still have
- * file opened, so this file exists in the system, but not in the
- * file system tree and as such, every write to such file is
- * effectively writing to /dev/null. To prevent that, we close and
- * reopen our current log file
+ * file store operation has particular issue. You can like, write
+ * hundreds of megabytes into disk and that data indeed will land
+ * into your block device *but* metadata will not! That is if power
+ * lose occurs (even after those hundreds of megs), file metadata
+ * might not be updated and we will lose whole, I say it again
+ * *whole* file. To prevent data lose on power down we try to sync
+ * file and its metadata to block device. We do this every
+ * configures "sync_every" field since syncing every single write
+ * would simply kill performance - its up to user to decide how much
+ * data he is willing to lose. It is also possible that altough we
+ * sync our file and metadata, parent directory might not get
+ * flushed and there will not be entry for our file - meaning file
+ * will be lost too, but such situations are ultra rare and there
+ * isn't really much we can do about it here but praying.
*/
- if (options->file)
+#if HAVE_FSYNC && HAVE_FILENO
+ int fd; /* systems file descriptor for options->file */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+#endif /* HAVE_FSYNC && HAVE_FILENO */
+
+ /*
+ * first flush data from stdio library buffers into kernel
+ */
+
+ if (fflush(options->file) != 0)
{
- fclose(options->file);
+ return -1;
}
- if ((options->file = fopen(current_log, "a")) == NULL)
+#if HAVE_FSYNC && HAVE_FILENO
+ /*
+ * and then sync data into block device
+ */
+
+ if ((fd = fileno(options->file)) < 0)
+ {
+ return -1;
+ }
+
+ if (fsync(fd) != 0)
+ {
+ return -1;
+ }
+#else /* HAVE_FSYNC && HAVE_FILENO */
+ /*
+ * if system does not implement fileno and fsync our only hope lies
+ * in closing (which should sync file to block device) and then
+ * opening file again. Yup, performance will suck, but hey, its
+ * about data safety!
+ */
+
+ fclose(options->file);
+
+ if ((options->file = fopen(options->current_log, "a")) == NULL)
{
errno = EBADF;
return -1;
}
- options->fpos = 0;
- }
+ fseek(options->file, 0, SEEK_END);
+ options->fpos = ftell(options->file);
+#endif /* HAVE_FSYNC && HAVE_FILENO */
- if (fwrite(s, sl, 1, options->file) != 1)
- {
- return -1;
- }
+#ifdef RUN_TESTS
+ file_synced = 1;
+#endif /* RUN_TESTS */
- if (fflush(options->file) != 0)
- {
- return -1;
+ options->written_after_sync = 0;
}
- options->fpos += sl;
return 0;
}
/* ==========================================================================
+ Puts string 's' into file if needed rotates file
+ ========================================================================== */
+
+
+int el_file_puts
+(
+ struct el_options *options, /* printing options */
+ const char *s /* string to 'put' into file */
+)
+{
+ size_t slen; /* size of string 's' */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ slen = strlen(s);
+ return el_file_putb(options, (unsigned char *)s, slen);
+}
+
+
+/* ==========================================================================
free resources allocated by this module
========================================================================== */
@@ -495,5 +733,11 @@ void el_file_cleanup
}
options->file = NULL;
- current_log[0] = '\0';
+
+ if (options->current_log)
+ {
+ options->current_log[0] = '\0';
+ free(options->current_log);
+ options->current_log = NULL;
+ }
}
diff --git a/src/el-file.h b/src/el-file.h
deleted file mode 100644
index 42687b6..0000000
--- a/src/el-file.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* ==========================================================================
- Licensed under BSD 2clause license See LICENSE file for more information
- Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
- ========================================================================== */
-
-#ifndef EL_FILE_H
-#define EL_FILE_H 1
-
-#include "el-options.h"
-
-int el_file_open(struct el_options *);
-int el_file_puts(struct el_options *, const char *);
-void el_file_cleanup(struct el_options *);
-
-#endif
diff --git a/src/el-options.c b/src/el-options.c
index b14b379..22b7e1a 100644
--- a/src/el-options.c
+++ b/src/el-options.c
@@ -39,20 +39,11 @@
#include "config.h"
#endif
-#include "el-options.h"
-#include "embedlog.h"
-#include "valid.h"
+#include "el-private.h"
#include <errno.h>
#include <string.h>
-#if ENABLE_OUT_FILE
-#include "el-file.h"
-#endif
-
-#if ENABLE_OUT_TTY
-#include "el-tty.h"
-#endif
/* ==========================================================================
__ __ __
@@ -162,9 +153,16 @@ static int el_vooption
{
case EL_LEVEL:
value_int = va_arg(ap, int);
+ VALID(EINVAL, value_int <= 7);
options->level = value_int;
return 0;
+ case EL_FILE_SYNC_LEVEL:
+ value_int = va_arg(ap, int);
+ VALID(EINVAL, value_int <= 7);
+ options->file_sync_level = value_int;
+ return 0;
+
case EL_OUT:
value_int = va_arg(ap, int);
VALID(EINVAL, (value_int & ~EL_OUT_ALL) == 0x00);
@@ -179,7 +177,23 @@ static int el_vooption
options->print_log_level = value_int;
return 0;
- #if ENABLE_COLORS
+ case EL_PRINT_NL:
+ value_int = va_arg(ap, int);
+ VALID(EINVAL, (value_int & ~1) == 0);
+
+ options->print_newline = value_int;
+ return 0;
+
+# if ENABLE_PREFIX
+
+ case EL_PREFIX:
+ value_str = va_arg(ap, const char *);
+ options->prefix = value_str;
+ return 0;
+
+# endif /* ENABLE_PREFIX */
+
+# if ENABLE_COLORS
case EL_COLORS:
/*
@@ -192,9 +206,9 @@ static int el_vooption
options->colors = value_int;
return 0;
- #endif /* ENABLE_COLORS */
+# endif /* ENABLE_COLORS */
- #if ENABLE_TIMESTAMP
+# if ENABLE_TIMESTAMP
case EL_TS:
value_int = va_arg(ap, int);
@@ -203,24 +217,40 @@ static int el_vooption
options->timestamp = value_int;
return 0;
+# if ENABLE_FRACTIONS
+
+ case EL_TS_FRACT:
+ value_int = va_arg(ap, int);
+ VALID(EINVAL, 0 <= value_int && value_int < EL_TS_FRACT_ERROR);
+
+ options->timestamp_fractions = value_int;
+ return 0;
+
+# endif /* ENABLE_FRACTIONS */
+
case EL_TS_TM:
value_int = va_arg(ap, int);
VALID(EINVAL, 0 <= value_int && value_int < EL_TS_TM_ERROR);
-#ifndef ENABLE_REALTIME
+
+# if ENABLE_REALTIME == 0
VALID(ENODEV, value_int != EL_TS_TM_REALTIME);
-#endif
+# endif
-#ifndef ENABLE_MONOTONIC
+# if ENABLE_MONOTONIC == 0
VALID(ENODEV, value_int != EL_TS_TM_MONOTONIC);
-#endif
+# endif
+
+# if ENABLE_CLOCK == 0
+ VALID(ENODEV, value_int != EL_TS_TM_CLOCK);
+# endif
options->timestamp_timer = value_int;
return 0;
- #endif /* ENABLE_TIMESTAMP */
+# endif /* ENABLE_TIMESTAMP */
- #if ENABLE_FINFO
+# if ENABLE_FINFO
case EL_FINFO:
value_int = va_arg(ap, int);
@@ -229,11 +259,11 @@ static int el_vooption
options->finfo = value_int;
return 0;
- #endif /* ENABLE_FINFO */
+# endif /* ENABLE_FINFO */
- #if ENABLE_OUT_FILE
+# if ENABLE_OUT_FILE
- case EL_FNAME:
+ case EL_FPATH:
value_str = va_arg(ap, const char *);
VALID(EINVAL, value_str);
options->fname = value_str;
@@ -272,9 +302,15 @@ static int el_vooption
options->frotate_size = value_long;
return 0;
- #endif /* ENABLE_OUT_FILE */
+ case EL_FILE_SYNC_EVERY:
+ value_long = va_arg(ap, long);
+ VALID(EINVAL, value_long >= 0);
+ options->file_sync_every = value_long;
+ return 0;
+
+# endif /* ENABLE_OUT_FILE */
- #if ENABLE_OUT_TTY
+# if ENABLE_OUT_TTY
case EL_TTY_DEV:
{
@@ -288,15 +324,15 @@ static int el_vooption
return el_tty_open(options, value_str, speed);
}
- #endif /* ENABLE_OUT_TTY */
+# endif /* ENABLE_OUT_TTY */
- #if ENABLE_OUT_CUSTOM
+# if ENABLE_OUT_CUSTOM
case EL_CUSTOM_PUTS:
options->custom_puts = va_arg(ap, el_custom_puts);
return 0;
- #endif /* ENABLE_OUT_CUSTOM */
+# endif /* ENABLE_OUT_CUSTOM */
default:
/*
@@ -361,8 +397,12 @@ int el_oinit
memset(options, 0, sizeof(struct el_options));
options->print_log_level = 1;
+ options->print_newline = 1;
options->level = EL_INFO;
+ options->level_current_msg = EL_DBG;
+ options->file_sync_level = EL_FATAL;
options->serial_fd = -1;
+ options->file_sync_every = 32768;
return 0;
}
diff --git a/src/el-options.h b/src/el-options.h
deleted file mode 100644
index 3149615..0000000
--- a/src/el-options.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* ==========================================================================
- Licensed under BSD 2clause license. See LICENSE file for more information
- Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
- ========================================================================== */
-
-
-#ifndef EL_OPTIONS_H
-#define EL_OPTIONS_H 1
-
-
-#include "embedlog.h"
-
-
-extern struct el_options g_options;
-
-int el_log_allowed(struct el_options *, enum el_level);
-
-#endif
diff --git a/src/el-pbinary.c b/src/el-pbinary.c
new file mode 100644
index 0000000..ae636ca
--- /dev/null
+++ b/src/el-pbinary.c
@@ -0,0 +1,224 @@
+/* ==========================================================================
+ Licensed under BSD 2clause license See LICENSE file for more information
+ Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
+ ==========================================================================
+ -------------------------------------------------------------
+ / Module reponsible for printing binary logs. And by binary I \
+ | don't mean only metadata and logs in ASCII, but full binary |
+ | including data. This may be useful when one need to log ie. |
+ | every message it saw on CAN. It would be a waste of space |
+ | to log it in ASCII when binary data can be less by an order |
+ \ of magnitude /
+ -------------------------------------------------------------
+ \
+ \ , _ ___.--'''`--''//-,-_--_.
+ \`"' ` || \\ \ \\/ / // / ,-\\`,_
+ /'` \ \ || Y | \|/ / // / - |__ `-,
+ /@"\ ` \ `\ | | ||/ // | \/ \ `-._`-,_.,
+ / _.-. `.-\,___/\ _/|_/_\_\/|_/ | `-._._)
+ `-'``/ / | // \__/\__ / \__/ \
+ `-' /-\/ | -| \__ \ |-' |
+ __/\ / _/ \/ __,-' ) ,' _|'
+ (((__/(((_.' ((___..-'((__,'
+ ========================================================================== */
+
+
+/* ==========================================================================
+ _ __ __ ____ _ __
+ (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____
+ / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/
+ / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ )
+ /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/
+
+ ========================================================================== */
+
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "el-private.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+
+/* ==========================================================================
+ _ __ __
+ ____ _____ (_)_ __ ____ _ / /_ ___ / /_ __ __ ____ ___ _____
+ / __ \ / ___// /| | / // __ `// __// _ \ / __// / / // __ \ / _ \ / ___/
+ / /_/ // / / / | |/ // /_/ // /_ / __/ / /_ / /_/ // /_/ // __/(__ )
+ / .___//_/ /_/ |___/ \__,_/ \__/ \___/ \__/ \__, // .___/ \___//____/
+/_/ /____//_/
+ ========================================================================== */
+
+
+#define FLAG_TS (0x01)
+
+#define FLAG_TS_FRACT_NONE (0x00)
+#define FLAG_TS_FRACT_NSEC (0x01)
+#define FLAG_TS_FRACT_USEC (0x02)
+#define FLAG_TS_FRACT_MSEC (0x03)
+#define FLAG_TS_FRACT_MASK (0x03)
+#define FLAG_TS_FRACT_SHIFT (1)
+
+#define FLAG_LEVEL_MASK (0x07)
+#define FLAG_LEVEL_SHIFT (3)
+
+
+/* ==========================================================================
+ _ __
+ ____ _____ (_)_ __ ____ _ / /_ ___
+ / __ \ / ___// /| | / // __ `// __// _ \
+ / /_/ // / / / | |/ // /_/ // /_ / __/
+ / .___//_/ /_/ |___/ \__,_/ \__/ \___/
+ /_/
+ ____ __ _
+ / __/__ __ ____ _____ / /_ (_)____ ____ _____
+ / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/
+ / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ )
+ /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/
+
+ ========================================================================== */
+
+
+/* ==========================================================================
+ Puts flags with log information to buf. Flags is single byte with fields
+
+ bit 0 timestamp enabled/disabled
+ bit 1-2 fraction timestamp (off, nsec, usec, msec)
+ bit 3-5 severity of the log
+ bit 6-7 reserved
+ ========================================================================== */
+
+
+static size_t el_flags
+(
+ enum el_level level,
+ struct el_options *options,
+ unsigned char *buf
+)
+{
+ *buf = 0;
+
+#if ENABLE_TIMESTAMP
+ if (options->timestamp != EL_TS_OFF)
+ {
+ *buf |= FLAG_TS;
+
+# if ENABLE_FRACTIONS
+ /*
+ * fraction of seconds can be printed only when timestamp is on
+ */
+
+ *buf |= options->timestamp_fractions << FLAG_TS_FRACT_SHIFT;
+# endif
+ }
+#endif
+
+ *buf |= level << FLAG_LEVEL_SHIFT;
+
+ return 1;
+}
+
+
+/* ==========================================================================
+ __ __ _
+ ____ __ __ / /_ / /(_)_____
+ / __ \ / / / // __ \ / // // ___/
+ / /_/ // /_/ // /_/ // // // /__
+ / .___/ \__,_//_.___//_//_/ \___/
+ /_/
+ ____ __ _
+ / __/__ __ ____ _____ / /_ (_)____ ____ _____
+ / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/
+ / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ )
+ /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/
+
+ ========================================================================== */
+
+
+int el_opbinary
+(
+ enum el_level level,
+ struct el_options *options,
+ const void *memory,
+ size_t mlen
+)
+{
+ unsigned char buf[EL_BUF_MAX]; /* buffer for message to print */
+ size_t l; /* length of encoded mlen */
+ size_t w; /* bytes written to buf */
+ int e; /* cache for errno */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ VALID(EINVAL, mlen);
+ VALID(EINVAL, memory);
+ VALID(EINVAL, options);
+ VALID(ENODEV, options->outputs);
+ VALID(ERANGE, el_log_allowed(options, level));
+
+ e = 0;
+ w = el_flags(level, options, buf);
+ w += el_timestamp(options, buf + w, TS_BINARY);
+
+ /*
+ * encode mlen to know how much bytes we are going to need
+ */
+
+ l = el_encode_number(mlen, buf + w);
+
+ if (w + l + mlen > sizeof(buf))
+ {
+ /*
+ * user tries to print more that than we can hold in our buffer,
+ * buffer overflow attack? Not going to happen! We truncate it.
+ */
+
+ mlen = EL_BUF_MAX - w - l;
+ e = ENOBUFS;
+ }
+
+ /*
+ * know that we know real value of mlen, we can encode mlen again
+ */
+
+ w += el_encode_number(mlen, buf + w);
+ memcpy(buf + w, memory, mlen);
+ options->level_current_msg = level;
+ w += mlen;
+
+ if (el_oputb(options, buf, w) != 0)
+ {
+ options->level_current_msg = EL_DBG;
+ return -1;
+ }
+
+ options->level_current_msg = EL_DBG;
+
+ if (e)
+ {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* ==========================================================================
+ Same as el_opbinary but uses global options object
+ ========================================================================== */
+
+
+int el_pbinary
+(
+ enum el_level level,
+ const void *memory,
+ size_t mlen
+)
+{
+ return el_opbinary(level, &g_options, memory, mlen);
+}
diff --git a/src/el-perror.c b/src/el-perror.c
index 4b7ccb7..a6dfa74 100644
--- a/src/el-perror.c
+++ b/src/el-perror.c
@@ -28,8 +28,7 @@
#include "config.h"
#endif
-#include "el-options.h"
-#include "embedlog.h"
+#include "el-private.h"
#include <errno.h>
#include <stdarg.h>
@@ -89,6 +88,12 @@ static int el_ovperror
rc |= el_oprint(file, num, level, options,
"errno num: %lu, strerror: %s", e, strerror(e));
+ /*
+ * in case errno has been modified return it to value from before this
+ * call
+ */
+
+ errno = e;
return rc;
}
diff --git a/src/el-pmemory.c b/src/el-pmemory.c
index c67650a..408086d 100644
--- a/src/el-pmemory.c
+++ b/src/el-pmemory.c
@@ -42,10 +42,7 @@
#include "config.h"
#endif
-#include "config-priv.h"
-#include "el-options.h"
-#include "embedlog.h"
-#include "valid.h"
+#include "el-private.h"
#include <ctype.h>
#include <stdio.h>
diff --git a/src/el-print.c b/src/el-print.c
index c0dda59..61d9a8e 100644
--- a/src/el-print.c
+++ b/src/el-print.c
@@ -48,14 +48,7 @@
#include "config.h"
#endif
-#include "config-priv.h"
-#include "el-options.h"
-#include "embedlog.h"
-#include "valid.h"
-
-#if ENABLE_TIMESTAMP
-#include <time.h>
-#endif
+#include "el-private.h"
#include <errno.h>
#include <stdarg.h>
@@ -89,6 +82,13 @@ static const char char_level[8] = { 'f', 'a', 'c', 'e', 'w', 'n', 'i', 'd' };
* colors indexes are synced with log level
*/
+#if ENABLE_COLORS_EXTENDED
+
+/*
+ * for those that like more colors, there are definitions with more colors!
+ * this will enable light version of some levels, but this is not supported
+ * on all terminal! You have been warned!
+ */
static const char *color[] =
{
@@ -103,7 +103,30 @@ static const char *color[] =
"\e[0m" /* remove all formats */
};
-#endif
+#else
+
+/*
+ * not all terminal can support extended colors with light version of them,
+ * for those who want to be more standard compliant there is this shortened
+ * version of colors. On downside is that some level will have same colors.
+ */
+
+static const char *color[] =
+{
+ "\e[31m", /* fatal red */
+ "\e[31m", /* alert red */
+ "\e[35m", /* critical magenta */
+ "\e[35m", /* error magenta */
+ "\e[33m", /* warning yellow */
+ "\e[32m", /* notice green */
+ "\e[32m", /* information green */
+ "\e[34m", /* debug blue */
+ "\e[0m" /* remove all formats */
+};
+
+#endif /* COLORS_EXTENDED */
+
+#endif /* ENABLE_COLORS */
/* ==========================================================================
@@ -159,184 +182,6 @@ static size_t el_color
/* ==========================================================================
- returns seconds and microseconds calculated from clock() function
- ========================================================================== */
-
-
-#if ENABLE_TIMESTAMP
-
-static void el_ts_clock
-(
- time_t *s, /* seconds will be stored here */
- long *us /* microseconds will be stored here */
-)
-{
- clock_t clk; /* clock value */
- /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
- clk = clock();
-
- *s = clk / CLOCKS_PER_SEC;
- *us = clk % CLOCKS_PER_SEC;
- *us *= 1000000 / CLOCKS_PER_SEC;
-}
-
-
-/* ==========================================================================
- returns seconds and microseconds calculated from time() function.
- ========================================================================== */
-
-
-static void el_ts_time
-(
- time_t *s, /* seconds will be stored here */
- long *us /* microseconds will be stored here */
-)
-{
- *s = (long)time(NULL);
- *us = 0;
-}
-
-
-/* ==========================================================================
- returns seconds and microseconds calculated from clock_gettime function
- ========================================================================== */
-
-
-#if ENABLE_REALTIME || ENABLE_MONOTONIC
-
-static void el_ts_clock_gettime
-(
- time_t *s, /* seconds will be stored here */
- long *us, /* microseconds will be stored here */
- clockid_t clkid /* clock id */
-)
-{
- struct timespec tp; /* clock value */
- /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
- clock_gettime(clkid, &tp);
-
- *s = tp.tv_sec;
- *us = tp.tv_nsec / 1000;
-}
-
-#endif
-
-#endif /* ENABLE_TIMESTAMP */
-
-
-/* ==========================================================================
- returns current timestamp in 'buf' location. Depending on global
- settings timestamp can be in long or short format. Function will return
- number of bytes copied to 'buf'. If timestamp has been disabled during
- compilation time or in runtime during settings, function will return 0.
- ========================================================================== */
-
-
-static size_t el_timestamp
-(
- struct el_options *options, /* options defining printing style */
- char *buf /* buffer where timestamp will be stored */
-)
-{
-#if ENABLE_TIMESTAMP
- time_t s; /* timestamp seconds */
- long us; /* timestamp microseconds */
- size_t tl; /* timestamp length */
- /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
- if (options->timestamp == EL_TS_OFF)
- {
- /*
- * user doesn't want us to print timestamp, that's fine
- */
-
- return 0;
- }
-
- /*
- * first we get seconds and microseconds from proper timer
- */
-
- switch (options->timestamp_timer)
- {
-#if ENABLE_REALTIME
-
- case EL_TS_TM_REALTIME:
- el_ts_clock_gettime(&s, &us, CLOCK_REALTIME);
- break;
-
-#endif
-
-#if ENABLE_MONOTONIC
-
- case EL_TS_TM_MONOTONIC:
- el_ts_clock_gettime(&s, &us, CLOCK_MONOTONIC);
- break;
-
-#endif
-
- case EL_TS_TM_TIME:
- el_ts_time(&s, &us);
- break;
-
- case EL_TS_TM_CLOCK:
- el_ts_clock(&s, &us);
- break;
-
- default:
- /*
- * bad timer means no timer
- */
-
- return 0;
- }
-
- /*
- * then convert retrieved time into string timestamp
- */
-
- if (options->timestamp == EL_TS_LONG)
- {
- struct tm tm; /* timestamp splitted */
- struct tm *tmp; /* timestamp splitted pointer */
- /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
- #if ENABLE_REENTRANT
- tmp = gmtime_r(&s, &tm);
- #else
- tmp = gmtime(&s);
- #endif
-
- tl = strftime(buf, 21, "[%Y-%m-%d %H:%M:%S", tmp);
- }
- else
- {
-#ifdef LLONG_MAX
- tl = sprintf(buf, "[%lld", (long long)s);
-#else
- /*
- * if long long is not available, code may be suscible to 2038 bug.
- * If you are sure your compiler does support long long type, but
- * doesn't define LLONG_MAX, define this value yourself to enable
- * long long.
- */
- tl = sprintf(buf, "[%ld", (long)s);
-#endif
- }
-
- tl += sprintf(buf + tl, ".%06ld]", us);
-
- return tl;
-
-#else
- return 0;
-#endif /* ENABLE_TIMESTAMP */
-}
-
-
-/* ==========================================================================
returns pointer to where basename of 's' starts
Examples:
@@ -561,8 +406,9 @@ int el_ovprint
* add preamble and colors to log line buf
*/
+ buf[0] = '\0';
w = el_color(options, buf, level);
- w += el_timestamp(options, buf + w);
+ w += el_timestamp(options, buf + w, TS_STRING);
w += el_finfo(options, buf + w, file, num);
if (w != 0 && buf[w - 1] == ']')
@@ -581,6 +427,31 @@ int el_ovprint
w += sprintf(buf + w, "%c/", char_level[level]);
}
+#if ENABLE_PREFIX
+ if (options->prefix)
+ {
+ /*
+ * there is a case where buf[w] will point to something different
+ * than '\0'. This is not wrong but will confuse strncat function
+ * and logs will be printed incorectly.
+ */
+
+ buf[w] = '\0';
+ strncat(buf + w, options->prefix, EL_PREFIX_LEN);
+
+ if ((flen = strlen(options->prefix)) > EL_PREFIX_LEN)
+ {
+ /*
+ * dodge a bullet - overflow would have occured
+ */
+
+ flen = EL_PREFIX_LEN;
+ }
+
+ w += flen;
+ }
+#endif
+
/*
* add requested log from format, we add + 1 to include null termination
*/
@@ -607,18 +478,42 @@ int el_ovprint
w += el_color(options, buf + w, 8 /* reset colors */);
+ if (options->print_newline)
+ {
+ /*
+ * add new line to log
+ */
+
+ buf[w++] = '\n';
+ }
+
+ buf[w++] = '\0';
+
/*
- * make sure buf is always null terminated and contains new line character
+ * some modules (like el-file) needs to know level of message they are
+ * printing, and such information may not be available from string, thus
+ * we set it here in a 'object global' variable
*/
- buf[w++] = '\n';
- buf[w++] = '\0';
+ options->level_current_msg = level;
if (el_oputs(options, buf) != 0)
{
+ options->level_current_msg = EL_DBG;
return -1;
}
+ /*
+ * after message is printed set current messasge level to debug, as next
+ * call might be using el_puts, which does not contain log level, and we
+ * thread all el_puts messages as they were debug. Note, it does not
+ * apply to log filtering, el_puts does not filter messages, but modules
+ * like el-file, will need this information to determin wheter fsync()
+ * data to file or not.
+ */
+
+ options->level_current_msg = EL_DBG;
+
if (e)
{
errno = e;
@@ -627,3 +522,4 @@ int el_ovprint
return 0;
}
+
diff --git a/src/config-priv.h b/src/el-private.h
index f662da1..c1c2d9b 100644
--- a/src/config-priv.h
+++ b/src/el-private.h
@@ -1,12 +1,14 @@
/* ==========================================================================
- Licensed under BSD 2clause license. See LICENSE file for more information
+ Licensed under BSD 2clause license See LICENSE file for more information
Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
========================================================================== */
+/*
+ * this file contains stuff internal to the library
+ */
-#ifndef EL_CONFIGPRIV_H
-#define EL_CONFIGPRIV_H 1
-
+#ifndef EL_PRIVATE_H
+#define EL_PRIVATE_H 1
/* ==========================================================================
_ __ __ ____ _ __
@@ -17,20 +19,47 @@
========================================================================== */
-
#if HAVE_CONFIG_H
-#include "config.h"
+# include "config.h"
+#endif
+
+#include "embedlog.h"
+#include "valid.h"
+
+#if HAVE_TERMIOS_H
+# include <termios.h>
#endif
+#include <limits.h>
-/* ==== library private macros ============================================== */
+/* ==========================================================================
+ __ __ __
+ ____ _ / /____ / /_ ____ _ / / _ __ ____ _ _____ _____
+ / __ `// // __ \ / __ \ / __ `// / | | / // __ `// ___// ___/
+ / /_/ // // /_/ // /_/ // /_/ // / | |/ // /_/ // / (__ )
+ \__, //_/ \____//_.___/ \__,_//_/ |___/ \__,_//_/ /____/
+ /____/
+ ========================================================================== */
-/* ==== defines for el_mprint function ====================================== */
+extern struct el_options g_options;
/* ==========================================================================
- single line of el_print_mem will be similar to this
+ __ __
+ _____ ____ ____ _____ / /_ ____ _ ____ / /_ _____
+ / ___// __ \ / __ \ / ___// __// __ `// __ \ / __// ___/
+ / /__ / /_/ // / / /(__ )/ /_ / /_/ // / / // /_ (__ )
+ \___/ \____//_/ /_//____/ \__/ \__,_//_/ /_/ \__//____/
+
+ ========================================================================== */
+
+
+/* ==== defines for el_pmemory function ===================================== */
+
+
+/* ==========================================================================
+ single line of el_pmemory will be similar to this
0xNNNN HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH CCCCCCCCCCCCCCC
========================================================================== */
@@ -75,30 +104,52 @@
/* ==========================================================================
length of long timestamp in a single log. Timestamp format is
- [yyyy-mm-dd hh:mm:ss.uuuuuu]
+ [yyyy-mm-dd hh:mm:ss]
- which is 28 bytes long
+ which is 21 bytes long
========================================================================== */
-#if ENABLE_TIMESTAMP
- #define EL_PRE_TS_LONG_LEN 28
-#else
- #define EL_PRE_TS_LONG_LEN 0
-#endif
+#define EL_PRE_TS_LONG_LEN 21
/* ==========================================================================
length of short timestamp in a single log. Timestamp format is
- [ssssssssss.uuuuuu]
+ [ssssssssss]
- which is 19 bytes long. This is maximum value for short timestamp, as it
+ which is 12 bytes long. This is maximum value for short timestamp, as it
can be shorter.
========================================================================== */
-#define EL_PRE_TS_SHORT_LEN 19
+#define EL_PRE_TS_SHORT_LEN 12
+
+
+/* ==========================================================================
+ Size of the fractions of seconds, that is part after seconds like:
+
+ [ssssssssss.fffffffff]
+
+ or
+
+ [yyyy-mm-dd hh:mm:ss.fffffffff]
+ ========================================================================== */
+
+
+#define EL_PRE_TS_FRACT_LEN 10
+
+
+/* ==========================================================================
+ Calculate what is the minimum needed length to hold longest timestamp
+ ========================================================================== */
+
+
+#if ENABLE_TIMESTAMP
+# define EL_PRE_TS_MAX (EL_PRE_TS_LONG_LEN + EL_PRE_TS_FRACT_LEN)
+#else
+# define EL_PRE_TS_MAX 0
+#endif
/* ==========================================================================
@@ -124,9 +175,9 @@
#if ENABLE_FINFO
- #define EL_PRE_FINFO_LEN ((EL_FLEN_MAX) + 3 + EL_PRE_FINFO_LINE_MAX_LEN)
+# define EL_PRE_FINFO_LEN ((EL_FLEN_MAX) + 3 + EL_PRE_FINFO_LINE_MAX_LEN)
#else
- #define EL_PRE_FINFO_LEN 0
+# define EL_PRE_FINFO_LEN 0
#endif
@@ -144,11 +195,18 @@
/* ==========================================================================
maximum length of preamble which may look like this
- [2017-05-24 14:43:10.123456][source-file.c:12345] e/
+ [2017-05-24 14:43:10.123456][source-file.c:12345] e/prefix
========================================================================== */
-#define EL_PRE_LEN (EL_PRE_TS_LONG_LEN + EL_PRE_FINFO_LEN + EL_PRE_LEVEL_LEN)
+#if ENABLE_PREFIX
+# define EL_PREFIX_LEN EL_PREFIX_MAX
+#else
+# define EL_PREFIX_LEN 0
+#endif
+
+#define EL_PRE_LEN (EL_PRE_TS_MAX + EL_PRE_FINFO_LEN + EL_PREFIX_LEN + \
+ EL_PRE_LEVEL_LEN)
/* ==========================================================================
@@ -159,9 +217,9 @@
#if ENABLE_COLORS
- #define EL_COLORS_LEN (5 + 4)
+# define EL_COLORS_LEN (5 + 4)
#else
- #define EL_COLORS_LEN 0
+# define EL_COLORS_LEN 0
#endif
@@ -175,4 +233,49 @@
#define EL_BUF_MAX (EL_PRE_LEN + (EL_LOG_MAX) + EL_COLORS_LEN)
+/* ==========================================================================
+ Defines if timestamp should be stored into buffer as string or binary
+ ========================================================================== */
+
+
+#define TS_STRING (0)
+#define TS_BINARY (1)
+
+
+/* ==========================================================================
+ ____ __ _
+ / __/__ __ ____ _____ / /_ (_)____ ____ _____
+ / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/
+ / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ )
+ /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/
+
+ ========================================================================== */
+
+
+#if ENABLE_OUT_FILE
+int el_file_open(struct el_options *options);
+int el_file_puts(struct el_options *options, const char *s);
+int el_file_putb(struct el_options *options, const void *mem, size_t mlen);
+void el_file_cleanup(struct el_options *options);
+#endif
+
+
+#if ENABLE_OUT_TTY
+int el_tty_open(struct el_options *options, const char *dev, speed_t speed);
+int el_tty_puts(struct el_options *options, const char *s);
+int el_tty_close(struct el_options *options);
+#endif
+
+int el_log_allowed(struct el_options *options, enum el_level level);
+size_t el_timestamp(struct el_options *options, void *buf, int binary);
+
+#if LLONG_MAX
+size_t el_encode_number(unsigned long long number, void *out);
+size_t el_decode_number(const void *number, unsigned long long *out);
+#else
+size_t el_encode_number(unsigned long number, void *out);
+size_t el_decode_number(const void *number, unsigned long *out);
+#endif
+
+
#endif
diff --git a/src/el-puts.c b/src/el-puts.c
index c6d7119..9ba9d1e 100644
--- a/src/el-puts.c
+++ b/src/el-puts.c
@@ -34,20 +34,11 @@
#include "config.h"
#endif
+#include "el-private.h"
+
#include <errno.h>
#include <stdio.h>
-#include "el-options.h"
-#include "embedlog.h"
-#include "valid.h"
-
-#if ENABLE_OUT_FILE
-#include "el-file.h"
-#endif
-
-#if ENABLE_OUT_TTY
-#include "el-tty.h"
-#endif
/* ==========================================================================
__ __ _
@@ -107,10 +98,10 @@ int el_oputs
}
#endif
-#if 0 /* TODO */
+#if ENABLE_OUT_SYSLOG
if (options->outputs & EL_OUT_SYSLOG)
{
- el_puts_syslog(s);
+ syslog(options->level, s);
}
#endif
@@ -131,7 +122,7 @@ int el_oputs
#if ENABLE_OUT_TTY
if (options->outputs & EL_OUT_TTY)
{
- el_tty_puts(options, s);
+ rv |= el_tty_puts(options, s);
}
#endif
@@ -144,3 +135,67 @@ int el_oputs
return rv;
}
+
+
+/* ==========================================================================
+ puts memory 'mem' to all supported output facilities specified by
+ default options. Not all outputs support printing binary data
+ ========================================================================== */
+
+
+int el_putb
+(
+ const void *mem, /* memory location to 'print' */
+ size_t mlen /* size of the mem buffer */
+)
+{
+ return el_oputb(&g_options, mem, mlen);
+}
+
+
+/* ==========================================================================
+ Puts binary data 'mem' of size 'mlen' to enabled output facilities
+ specified by 'options'. No all outputs support printing binary data
+ ========================================================================== */
+
+
+int el_oputb
+(
+ struct el_options *options, /* options defining printing style */
+ const void *mem, /* memory location to 'print' */
+ size_t mlen /* size of the mem buffer */
+)
+{
+ int rv; /* return value from function */
+ int called; /* at least one output was called */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ VALID(EINVAL, options);
+ VALID(EINVAL, mem);
+ VALID(EINVAL, mlen);
+ VALID(ENODEV, options->outputs != 0);
+
+ rv = 0;
+ called = 0;
+
+#if ENABLE_OUT_FILE
+ if (options->outputs & EL_OUT_FILE)
+ {
+ called = 1;
+ rv |= el_file_putb(options, mem, mlen);
+ }
+#endif
+
+ if (called == 0)
+ {
+ /*
+ * couldn't find any supported output for binary logging
+ */
+
+ errno = ENODEV;
+ return -1;
+ }
+
+ return rv;
+}
diff --git a/src/el-syslog.c b/src/el-syslog.c
new file mode 100644
index 0000000..259beb1
--- /dev/null
+++ b/src/el-syslog.c
@@ -0,0 +1,170 @@
+/* ==========================================================================
+ Licensed under BSD 2clause license See LICENSE file for more information
+ Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
+ ========================================================================== */
+
+
+/* ==========================================================================
+ _ __ __ ____ _ __
+ (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____
+ / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/
+ / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ )
+ /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/
+
+ ========================================================================== */
+
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "el-private.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#if HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+
+/* ==========================================================================
+ __ __ _
+ ____ __ __ / /_ / /(_)_____
+ / __ \ / / / // __ \ / // // ___/
+ / /_/ // /_/ // /_/ // // // /__
+ / .___/ \__,_//_.___//_//_/ \___/
+ /_/
+ ____ __ _
+ / __/__ __ ____ _____ / /_ (_)____ ____ _____
+ / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/
+ / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ )
+ /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/
+
+ ========================================================================== */
+
+
+/* ==========================================================================
+ Open tty device and configure it for output only
+ ========================================================================== */
+
+
+int el_tty_open
+(
+ struct el_options *options, /* store serial file descriptor here */
+ const char *dev, /* device to open like /dev/ttyS0 */
+ speed_t speed /* serial port baud rate */
+)
+{
+ struct termios tty; /* serial port settings */
+ int e; /* holder for errno */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ /*
+ * it is possible to reopen serial device (like changing output device
+ * with EL_TTY_DEV again) during runtime, so we need to close previous
+ * socket first
+ */
+
+ el_tty_close(options);
+
+ if ((options->serial_fd = open(dev, O_WRONLY | O_NOCTTY | O_SYNC)) < 0)
+ {
+ return -1;
+ }
+
+#if HAVE_TERMIOS_H
+ /*
+ * if termios is not available, simply open device without reconfiguring
+ * it. Some embedded OSes might configure tty during compilation and
+ * have termios disabled to save memory.
+ */
+
+ if (speed == B0)
+ {
+ /*
+ * normally with B0 we should terminate transmission, it is used
+ * with modem lines, but since we do not use modem lines, and in
+ * logger terminating connection does not make any sense, we use
+ * this to tell embedlog *not* to change any settings but only open
+ * serial
+ */
+
+ return 0;
+ }
+
+ if (tcgetattr(options->serial_fd, &tty) != 0)
+ {
+ goto error;
+ }
+
+ if (cfsetispeed(&tty, speed) != 0)
+ {
+ goto error;
+ }
+
+ tty.c_cflag &= ~CSIZE; /* apply size mask */
+ tty.c_cflag |= CS8; /* 8bit transmission */
+ tty.c_cflag &= ~PARENB; /* no parity check */
+ tty.c_cflag &= ~CSTOPB; /* set one stop bit */
+
+ tty.c_cflag |= CLOCAL; /* ignore modem lines */
+ tty.c_cflag &= ~CREAD; /* disable receiver - we only send data */
+ tty.c_oflag |= OPOST | ONLCR; /* enable output post-processing by OS */
+
+ if (tcsetattr(options->serial_fd, TCSANOW, &tty) != 0)
+ {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ e = errno;
+ close(options->serial_fd);
+ options->serial_fd = -1;
+ errno = e;
+ return -1;
+
+#else
+ (void)tty;
+ (void)e;
+ return 0;
+#endif
+}
+
+
+/* ==========================================================================
+ Yup, you guessed right - it closes serial.
+ ========================================================================== */
+
+
+int el_tty_close
+(
+ struct el_options *options /* options object with serial descriptor */
+)
+{
+ VALID(EINVAL, options->serial_fd != -1);
+
+ close(options->serial_fd);
+ options->serial_fd = -1;
+ return 0;
+}
+
+
+/* ==========================================================================
+ Simply sends string pointed by s to serial port configured in options
+ ========================================================================== */
+
+
+int el_tty_puts
+(
+ struct el_options *options, /* options object with serial descriptor */
+ const char *s /* string to send */
+)
+{
+ return write(options->serial_fd, s, strlen(s));
+}
diff --git a/src/el-ts.c b/src/el-ts.c
new file mode 100644
index 0000000..503c437
--- /dev/null
+++ b/src/el-ts.c
@@ -0,0 +1,300 @@
+/* ==========================================================================
+ Licensed under BSD 2clause license See LICENSE file for more information
+ Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
+ ========================================================================== */
+
+
+/* ==========================================================================
+ _ __ __ ____ _ __
+ (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____
+ / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/
+ / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ )
+ /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/
+
+ ========================================================================== */
+
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "el-private.h"
+
+#include <time.h>
+
+
+/* ==========================================================================
+ returns seconds and nanoseconds calculated from clock() function
+ ========================================================================== */
+
+
+#if ENABLE_TIMESTAMP
+#if ENABLE_CLOCK
+static void el_ts_clock
+(
+ time_t *s, /* seconds will be stored here */
+ long *ns /* nanoseconds will be stored here */
+)
+{
+ clock_t clk; /* clock value */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ clk = clock();
+
+ *s = clk / CLOCKS_PER_SEC;
+ *ns = clk % CLOCKS_PER_SEC; /* [cps] */
+ *ns *= (1000000L / CLOCKS_PER_SEC); /* [ms] */
+ *ns *= 1000L; /* [ns] */
+}
+#endif /* ENABLE_CLOCK */
+#endif /* ENABLE_TIMESTAMP */
+
+/* ==========================================================================
+ returns seconds and nanoseconds calculated from time() function.
+ ========================================================================== */
+
+
+#if ENABLE_TIMESTAMP
+static void el_ts_time
+(
+ time_t *s, /* seconds will be stored here */
+ long *ns /* nanoseconds will be stored here */
+)
+{
+ *s = time(NULL);
+ *ns = 0;
+}
+#endif /* ENABLE_TIMESTAMP */
+
+/* ==========================================================================
+ returns seconds and nanoseconds calculated from clock_gettime function
+ ========================================================================== */
+
+
+#if ENABLE_TIMESTAMP
+#if ENABLE_REALTIME || ENABLE_MONOTONIC
+static void el_ts_clock_gettime
+(
+ time_t *s, /* seconds will be stored here */
+ long *ns, /* nanoseconds will be stored here */
+ clockid_t clkid /* clock id */
+)
+{
+ struct timespec tp; /* clock value */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ clock_gettime(clkid, &tp);
+
+ *s = tp.tv_sec;
+ *ns = tp.tv_nsec;
+}
+#endif /* ENABLE_REALTIME || ENABLE_MONOTONIC */
+#endif /* ENABLE_TIMESTAMP */
+
+
+/* ==========================================================================
+ returns current timestamp in 'buf' location. Depending on global
+ settings timestamp can be in long or short format. Function will return
+ number of bytes copied to 'buf'. If timestamp has been disabled during
+ compilation time or in runtime during settings, function will return 0.
+ ========================================================================== */
+
+
+size_t el_timestamp
+(
+ struct el_options *options, /* options defining printing style */
+ void *b, /* buffer where timestamp will be stored */
+ int binary /* 1 if timestamp should be binary */
+)
+{
+#if ENABLE_TIMESTAMP
+ time_t s; /* timestamp seconds */
+ long ns; /* timestamp nanoseconds */
+ size_t tl; /* timestamp length */
+ char *buf; /* b threated as known type */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ if (options->timestamp == EL_TS_OFF)
+ {
+ /*
+ * user doesn't want us to print timestamp, that's fine
+ */
+
+ return 0;
+ }
+
+ buf = b;
+
+ /*
+ * first we get seconds and nanoseconds from proper timer
+ */
+
+ switch (options->timestamp_timer)
+ {
+# if ENABLE_REALTIME
+
+ case EL_TS_TM_REALTIME:
+ el_ts_clock_gettime(&s, &ns, CLOCK_REALTIME);
+ break;
+
+# endif
+
+# if ENABLE_MONOTONIC
+
+ case EL_TS_TM_MONOTONIC:
+ el_ts_clock_gettime(&s, &ns, CLOCK_MONOTONIC);
+ break;
+
+# endif
+
+ case EL_TS_TM_TIME:
+ el_ts_time(&s, &ns);
+ break;
+
+# if ENABLE_CLOCK
+
+ case EL_TS_TM_CLOCK:
+ el_ts_clock(&s, &ns);
+ break;
+
+# endif
+
+ default:
+ /*
+ * bad timer means no timer
+ */
+
+ return 0;
+ }
+
+ if (binary)
+ {
+ /*
+ * put encoded seconds timestamp in buf
+ */
+
+# ifdef LLONG_MAX
+ tl = el_encode_number((unsigned long long)s, (unsigned char *)buf);
+# else
+ tl = el_encode_number((unsigned long)s, (unsigned char*)buf);
+# endif
+
+ /*
+ * put encoded nano/micro/milli seconds in buf if enabled
+ */
+# if ENABLE_FRACTIONS
+
+ switch (options->timestamp_fractions)
+ {
+ case EL_TS_FRACT_OFF:
+ /*
+ * we don't add fractions, so simply return what has been
+ * already stored (only seconds)
+ */
+
+ return tl;
+
+ case EL_TS_FRACT_MS:
+ tl += el_encode_number(ns / 1000000, buf + tl);
+ return tl;
+
+ case EL_TS_FRACT_US:
+ tl += el_encode_number(ns / 1000, buf + tl);
+ return tl;
+
+ case EL_TS_FRACT_NS:
+ tl += el_encode_number(ns, buf + tl);
+ return tl;
+
+ default:
+ /*
+ * something went somehere seriously wrong, act like no
+ * timestamp has been configured
+ */
+
+ return 0;
+ }
+#else /* ENABLE_FRACTIONS */
+ return tl;
+#endif /* ENABLE_FRACTIONS */
+ }
+ else
+ {
+ /*
+ * then convert retrieved time into string timestamp
+ */
+
+ if (options->timestamp == EL_TS_LONG)
+ {
+ struct tm tm; /* timestamp splitted */
+ struct tm *tmp; /* timestamp splitted pointer */
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+# if ENABLE_REENTRANT
+ tmp = gmtime_r(&s, &tm);
+# else
+ tmp = gmtime(&s);
+# endif
+
+ tl = strftime(buf, 21, "[%Y-%m-%d %H:%M:%S", tmp);
+ }
+ else
+ {
+# ifdef LLONG_MAX
+ tl = sprintf(buf, "[%lld", (long long)s);
+# else
+ /*
+ * if long long is not available, code may be suscible to 2038
+ * bug. If you are sure your compiler does support long long
+ * type, but doesn't define LLONG_MAX, define this value to
+ * enable long long.
+ */
+
+ tl = sprintf(buf, "[%ld", (long)s);
+# endif
+ }
+
+ /*
+ * if requested, add proper fractions of seconds
+ */
+# if ENABLE_FRACTIONS
+ switch (options->timestamp_fractions)
+ {
+ case EL_TS_FRACT_OFF:
+ /*
+ * we don't add fractions, so simply close opening bracker '['
+ */
+
+ buf[tl++] = ']';
+ return tl;
+
+ case EL_TS_FRACT_MS:
+ tl += sprintf(buf + tl, ".%03ld]", ns / 1000000);
+ return tl;
+
+ case EL_TS_FRACT_US:
+ tl += sprintf(buf + tl, ".%06ld]", ns / 1000);
+ return tl;
+
+ case EL_TS_FRACT_NS:
+ tl += sprintf(buf + tl, ".%09ld]", ns);
+ return tl;
+
+ default:
+ /*
+ * something went somehere seriously wrong, act like no
+ * timestamp has been configured
+ */
+
+ return 0;
+ }
+# else /* ENABLE_FRACTIONS */
+ buf[tl++] = ']';
+ return tl;
+# endif /* ENABLE_FRACTIONS */
+ }
+
+#else /* ENABLE_TIMESTAMP */
+ return 0;
+#endif /* ENABLE_TIMESTAMP */
+}
diff --git a/src/el-tty.c b/src/el-tty.c
index 6e8300f..0b0791d 100644
--- a/src/el-tty.c
+++ b/src/el-tty.c
@@ -18,17 +18,16 @@
#include "config.h"
#endif
-#include "el-options.h"
-#include "el-tty.h"
-#include "valid.h"
+#include "el-private.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
-#include <termios.h>
#include <unistd.h>
-
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
/* ==========================================================================
__ __ _
@@ -59,7 +58,6 @@ int el_tty_open
)
{
struct termios tty; /* serial port settings */
- int sfd; /* serial port file descriptor */
int e; /* holder for errno */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@@ -72,12 +70,32 @@ int el_tty_open
el_tty_close(options);
- if ((sfd = open(dev, O_WRONLY | O_NOCTTY | O_SYNC)) < 0)
+ if ((options->serial_fd = open(dev, O_WRONLY | O_NOCTTY | O_SYNC)) < 0)
{
return -1;
}
- if (tcgetattr(sfd, &tty) != 0)
+#if HAVE_TERMIOS_H
+ /*
+ * if termios is not available, simply open device without reconfiguring
+ * it. Some embedded OSes might configure tty during compilation and
+ * have termios disabled to save memory.
+ */
+
+ if (speed == B0)
+ {
+ /*
+ * normally with B0 we should terminate transmission, it is used
+ * with modem lines, but since we do not use modem lines, and in
+ * logger terminating connection does not make any sense, we use
+ * this to tell embedlog *not* to change any settings but only open
+ * serial
+ */
+
+ return 0;
+ }
+
+ if (tcgetattr(options->serial_fd, &tty) != 0)
{
goto error;
}
@@ -96,19 +114,25 @@ int el_tty_open
tty.c_cflag &= ~CREAD; /* disable receiver - we only send data */
tty.c_oflag |= OPOST | ONLCR; /* enable output post-processing by OS */
- if (tcsetattr(sfd, TCSANOW, &tty) != 0)
+ if (tcsetattr(options->serial_fd, TCSANOW, &tty) != 0)
{
goto error;
}
- options->serial_fd = sfd;
return 0;
error:
e = errno;
- close(sfd);
+ close(options->serial_fd);
+ options->serial_fd = -1;
errno = e;
return -1;
+
+#else
+ (void)tty;
+ (void)e;
+ return 0;
+#endif
}
diff --git a/src/el-tty.h b/src/el-tty.h
deleted file mode 100644
index 2d56561..0000000
--- a/src/el-tty.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* ==========================================================================
- Licensed under BSD 2clause license See LICENSE file for more information
- Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
- ========================================================================== */
-
-#ifndef EL_TTY_H
-#define EL_TTY_H 1
-
-#include "el-options.h"
-#include <termios.h>
-
-int el_tty_open(struct el_options *, const char *, speed_t);
-int el_tty_puts(struct el_options *, const char *);
-int el_tty_close(struct el_options *);
-
-#endif
diff --git a/src/snprintf.c b/src/snprintf.c
index 2eb2926..f26389c 100644
--- a/src/snprintf.c
+++ b/src/snprintf.c
@@ -411,10 +411,12 @@ int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
/* declarations */
+#if !defined(HAVE_SNPRINTF)
static char credits[] = "\n\
@(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
+#endif
#if defined(NEED_ASPRINTF)
int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
diff --git a/test-compilation.sh b/test-compilation.sh
index 8db2336..ca6093c 100755
--- a/test-compilation.sh
+++ b/test-compilation.sh
@@ -4,10 +4,14 @@ options=(--disable-out-file \
--disable-out-stderr \
--disable-out-tty \
--disable-timestamp \
+ --disable-fractions \
--disable-realtime \
--disable-monotonic \
--disable-finfo \
- --disable-colors)
+ --disable-colors \
+ --disable-prefix \
+ --disable-reentrant \
+ --enable-binary-logs)
iterations=$((2**${#options[@]} - 1))
for i in `seq 0 1 $iterations`
diff --git a/tst/Makefile.am b/tst/Makefile.am
index fe72bc6..1866f7b 100644
--- a/tst/Makefile.am
+++ b/tst/Makefile.am
@@ -9,6 +9,7 @@ test_SOURCES += main.c \
test-el-file.c \
test-el-perror.c \
test-el-pmemory.c \
+ test-el-pbinary.c \
mtest.h \
test-group-list.h
@@ -16,6 +17,7 @@ test_CFLAGS = -I$(top_srcdir)/include \
-I$(top_srcdir)/src \
-I/usr/local/include \
-I/usr/include \
+ -DRUN_TESTS=1 \
$(COVERAGE_CFLAGS)
test_LDFLAGS = -L/usr/local/lib \
diff --git a/tst/el-decode-number.c b/tst/el-decode-number.c
new file mode 120000
index 0000000..268c6a8
--- /dev/null
+++ b/tst/el-decode-number.c
@@ -0,0 +1 @@
+../src/el-decode-number.c \ No newline at end of file
diff --git a/tst/el-encode-number.c b/tst/el-encode-number.c
new file mode 120000
index 0000000..c90c2c0
--- /dev/null
+++ b/tst/el-encode-number.c
@@ -0,0 +1 @@
+../src/el-encode-number.c \ No newline at end of file
diff --git a/tst/el-pbinary.c b/tst/el-pbinary.c
new file mode 120000
index 0000000..3e46c13
--- /dev/null
+++ b/tst/el-pbinary.c
@@ -0,0 +1 @@
+../src/el-pbinary.c \ No newline at end of file
diff --git a/tst/el-ts.c b/tst/el-ts.c
new file mode 120000
index 0000000..478d0cd
--- /dev/null
+++ b/tst/el-ts.c
@@ -0,0 +1 @@
+../src/el-ts.c \ No newline at end of file
diff --git a/tst/main.c b/tst/main.c
index 2d282f3..8a73d00 100644
--- a/tst/main.c
+++ b/tst/main.c
@@ -3,7 +3,6 @@
Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
========================================================================== */
-#include "embedlog.h"
#include "mtest.h"
#include "test-group-list.h"
@@ -16,5 +15,6 @@ int main(void)
el_file_test_group();
el_perror_test_group();
el_pmemory_test_group();
+ el_pbinary_test_group();
mt_return();
}
diff --git a/tst/test-el-file.c b/tst/test-el-file.c
index 2464de9..dd13683 100644
--- a/tst/test-el-file.c
+++ b/tst/test-el-file.c
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -30,12 +31,12 @@
/* ==========================================================================
- _ __
- ____ _____ (_)_ __ ____ _ / /_ ___
- / __ \ / ___// /| | / // __ `// __// _ \
- / /_/ // / / / | |/ // /_/ // /_ / __/
- / .___//_/ /_/ |___/ \__,_/ \__/ \___/
- /_/
+ __ __ __
+ ____ _ / /____ / /_ ____ _ / /
+ / __ `// // __ \ / __ \ / __ `// /
+ / /_/ // // /_/ // /_/ // /_/ // /
+ \__, //_/ \____//_.___/ \__,_//_/
+ /____/
_ __ __
_ __ ____ _ _____ (_)____ _ / /_ / /___ _____
| | / // __ `// ___// // __ `// __ \ / // _ \ / ___/
@@ -45,11 +46,29 @@
========================================================================== */
+/*
+ * variable is set in el-file.c when we successfully executed fsync path of
+ * the code
+ */
+int file_synced;
+
+
+/* ==========================================================================
+ __ __
+ _____ ____ ____ _____ / /_ ____ _ ____ / /_ _____
+ / ___// __ \ / __ \ / ___// __// __ `// __ \ / __// ___/
+ / /__ / /_/ // / / /(__ )/ /_ / /_/ // / / // /_ (__ )
+ \___/ \____//_/ /_//____/ \__/ \__,_//_/ /_/ \__//____/
+
+ ========================================================================== */
+
+
#define WORKDIR "./embedlog-tests"
#define s9 "123456789"
#define s8 "qwertyui"
#define s5 "qwert"
#define s3 "asd"
+#define s1 "a"
mt_defs_ext();
@@ -106,8 +125,9 @@ static void test_prepare(void)
el_option(EL_OUT, EL_OUT_FILE);
el_option(EL_FROTATE_SIZE, 16);
el_option(EL_FROTATE_NUMBER, 0);
- el_option(EL_FNAME, WORKDIR"/log");
-
+ el_option(EL_FPATH, WORKDIR"/log");
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ file_synced = 0;
}
@@ -190,7 +210,8 @@ static void file_reopen(void)
el_option(EL_OUT, EL_OUT_FILE);
el_option(EL_FROTATE_SIZE, 16);
el_option(EL_FROTATE_NUMBER, 0);
- el_option(EL_FNAME, WORKDIR"/log");
+ el_option(EL_FPATH, WORKDIR"/log");
+ el_option(EL_FILE_SYNC_EVERY, 0);
el_puts(s8);
mt_fok(file_check(WORKDIR"/log", s9 s8));
@@ -210,7 +231,8 @@ static void file_reopen_different_file(void)
el_option(EL_OUT, EL_OUT_FILE);
el_option(EL_FROTATE_SIZE, 16);
el_option(EL_FROTATE_NUMBER, 0);
- el_option(EL_FNAME, WORKDIR"/log-another");
+ el_option(EL_FPATH, WORKDIR"/log-another");
+ el_option(EL_FILE_SYNC_EVERY, 0);
el_puts(s8);
mt_fok(file_check(WORKDIR"/log", s9));
@@ -235,6 +257,104 @@ static void file_unexpected_third_party_delete(void)
========================================================================== */
+static void file_directory_deleted(void)
+{
+ el_puts(s9);
+ unlink(WORKDIR"/log");
+ rmdir(WORKDIR);
+ mt_ferr(el_puts(s9), ENOENT);
+ mkdir(WORKDIR, 0755);
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_directory_reappear_after_delete(void)
+{
+ el_puts(s9);
+ unlink(WORKDIR"/log");
+ rmdir(WORKDIR);
+ mt_ferr(el_puts(s9), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(el_puts(s9));
+ mt_fok(file_check(WORKDIR"/log", s9));
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_write_after_failed_open(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+
+ rmdir(WORKDIR);
+ mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(el_puts(s9));
+ mt_fok(file_check(WORKDIR"/log", s9));
+
+ unlink(WORKDIR"/log");
+ el_cleanup();
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_write_after_failed_open_to_existing_file(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+
+ rmdir(WORKDIR);
+ mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(system("echo test > \""WORKDIR"/log\""));
+ mt_fok(el_puts(s9));
+ mt_fok(file_check(WORKDIR"/log", "test\n"s9));
+
+ unlink(WORKDIR"/log");
+ el_cleanup();
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_and_directory_reapear(void)
+{
+ el_puts(s9);
+ unlink(WORKDIR"/log");
+ rmdir(WORKDIR);
+ mt_ferr(el_puts(s9), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(system("echo test > \""WORKDIR"/log\""));
+ mt_fok(el_puts(s9));
+ mt_fok(file_check(WORKDIR"/log", "test\n"s9));
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
static void file_filename_too_long(void)
{
char path[8192];
@@ -243,7 +363,7 @@ static void file_filename_too_long(void)
memset(path, 'a', sizeof(path));
path[sizeof(path) - 1] = '\0';
- mt_ferr(el_option(EL_FNAME, path), ENAMETOOLONG);
+ mt_ferr(el_option(EL_FPATH, path), ENAMETOOLONG);
}
@@ -265,7 +385,7 @@ static void file_path_too_long(void)
path[sizeof(path) - 3] = 'l';
path[sizeof(path) - 2] = 'e';
path[sizeof(path) - 1] = '\0';
- mt_ferr(el_option(EL_FNAME, path), ENAMETOOLONG);
+ mt_ferr(el_option(EL_FPATH, path), ENAMETOOLONG);
}
@@ -289,9 +409,11 @@ static void file_print_after_cleanup(void)
el_option(EL_OUT, EL_OUT_FILE);
el_option(EL_FROTATE_SIZE, 16);
el_option(EL_FROTATE_NUMBER, 0);
- el_option(EL_FNAME, WORKDIR"/log");
+ el_option(EL_FPATH, WORKDIR"/log");
+ el_option(EL_FILE_SYNC_EVERY, 0);
el_cleanup();
mt_ferr(el_puts("whatev"), ENODEV);
+ unlink(WORKDIR"/log");
}
@@ -305,6 +427,7 @@ static void file_print_without_setting_file(void)
el_option(EL_OUT, EL_OUT_FILE);
el_option(EL_FROTATE_SIZE, 16);
el_option(EL_FROTATE_NUMBER, 0);
+ el_option(EL_FILE_SYNC_EVERY, 0);
mt_ferr(el_puts("no file set"), EBADF);
el_cleanup();
}
@@ -393,7 +516,8 @@ static void file_rotate_1_reopen(void)
el_option(EL_OUT, EL_OUT_FILE);
el_option(EL_FROTATE_SIZE, 16);
el_option(EL_FROTATE_NUMBER, 1);
- el_option(EL_FNAME, WORKDIR"/log");
+ el_option(EL_FPATH, WORKDIR"/log");
+ el_option(EL_FILE_SYNC_EVERY, 0);
el_puts(s8);
mt_fok(file_check(WORKDIR"/log.0", s5 s8));
@@ -535,7 +659,8 @@ static void file_rotate_2_reopen(void)
el_option(EL_OUT, EL_OUT_FILE);
el_option(EL_FROTATE_SIZE, 16);
el_option(EL_FROTATE_NUMBER, 2);
- el_option(EL_FNAME, WORKDIR"/log");
+ el_option(EL_FPATH, WORKDIR"/log");
+ el_option(EL_FILE_SYNC_EVERY, 0);
el_puts(s5);
mt_fok(file_check(WORKDIR"/log.0", s9));
@@ -716,7 +841,8 @@ static void file_rotate_5_reopen(void)
el_option(EL_OUT, EL_OUT_FILE);
el_option(EL_FROTATE_SIZE, 16);
el_option(EL_FROTATE_NUMBER, 5);
- el_option(EL_FNAME, WORKDIR"/log");
+ el_option(EL_FPATH, WORKDIR"/log");
+ el_option(EL_FILE_SYNC_EVERY, 0);
el_puts(s9);
el_puts(s8);
@@ -754,20 +880,22 @@ static void file_rotate_5_hole_in_log_rotate(void)
el_puts("123");
mt_fok(file_check(WORKDIR"/log.0", "qaz"));
- mt_fok(file_check(WORKDIR"/log.1", "edc"));
- mt_fok(file_check(WORKDIR"/log.2", "rfv"));
+ mt_fok(file_check(WORKDIR"/log.2", "edc"));
+ mt_fok(file_check(WORKDIR"/log.3", "rfv"));
mt_fok(file_check(WORKDIR"/log.4", "123"));
el_puts("456");
- mt_fok(file_check(WORKDIR"/log.0", "edc"));
- mt_fok(file_check(WORKDIR"/log.1", "rfv"));
+ mt_fok(file_check(WORKDIR"/log.0", "qaz"));
+ mt_fok(file_check(WORKDIR"/log.1", "edc"));
+ mt_fok(file_check(WORKDIR"/log.2", "rfv"));
mt_fok(file_check(WORKDIR"/log.3", "123"));
mt_fok(file_check(WORKDIR"/log.4", "456"));
el_puts("789");
- mt_fok(file_check(WORKDIR"/log.0", "rfv"));
+ mt_fok(file_check(WORKDIR"/log.0", "edc"));
+ mt_fok(file_check(WORKDIR"/log.1", "rfv"));
mt_fok(file_check(WORKDIR"/log.2", "123"));
mt_fok(file_check(WORKDIR"/log.3", "456"));
mt_fok(file_check(WORKDIR"/log.4", "789"));
@@ -804,7 +932,7 @@ static void file_rotate_5_rename_file_halfway(void)
el_puts("rfv");
el_puts("tgb");
- el_option(EL_FNAME, WORKDIR"/log-another");
+ el_option(EL_FPATH, WORKDIR"/log-another");
el_puts("123");
el_puts("456");
@@ -830,10 +958,313 @@ static void file_rotate_5_rename_file_halfway(void)
========================================================================== */
+static void file_rotate_directory_deleted(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ el_option(EL_FROTATE_SIZE, 3);
+ el_option(EL_FROTATE_NUMBER, 5);
+ mt_fok(el_option(EL_FPATH, WORKDIR"/log"));
+
+ el_puts("qaz");
+ el_puts("wsx");
+ el_puts("edc");
+
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+ rmdir(WORKDIR);
+ mt_ferr(el_puts("rfv"), ENOENT);
+
+ mkdir(WORKDIR, 0755);
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_rotate_directory_reappear_after_delete(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ el_option(EL_FROTATE_SIZE, 3);
+ el_option(EL_FROTATE_NUMBER, 5);
+ mt_fok(el_option(EL_FPATH, WORKDIR"/log"));
+
+ el_puts("qaz");
+ el_puts("wsx");
+ el_puts("edc");
+
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+ rmdir(WORKDIR);
+ mt_ferr(el_puts("rfv"), ENOENT);
+
+ mkdir(WORKDIR, 0755);
+
+ mt_fok(el_puts("rfv"));
+ mt_fok(el_puts("tgb"));
+ mt_fok(el_puts("yhn"));
+ mt_fok(el_puts("ujm"));
+ mt_fok(file_check(WORKDIR"/log.0", "rfv"));
+ mt_fok(file_check(WORKDIR"/log.1", "tgb"));
+ mt_fok(file_check(WORKDIR"/log.2", "yhn"));
+ mt_fok(file_check(WORKDIR"/log.3", "ujm"));
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+ unlink(WORKDIR"/log.3");
+ unlink(WORKDIR"/log.4");
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_rotate_write_after_failed_open(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ el_option(EL_FROTATE_SIZE, 3);
+ el_option(EL_FROTATE_NUMBER, 5);
+
+ rmdir(WORKDIR);
+ mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(el_puts("qaz"));
+ mt_fok(file_check(WORKDIR"/log.0", "qaz"));
+
+ unlink(WORKDIR"/log.0");
+ el_cleanup();
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_rotate_write_after_failed_open_to_existing_file(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ el_option(EL_FROTATE_SIZE, 3);
+ el_option(EL_FROTATE_NUMBER, 5);
+
+ rmdir(WORKDIR);
+ mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(system("echo -n qaz > \""WORKDIR"/log.0\""));
+ mt_fok(system("echo -n ws > \""WORKDIR"/log.1\""));
+ mt_fok(el_puts("edc"));
+ mt_fok(file_check(WORKDIR"/log.0", "qaz"));
+ mt_fok(file_check(WORKDIR"/log.1", "ws"));
+ mt_fok(file_check(WORKDIR"/log.2", "edc"));
+
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+ el_cleanup();
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_rotate_write_after_failed_open_to_existing_file_with_holes(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ el_option(EL_FROTATE_SIZE, 3);
+ el_option(EL_FROTATE_NUMBER, 5);
+
+ rmdir(WORKDIR);
+ mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(system("echo -n qaz > \""WORKDIR"/log.0\""));
+ mt_fok(system("echo -n ws > \""WORKDIR"/log.2\""));
+ mt_fok(system("echo -n e > \""WORKDIR"/log.4\""));
+ mt_fok(el_puts("123"));
+ mt_fok(el_puts("456"));
+
+ mt_fok(file_check(WORKDIR"/log.0", "ws"));
+ mt_fok(file_check(WORKDIR"/log.2", "e"));
+ mt_fok(file_check(WORKDIR"/log.3", "123"));
+ mt_fok(file_check(WORKDIR"/log.4", "456"));
+
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+ unlink(WORKDIR"/log.3");
+ unlink(WORKDIR"/log.4");
+ el_cleanup();
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_rotate_write_after_failed_open_to_existing_file_with_holes2(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ el_option(EL_FROTATE_SIZE, 3);
+ el_option(EL_FROTATE_NUMBER, 5);
+
+ rmdir(WORKDIR);
+ mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(system("echo -n qaz > \""WORKDIR"/log.0\""));
+ mt_fok(system("echo -n ws > \""WORKDIR"/log.3\""));
+ mt_fok(system("echo -n e > \""WORKDIR"/log.4\""));
+ mt_fok(el_puts("123"));
+ mt_fok(el_puts("456"));
+
+ mt_fok(file_check(WORKDIR"/log.0", "qaz"));
+ mt_fok(file_check(WORKDIR"/log.1", "ws"));
+ mt_fok(file_check(WORKDIR"/log.2", "e"));
+ mt_fok(file_check(WORKDIR"/log.3", "123"));
+ mt_fok(file_check(WORKDIR"/log.4", "456"));
+
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+ unlink(WORKDIR"/log.3");
+ unlink(WORKDIR"/log.4");
+ el_cleanup();
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_rotate_write_after_failed_open_to_existing_file_with_holes3(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ el_option(EL_FROTATE_SIZE, 3);
+ el_option(EL_FROTATE_NUMBER, 5);
+
+ rmdir(WORKDIR);
+ mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT);
+ mkdir(WORKDIR, 0755);
+ mt_fok(system("echo -n qaz > \""WORKDIR"/log.1\""));
+ mt_fok(system("echo -n ws > \""WORKDIR"/log.3\""));
+ mt_fok(system("echo -n e > \""WORKDIR"/log.4\""));
+ mt_fok(el_puts("123"));
+ mt_fok(el_puts("456"));
+ mt_fok(el_puts("789"));
+ mt_fok(file_check(WORKDIR"/log.0", "ws"));
+ mt_fok(file_check(WORKDIR"/log.1", "e"));
+ mt_fok(file_check(WORKDIR"/log.2", "123"));
+ mt_fok(file_check(WORKDIR"/log.3", "456"));
+ mt_fok(file_check(WORKDIR"/log.4", "789"));
+
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+ unlink(WORKDIR"/log.3");
+ unlink(WORKDIR"/log.4");
+ el_cleanup();
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_rotate_and_directory_reappear(void)
+{
+ /*
+ * mt_prepare_test not running here
+ */
+
+ el_init();
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ el_option(EL_FROTATE_SIZE, 3);
+ el_option(EL_FROTATE_NUMBER, 5);
+ mt_fok(el_option(EL_FPATH, WORKDIR"/log"));
+
+ mt_fok(el_puts("123"));
+ mt_fok(el_puts("456"));
+ mt_fok(el_puts("789"));
+
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+
+ rmdir(WORKDIR);
+ mt_ferr(el_puts(s9), ENOENT);
+ mkdir(WORKDIR, 0755);
+
+ mt_fok(system("echo -n 123 > \""WORKDIR"/log.0\""));
+ mt_fok(system("echo -n 456 > \""WORKDIR"/log.1\""));
+ mt_fok(system("echo -n 789 > \""WORKDIR"/log.2\""));
+
+ mt_fok(el_puts("qaz"));
+ mt_fok(file_check(WORKDIR"/log.0", "123"));
+ mt_fok(file_check(WORKDIR"/log.1", "456"));
+ mt_fok(file_check(WORKDIR"/log.2", "789"));
+ mt_fok(file_check(WORKDIR"/log.3", "qaz"));
+
+ unlink(WORKDIR"/log.0");
+ unlink(WORKDIR"/log.1");
+ unlink(WORKDIR"/log.2");
+ unlink(WORKDIR"/log.3");
+ unlink(WORKDIR"/log.4");
+ el_cleanup();
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
static void file_no_dir_for_logs(void)
{
- mt_ferr(el_option(EL_FNAME, "/tmp/i-dont/exist"), ENOENT);
- mt_ferr(el_puts("whatever"), EBADF);
+ mt_ferr(el_option(EL_FPATH, "/tmp/i-dont/exist"), ENOENT);
+ mt_ferr(el_puts("whatever"), ENOENT);
}
@@ -846,7 +1277,7 @@ static void file_dir_removed_after_open_then_created_back_again(void)
mt_fok(el_puts(s8));
unlink(WORKDIR"/log");
rmdir(WORKDIR);
- mt_ferr(el_puts(s3), EBADF);
+ mt_ferr(el_puts(s3), ENOENT);
mkdir(WORKDIR, 0755);
mt_fok(el_puts(s8));
}
@@ -865,14 +1296,14 @@ static void file_dir_no_access(void)
* root just doesn't give a fuck about no-write-permissions
*/
- mt_fok(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"));
+ mt_fok(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"));
mt_fok(el_puts(s3));
mt_fok(file_check("/tmp/embedlog-no-write/log", s3));
}
else
{
- mt_ferr(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"), EACCES);
- mt_ferr(el_puts(s3), EBADF);
+ mt_ferr(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"), EACCES);
+ mt_ferr(el_puts(s3), EACCES);
}
unlink("/tmp/embedlog-no-write/log");
rmdir("/tmp/embedlog-no-write");
@@ -895,14 +1326,14 @@ static void file_no_access_to_file(void)
if (getuid() == 0)
{
- mt_fok(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"));
+ mt_fok(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"));
mt_fok(el_puts(s5));
mt_fok(file_check("/tmp/embedlog-no-write/log", s5));
}
else
{
- mt_ferr(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"), EACCES);
- mt_ferr(el_puts("whatever"), EBADF);
+ mt_ferr(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"), EACCES);
+ mt_ferr(el_puts("whatever"), EACCES);
}
unlink("/tmp/embedlog-no-write/log");
rmdir("/tmp/embedlog-no-write");
@@ -916,8 +1347,8 @@ static void file_no_access_to_file(void)
static void file_rotate_no_dir_for_logs(void)
{
el_option(EL_FROTATE_NUMBER, 5);
- mt_ferr(el_option(EL_FNAME, "/tmp/i-dont/exist"), ENOENT);
- mt_ferr(el_puts("whatever"), EBADF);
+ mt_ferr(el_option(EL_FPATH, "/tmp/i-dont/exist"), ENOENT);
+ mt_ferr(el_puts("whatever"), ENOENT);
}
@@ -938,34 +1369,34 @@ static void file_rotate_dir_removed_after_open_then_created_back_again(void)
unlink(WORKDIR"/log.1");
unlink(WORKDIR"/log.2");
rmdir(WORKDIR);
- mt_ferr(el_puts(s3), EBADF);
+ mt_ferr(el_puts(s3), ENOENT);
mkdir(WORKDIR, 0755);
mt_fok(el_puts(s8));
mt_fok(el_puts(s5));
- mt_fok(file_check(WORKDIR"/log.2", s8 s5));
+ mt_fok(file_check(WORKDIR"/log.0", s8 s5));
mt_fok(el_puts(s8));
mt_fok(el_puts(s3));
mt_fok(el_puts(s5));
- mt_fok(file_check(WORKDIR"/log.2", s8 s5));
- mt_fok(file_check(WORKDIR"/log.3", s8 s3 s5));
+ mt_fok(file_check(WORKDIR"/log.0", s8 s5));
+ mt_fok(file_check(WORKDIR"/log.1", s8 s3 s5));
mt_fok(el_puts(s9));
mt_fok(el_puts(s5));
- mt_fok(file_check(WORKDIR"/log.2", s8 s5));
- mt_fok(file_check(WORKDIR"/log.3", s8 s3 s5));
- mt_fok(file_check(WORKDIR"/log.4", s9 s5));
+ mt_fok(file_check(WORKDIR"/log.0", s8 s5));
+ mt_fok(file_check(WORKDIR"/log.1", s8 s3 s5));
+ mt_fok(file_check(WORKDIR"/log.2", s9 s5));
mt_fok(el_puts(s3));
mt_fok(el_puts(s8));
- mt_fok(file_check(WORKDIR"/log.1", s8 s5));
- mt_fok(file_check(WORKDIR"/log.2", s8 s3 s5));
- mt_fok(file_check(WORKDIR"/log.3", s9 s5));
- mt_fok(file_check(WORKDIR"/log.4", s3 s8));
+ mt_fok(file_check(WORKDIR"/log.0", s8 s5));
+ mt_fok(file_check(WORKDIR"/log.1", s8 s3 s5));
+ mt_fok(file_check(WORKDIR"/log.2", s9 s5));
+ mt_fok(file_check(WORKDIR"/log.3", s3 s8));
}
@@ -980,14 +1411,14 @@ static void file_rotate_dir_no_access(void)
if (getuid() == 0)
{
- mt_fok(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"));
+ mt_fok(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"));
mt_fok(el_puts(s3));
mt_fok(file_check("/tmp/embedlog-no-write/log.0", s3));
}
else
{
- mt_ferr(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"), EACCES);
- mt_ferr(el_puts(s3), EBADF);
+ mt_ferr(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"), EACCES);
+ mt_ferr(el_puts(s3), EACCES);
}
unlink("/tmp/embedlog-no-write/log.0");
@@ -1012,14 +1443,14 @@ static void file_rotate_no_access_to_file(void)
if (getuid() == 0)
{
- mt_fok(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"));
+ mt_fok(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"));
mt_fok(el_puts(s8));
mt_fok(file_check("/tmp/embedlog-no-write/log.0", s8));
}
else
{
- mt_ferr(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"), EACCES);
- mt_ferr(el_puts("whatever"), EBADF);
+ mt_ferr(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"), EACCES);
+ mt_ferr(el_puts("whatever"), EACCES);
}
unlink("/tmp/embedlog-no-write/log.0");
@@ -1040,7 +1471,7 @@ static void file_rotate_filename_too_long(void)
memset(path, 'a', sizeof(path));
path[sizeof(path) - 1] = '\0';
el_option(EL_FROTATE_NUMBER, 5);
- mt_ferr(el_option(EL_FNAME, path), ENAMETOOLONG);
+ mt_ferr(el_option(EL_FPATH, path), ENAMETOOLONG);
}
@@ -1063,7 +1494,7 @@ static void file_rotate_path_too_long(void)
path[sizeof(path) - 2] = 'e';
path[sizeof(path) - 1] = '\0';
el_option(EL_FROTATE_NUMBER, 5);
- mt_ferr(el_option(EL_FNAME, path), ENAMETOOLONG);
+ mt_ferr(el_option(EL_FPATH, path), ENAMETOOLONG);
}
@@ -1083,7 +1514,97 @@ static void file_rotate_fail(void)
mkdir(WORKDIR, 0755);
mt_fok(el_puts(s8));
- mt_fok(file_check(WORKDIR"/log.1", s8));
+ mt_fok(file_check(WORKDIR"/log.0", s8));
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_sync_always(void)
+{
+ el_option(EL_FILE_SYNC_EVERY, 0);
+ mt_fok(el_puts(s8));
+ mt_fail(file_synced == 1);
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_sync_periodic(void)
+{
+ el_option(EL_FILE_SYNC_EVERY, 8);
+ mt_fok(el_puts(s5));
+ mt_fail(file_synced == 0);
+ mt_fok(el_puts(s3));
+ mt_fail(file_synced == 1);
+ file_synced = 0;
+
+ mt_fok(el_puts(s5));
+ mt_fail(file_synced == 0);
+ mt_fok(el_puts(s8));
+ mt_fail(file_synced == 1);
+ file_synced = 0;
+
+ mt_fok(el_puts(s5));
+ mt_fail(file_synced == 0);
+ mt_fok(el_puts(s5));
+ mt_fail(file_synced == 1);
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void file_sync_level(void)
+{
+ el_option(EL_FILE_SYNC_EVERY, 1024);
+ el_option(EL_FILE_SYNC_LEVEL, EL_ERROR);
+ el_option(EL_LEVEL, EL_DBG);
+ mt_fok(el_print(ELW, s8));
+ mt_fail(file_synced == 0);
+ mt_fok(el_print(ELW, s8));
+ mt_fail(file_synced == 0);
+ mt_fok(el_puts(s5));
+ mt_fail(file_synced == 0);
+
+ mt_fok(el_print(ELE, s1));
+ mt_fail(file_synced == 1);
+ file_synced = 0;
+
+ mt_fok(el_print(ELC, s1));
+ mt_fail(file_synced == 1);
+ file_synced = 0;
+
+ mt_fok(el_print(ELA, s1));
+ mt_fail(file_synced == 1);
+ file_synced = 0;
+
+ mt_fok(el_print(ELF, s1));
+ mt_fail(file_synced == 1);
+ file_synced = 0;
+
+ mt_fok(el_print(ELW, s1));
+ mt_fail(file_synced == 0);
+
+ mt_fok(el_print(ELN, s1));
+ mt_fail(file_synced == 0);
+
+ mt_fok(el_print(ELI, s1));
+ mt_fail(file_synced == 0);
+
+ mt_fok(el_print(ELD, s1));
+ mt_fail(file_synced == 0);
+
+ mt_fok(el_pmemory(ELI, s8, sizeof(s8)));
+ mt_fail(file_synced == 0);
+
+ mt_fok(el_pmemory(ELF, s8, sizeof(s8)));
+ mt_fail(file_synced == 1);
}
@@ -1105,6 +1626,16 @@ void el_file_test_group(void)
mt_run(file_print_without_init);
mt_run(file_print_after_cleanup);
mt_run(file_print_without_setting_file);
+ mt_run(file_write_after_failed_open);
+ mt_run(file_write_after_failed_open_to_existing_file);
+ mt_run(file_rotate_directory_deleted);
+ mt_run(file_rotate_directory_reappear_after_delete);
+ mt_run(file_rotate_write_after_failed_open);
+ mt_run(file_rotate_write_after_failed_open_to_existing_file);
+ mt_run(file_rotate_write_after_failed_open_to_existing_file_with_holes);
+ mt_run(file_rotate_write_after_failed_open_to_existing_file_with_holes2);
+ mt_run(file_rotate_write_after_failed_open_to_existing_file_with_holes3);
+ mt_run(file_rotate_and_directory_reappear);
mt_prepare_test = &test_prepare;
mt_cleanup_test = &test_cleanup;
@@ -1114,6 +1645,9 @@ void el_file_test_group(void)
mt_run(file_reopen);
mt_run(file_reopen_different_file);
mt_run(file_unexpected_third_party_delete);
+ mt_run(file_directory_deleted);
+ mt_run(file_directory_reappear_after_delete);
+ mt_run(file_and_directory_reapear);
mt_run(file_filename_too_long);
mt_run(file_path_too_long);
mt_run(file_rotate_1_no_rotate);
@@ -1154,6 +1688,9 @@ void el_file_test_group(void)
mt_run(file_rotate_filename_too_long);
mt_run(file_rotate_path_too_long);
mt_run(file_rotate_fail);
+ mt_run(file_sync_always);
+ mt_run(file_sync_periodic);
+ mt_run(file_sync_level);
rmdir(WORKDIR);
#endif
diff --git a/tst/test-el-options.c b/tst/test-el-options.c
index b964f8d..153e699 100644
--- a/tst/test-el-options.c
+++ b/tst/test-el-options.c
@@ -16,8 +16,7 @@
#include <string.h>
#include <errno.h>
-#include "embedlog.h"
-#include "el-options.h"
+#include "el-private.h"
#include "mtest.h"
#include "test-group-list.h"
#include "config.h"
@@ -93,22 +92,27 @@ static void options_init(void)
memset(&default_options, 0, sizeof(default_options));
- default_options.outputs = 0;
- default_options.level = EL_INFO;
- default_options.colors = 0;
- default_options.timestamp = EL_TS_OFF;
- default_options.timestamp_timer = EL_TS_TM_CLOCK;
- default_options.print_log_level = 1;
- default_options.custom_puts = NULL;
- default_options.serial_fd = -1;
-
- default_options.finfo = 0;
- default_options.frotate_number = 0;
- default_options.fcurrent_rotate = 0;
- default_options.frotate_size = 0;
- default_options.fpos = 0;
- default_options.file = NULL;
- default_options.fname = NULL;
+ default_options.outputs = 0;
+ default_options.level = EL_INFO;
+ default_options.file_sync_level = EL_FATAL;
+ default_options.level_current_msg = EL_DBG;
+ default_options.colors = 0;
+ default_options.timestamp = EL_TS_OFF;
+ default_options.timestamp_timer = EL_TS_TM_TIME;
+ default_options.timestamp_fractions = EL_TS_FRACT_OFF;
+ default_options.print_log_level = 1;
+ default_options.print_newline = 1;
+ default_options.custom_puts = NULL;
+ default_options.serial_fd = -1;
+
+ default_options.finfo = 0;
+ default_options.frotate_number = 0;
+ default_options.fcurrent_rotate = 0;
+ default_options.frotate_size = 0;
+ default_options.fpos = 0;
+ default_options.file = NULL;
+ default_options.file_sync_every = 32768;
+ default_options.fname = NULL;
mt_fail(el_oinit(&options) == 0);
mt_fail(memcmp(&options, &default_options, sizeof(options)) == 0);
@@ -142,10 +146,18 @@ static void options_level_set(void)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
- for (i = 0; i != 32; ++i)
+ for (i = 0; i != 16; ++i)
{
- mt_fail(el_option(EL_LEVEL, i) == 0);
- mt_fail(g_options.level == i);
+ if (i <= EL_DBG)
+ {
+ mt_fail(el_option(EL_LEVEL, i) == 0);
+ mt_fail(g_options.level == i);
+ }
+ else
+ {
+ mt_ferr(el_option(EL_LEVEL, i), EINVAL);
+ mt_fail(g_options.level == EL_DBG);
+ }
}
}
@@ -154,13 +166,36 @@ static void options_level_set(void)
========================================================================== */
-static void options_output(void)
+static void options_file_sync_level_set(void)
{
- int current_outputs;
int i;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
- current_outputs = 0;
+
+ for (i = 0; i != 16; ++i)
+ {
+ if (i <= EL_DBG)
+ {
+ mt_fail(el_option(EL_FILE_SYNC_LEVEL, i) == 0);
+ mt_fail(g_options.file_sync_level == i);
+ }
+ else
+ {
+ mt_ferr(el_option(EL_FILE_SYNC_LEVEL, i), EINVAL);
+ mt_fail(g_options.file_sync_level == EL_DBG);
+ }
+ }
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void options_output(void)
+{
+ int i;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
for (i = 0; i != EL_OUT_ALL; ++i)
{
@@ -341,6 +376,14 @@ static void options_opt_timestamp_timer(void)
}
# endif
+# if ENABLE_CLOCK == 0
+ if (i == EL_TS_TM_CLOCK)
+ {
+ mt_ferr(el_option(EL_TS_TM, i), ENODEV);
+ continue;
+ }
+# endif
+
mt_fok(el_option(EL_TS_TM, i));
mt_fail(g_options.timestamp_timer == i);
#else
@@ -356,6 +399,29 @@ static void options_opt_timestamp_timer(void)
}
+static void options_opt_timestamp_fraction(void)
+{
+ int i;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ for (i = EL_TS_FRACT_OFF; i != EL_TS_FRACT_ERROR; ++i)
+ {
+#if ENABLE_FRACTIONS && ENABLE_TIMESTAMP
+ mt_fok(el_option(EL_TS_FRACT, i));
+ mt_fail(g_options.timestamp_fractions == i);
+#else
+ mt_ferr(el_option(EL_TS_FRACT, i), ENOSYS);
+#endif
+ }
+
+#if ENABLE_FRACTIONS && ENABLE_TIMESTAMP
+ mt_ferr(el_option(EL_TS_FRACT, i), EINVAL);
+#else
+ mt_ferr(el_option(EL_TS_FRACT, i), ENOSYS);
+#endif
+}
+
/* ==========================================================================
========================================================================== */
@@ -367,10 +433,10 @@ static void options_ooption_test(void)
#if ENABLE_TIMESTAMP
- el_ooption(&opts, EL_TS_TM, EL_TS_TM_CLOCK);
- mt_fail(opts.timestamp_timer == EL_TS_TM_CLOCK);
+ el_ooption(&opts, EL_TS_TM, EL_TS_TM_TIME);
+ mt_fail(opts.timestamp_timer == EL_TS_TM_TIME);
#else
- mt_ferr(el_ooption(&opts, EL_TS_TM, EL_TS_TM_CLOCK), ENOSYS);
+ mt_ferr(el_ooption(&opts, EL_TS_TM, EL_TS_TM_TIME), ENOSYS);
#endif
}
@@ -379,6 +445,24 @@ static void options_ooption_test(void)
========================================================================== */
+static void options_prefix(void)
+{
+ el_option(EL_PREFIX, "prefix");
+#if ENABLE_PREFIX
+ mt_fok(strcmp("prefix", g_options.prefix));
+#else
+ mt_fail(g_options.prefix == NULL);
+#endif
+
+ el_option(EL_PREFIX, NULL);
+ mt_fail(g_options.prefix == NULL);
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
static void options_einval(void)
{
mt_ferr(el_option(10000, 5), EINVAL);
@@ -404,12 +488,15 @@ void el_options_test_group(void)
mt_cleanup_test = &test_cleanup;
mt_run(options_level_set);
+ mt_run(options_file_sync_level_set);
mt_run(options_output);
mt_run(options_log_allowed);
mt_run(options_opt_print_level);
mt_run(options_opt_colors);
mt_run(options_opt_timestamp);
mt_run(options_opt_timestamp_timer);
+ mt_run(options_opt_timestamp_fraction);
mt_run(options_ooption_test);
mt_run(options_einval);
+ mt_run(options_prefix);
}
diff --git a/tst/test-el-pbinary.c b/tst/test-el-pbinary.c
new file mode 100644
index 0000000..320be31
--- /dev/null
+++ b/tst/test-el-pbinary.c
@@ -0,0 +1,725 @@
+/* ==========================================================================
+ Licensed under BSD 2clause license See LICENSE file for more information
+ Author: Michał Łyszczek <michal.lyszczek@bofc.pl>
+ ========================================================================== */
+
+
+/* ==========================================================================
+ _ __ __ ____ _ __
+ (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____
+ / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/
+ / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ )
+ /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/
+
+ ========================================================================== */
+
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <libgen.h>
+#include <rb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include "mtest.h"
+#include "el-private.h"
+
+#if ENABLE_BINARY_LOGS
+
+/* ==========================================================================
+ _ __ __
+ ____ _____ (_)_ __ ____ _ / /_ ___ / /_ __ __ ____ ___ _____
+ / __ \ / ___// /| | / // __ `// __// _ \ / __// / / // __ \ / _ \ / ___/
+ / /_/ // / / / | |/ // /_/ // /_ / __/ / /_ / /_/ // /_/ // __/(__ )
+ / .___//_/ /_/ |___/ \__,_/ \__/ \___/ \__/ \__, // .___/ \___//____/
+/_/ /____//_/
+
+ ========================================================================== */
+
+
+struct log_message
+{
+ const unsigned char *msg;
+ size_t msglen;
+ int level;
+};
+
+
+/* ==========================================================================
+ _ __
+ ____ _____ (_)_ __ ____ _ / /_ ___
+ / __ \ / ___// /| | / // __ `// __// _ \
+ / /_/ // / / / | |/ // /_/ // /_ / __/
+ / .___//_/ /_/ |___/ \__,_/ \__/ \___/
+ /_/
+ _ __ __
+ _ __ ____ _ _____ (_)____ _ / /_ / /___ _____
+ | | / // __ `// ___// // __ `// __ \ / // _ \ / ___/
+ | |/ // /_/ // / / // /_/ // /_/ // // __/(__ )
+ |___/ \__,_//_/ /_/ \__,_//_.___//_/ \___//____/
+
+ ========================================================================== */
+
+
+mt_defs_ext();
+static const char *logpath = "/tmp/embedlog-pbinary-test";
+static struct rb *expected_logs; /* array of expected logs */
+static int truncate_test;
+
+static unsigned char d1[] = { 0x01 };
+static unsigned char d2[] = { 0x53, 0x10 };
+static unsigned char d3[] = { 0x00, 0x10, 0x12 };
+static unsigned char d5[] = { 'f', 'o', 'o', 0x00, 'b' };
+static unsigned char d8[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 };
+
+
+/* ==========================================================================
+ _ __
+ ____ _____ (_)_ __ ____ _ / /_ ___
+ / __ \ / ___// /| | / // __ `// __// _ \
+ / /_/ // / / / | |/ // /_/ // /_ / __/
+ / .___//_/ /_/ |___/ \__,_/ \__/ \___/
+ /_/
+ ____ __ _
+ / __/__ __ ____ _____ / /_ (_)____ ____ _____
+ / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/
+ / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ )
+ /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/
+
+ ========================================================================== */
+
+
+/* ==========================================================================
+ Checks if el_print function prints everything in well-defined format.
+ This function cross-checks logbuf buffer where el_print function prints
+ message into, and expected_logs with raw information about what should
+ be printed.
+ ========================================================================== */
+
+
+static int pbinary_check(void)
+{
+ struct log_message expected;
+ struct stat st;
+ unsigned char *msg;
+ unsigned char *msgsave;
+ int fd;
+ unsigned char tmp[1024];
+ int i;
+ int slevel;
+ size_t msglen;
+ size_t len;
+ unsigned char flags;
+ unsigned long long val;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ stat(logpath, &st);
+ fd = open(logpath, O_RDONLY);
+ msg = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ msgsave = msg;
+
+ while (rb_read(expected_logs, &expected, 1) == 1)
+ {
+ slevel = expected.level > EL_DBG ? EL_DBG : expected.level;
+
+ if (expected.level > g_options.level)
+ {
+ /*
+ * log should not have been printed due to current log level
+ * restriction. We just continue here, because if log was indeed
+ * printed, next checks will fail anyway.
+ */
+
+ continue;
+ }
+
+ /*
+ * read flags
+ */
+
+ flags = *msg++;
+
+ /*
+ * check if level is added to flags
+ */
+
+ if ((flags >> 3) != expected.level)
+ {
+ /*
+ * no, its not set correctly
+ */
+
+ goto error;
+ }
+
+ /*
+ * check printing timestamp
+ */
+
+ if (g_options.timestamp != EL_TS_OFF)
+ {
+ unsigned long long tmp;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ len = el_decode_number(msg, &tmp);
+ msg += len;
+
+ /*
+ * we don't know exact time embedlog put into log file, so we
+ * have to trust the size
+ */
+
+ if (flags & 0x01 != 0x01)
+ {
+ /*
+ * expected timestamp flag to be set
+ */
+
+ goto error;
+ }
+
+ /*
+ * fraction of seconds checks makes sense only when ts is set
+ */
+
+ if (g_options.timestamp_fractions)
+ {
+ unsigned long long usec;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ len = el_decode_number(msg, &usec);
+ msg += len;
+
+ if (usec > 999999)
+ {
+ /*
+ * we can't check exact value of microseconds, but we surely
+ * can check if this value is not too big (and thus corrupt)
+ */
+
+ goto error;
+ }
+
+ if (flags & 0x04 != 0x04)
+ {
+ /*
+ * usec not set, and should be
+ */
+
+ goto error;
+ }
+ }
+ else
+ {
+ if (flags & 0x06)
+ {
+ /*
+ * fraction flags set, and should not be
+ */
+
+ goto error;
+ }
+ }
+
+
+ }
+ else
+ {
+ if (flags & 0x01)
+ {
+ /*
+ * we didn't expect timestamp flag to be set
+ */
+
+ goto error;
+ }
+
+ if (flags & 0x06)
+ {
+ /*
+ * when timestamp is not set, we expect fraction of seconds
+ * to not be set as well
+ */
+
+ goto error;
+ }
+ }
+
+ /*
+ * now we can check printed data
+ */
+
+ if (truncate_test)
+ {
+ int i;
+ int written;
+ char expect[EL_BUF_MAX];
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ for (i = 0; i != sizeof(expect); ++i)
+ {
+ expect[i] = (unsigned char)i;
+ }
+
+ written = msg - msgsave;
+
+ len = el_decode_number(msg, &val);
+
+ if (val != EL_BUF_MAX - written - len)
+ {
+ /*
+ * truncated message has wrong length
+ */
+
+ return -1;
+ }
+
+ msg += len;
+
+ if (memcmp(msg, expect, val) != 0)
+ {
+ /*
+ * data is incorrect
+ */
+
+ return -1;
+ }
+
+ /*
+ * point to next message
+ */
+
+ msg += val;
+ }
+ else
+ {
+ len = el_decode_number(msg, &val);
+ msg += len;
+
+ if (memcmp(msg, expected.msg, val) != 0)
+ {
+ /*
+ * data different than expected
+ */
+
+ goto error;
+ }
+
+ /*
+ * finally set msg to point to next message
+ */
+
+ msg += val;
+ }
+ }
+
+ munmap(msgsave, st.st_size);
+ close(fd);
+ return 0;
+
+error:
+ munmap(msgsave, st.st_size);
+ close(fd);
+ return 1;
+}
+
+
+/* ==========================================================================
+ adds log to array of expected logs and then sends passed log message
+ into el_print
+ ========================================================================== */
+
+
+static void add_log
+(
+ enum el_level level,
+ const void *mem,
+ size_t mlen
+)
+{
+ struct log_message expected;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ expected.level = level;
+ expected.msg = mem;
+ expected.msglen = mlen;
+ rb_write(expected_logs, &expected, 1);
+ el_pbinary(level, mem, mlen);
+}
+
+
+/* ==========================================================================
+ Called before every test, initializes embedlog to known state, and
+ allocates memory for expected_logs buffer
+ ========================================================================== */
+
+
+static void test_prepare(void)
+{
+ el_init();
+ unlink(logpath);
+ el_option(EL_PRINT_LEVEL, 0);
+ el_option(EL_OUT, EL_OUT_FILE);
+ el_option(EL_PREFIX, NULL);
+ el_option(EL_COLORS, 0);
+ el_option(EL_TS, EL_TS_OFF);
+ el_option(EL_TS_FRACT, EL_TS_FRACT_OFF);
+ el_option(EL_PRINT_LEVEL, 0);
+ el_option(EL_FROTATE_NUMBER, 0);
+ el_option(EL_FILE_SYNC_EVERY, 1024);
+ el_option(EL_FPATH, logpath);
+ truncate_test = 0;
+ expected_logs = rb_new(1024, sizeof(struct log_message), 0);
+}
+
+
+/* ==========================================================================
+ Called after every test. Frees all memory allocated by test_prepare
+ ========================================================================== */
+
+
+static void test_cleanup(void)
+{
+ el_cleanup();
+ rb_destroy(expected_logs);
+ unlink(logpath);
+}
+
+
+/* ==========================================================================
+ __ __
+ / /_ ___ _____ / /_ _____
+ / __// _ \ / ___// __// ___/
+ / /_ / __/(__ )/ /_ (__ )
+ \__/ \___//____/ \__//____/
+
+ ========================================================================== */
+
+
+static void pbinary_simple_message(void)
+{
+ add_log(EL_FATAL, d5, 5);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_simple_multiple_message(void)
+{
+ add_log(EL_FATAL, d1, 1);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_ts_without_fractions(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_OFF);
+ add_log(EL_FATAL, d1, 1);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_ts_fractions_ms(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_MS);
+ add_log(EL_FATAL, d1, 1);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_ts_fractions_us(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_US);
+ add_log(EL_FATAL, d1, 1);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_ts_fractions_ns(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_NS);
+ add_log(EL_FATAL, d1, 1);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_timestamp_short(void)
+{
+ el_option(EL_TS, EL_TS_SHORT);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_timestamp_long(void)
+{
+ el_option(EL_TS, EL_TS_LONG);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_timestamp_short_no_fractions(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_OFF);
+ el_option(EL_TS, EL_TS_SHORT);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_timestamp_long_no_fractions(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_OFF);
+ el_option(EL_TS, EL_TS_LONG);
+ add_log(EL_FATAL, d2, 2);
+ add_log(EL_FATAL, d5, 5);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_different_clocks(void)
+{
+ int i;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ for (i = EL_TS_TM_CLOCK; i != EL_TS_TM_ERROR; ++i)
+ {
+ test_prepare();
+ el_option(EL_TS_TM, i);
+ el_option(EL_TS, EL_TS_LONG);
+ add_log(EL_FATAL, d8, 8);
+ add_log(EL_FATAL, d8, 8);
+ mt_fok(pbinary_check());
+ test_cleanup();
+ }
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_mix_of_everything(void)
+{
+ int level;
+ int timestamp;
+ int printlevel;
+ int finfo;
+ int colors;
+ int prefix;
+ int fract;
+ int nl;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ for (fract = EL_TS_FRACT_OFF; fract != EL_TS_FRACT_ERROR; ++fract)
+ for (level = EL_FATAL; level <= EL_DBG; ++level)
+ for (timestamp = EL_TS_OFF; timestamp != EL_TS_ERROR; ++timestamp)
+ for (printlevel = 0; printlevel <= 1; ++printlevel)
+ for (finfo = 0; finfo <= 1; ++finfo)
+ for (colors = 0; colors <= 1; ++colors)
+ for (prefix = 0; prefix <= 1; ++prefix)
+ for (nl = 0; nl <= 1; ++nl)
+ {
+ test_prepare();
+ el_option(EL_LEVEL, level);
+ el_option(EL_TS, timestamp);
+ el_option(EL_PRINT_LEVEL, printlevel);
+ el_option(EL_PRINT_NL, nl);
+ el_option(EL_FINFO, finfo);
+ el_option(EL_COLORS, colors);
+ el_option(EL_PREFIX, prefix ? "prefix" : NULL);
+ el_option(EL_TS_FRACT, fract);
+
+ add_log(EL_FATAL, d1, 1);
+ add_log(EL_ALERT, d1, 1);
+ add_log(EL_CRIT, d1, 1);
+ add_log(EL_ERROR, d5, 5);
+ add_log(EL_WARN, d1, 1);
+ add_log(EL_NOTICE, d2, 2);
+ add_log(EL_INFO, d1, 1);
+ add_log(EL_DBG, d8, 8);
+
+ /*
+ * cleanup so embedlog closes test file, which will flush data into
+ * disk so pbinary_check() can read that data
+ */
+
+ el_cleanup();
+ mt_fok(pbinary_check());
+
+ test_cleanup();
+ }
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_with_no_output_available(void)
+{
+ el_option(EL_OUT, EL_OUT_NONE);
+ mt_ferr(el_pbinary(EL_INFO, d2, 2), ENODEV);
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_level_not_high_enough(void)
+{
+ mt_ferr(el_pbinary(EL_DBG, d8, 8), ERANGE);
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_null(void)
+{
+ mt_ferr(el_pbinary(EL_ALERT, NULL, 0), EINVAL);
+ mt_ferr(el_pbinary(EL_ALERT, NULL, 5), EINVAL);
+ mt_ferr(el_pbinary(EL_ALERT, d5, 0), EINVAL);
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void pbinary_truncate(void)
+{
+ int i;
+ char msg[EL_BUF_MAX + 3];
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ for (i = 0; i != sizeof(msg); ++i)
+ {
+ msg[i] = (unsigned char)i;
+ }
+
+ el_option(EL_TS, EL_TS_LONG);
+ el_option(EL_TS_FRACT, EL_TS_FRACT_NS);
+ add_log(EL_INFO, msg, sizeof(msg));
+
+ /*
+ * tell checker that this is truncate test
+ */
+
+ truncate_test = 1;
+ mt_fok(pbinary_check());
+}
+
+#endif
+/* ==========================================================================
+ __ __
+ / /_ ___ _____ / /_ ____ _ _____ ____ __ __ ____
+ / __// _ \ / ___// __/ / __ `// ___// __ \ / / / // __ \
+ / /_ / __/(__ )/ /_ / /_/ // / / /_/ // /_/ // /_/ /
+ \__/ \___//____/ \__/ \__, //_/ \____/ \__,_// .___/
+ /____/ /_/
+ ========================================================================== */
+
+
+void el_pbinary_test_group(void)
+{
+#if ENABLE_BINARY_LOGS
+ mt_run(pbinary_different_clocks);
+ mt_run(pbinary_mix_of_everything);
+
+ mt_prepare_test = &test_prepare;
+ mt_cleanup_test = &test_cleanup;
+
+ mt_run(pbinary_simple_message);
+ mt_run(pbinary_simple_multiple_message);
+ mt_run(pbinary_ts_without_fractions);
+ mt_run(pbinary_timestamp_short);
+ mt_run(pbinary_timestamp_long);
+ mt_run(pbinary_ts_fractions_ms);
+ mt_run(pbinary_ts_fractions_us);
+ mt_run(pbinary_ts_fractions_ns);
+ mt_run(pbinary_timestamp_short_no_fractions);
+ mt_run(pbinary_timestamp_long_no_fractions);
+ mt_run(pbinary_with_no_output_available);
+ mt_run(pbinary_level_not_high_enough);
+ mt_run(pbinary_null);
+ mt_run(pbinary_truncate);
+#endif
+}
diff --git a/tst/test-el-print.c b/tst/test-el-print.c
index 6094c0f..2af6c92 100644
--- a/tst/test-el-print.c
+++ b/tst/test-el-print.c
@@ -19,13 +19,14 @@
#include <ctype.h>
#include <errno.h>
#include <libgen.h>
+#include <stdint.h>
+#include <math.h>
#include "mtest.h"
#include "stdlib.h"
#include "embedlog.h"
-#include "config-priv.h"
-#include "el-options.h"
+#include "el-private.h"
/* ==========================================================================
@@ -122,6 +123,7 @@ static int print_check(void)
int i;
int slevel;
size_t msglen;
+#if ENABLE_COLORS_EXTENDED
static const char *color[] =
{
"\e[91m", /* fatal light red */
@@ -134,6 +136,20 @@ static int print_check(void)
"\e[34m", /* debug blue */
"\e[0m" /* remove all formats */
};
+#else
+ static const char *color[] =
+ {
+ "\e[31m", /* fatal light red */
+ "\e[31m", /* alert red */
+ "\e[35m", /* critical light magenta */
+ "\e[35m", /* error magenta */
+ "\e[33m", /* warning light yellow */
+ "\e[32m", /* notice light green */
+ "\e[32m", /* information green */
+ "\e[34m", /* debug blue */
+ "\e[0m" /* remove all formats */
+ };
+#endif
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
msg = logbuf;
@@ -196,29 +212,46 @@ static int print_check(void)
IS_CHAR(':');
IS_DIGIT();
IS_DIGIT();
- IS_CHAR('.');
- for (i = 0; i != 6; ++i)
+ if (g_options.timestamp_fractions)
{
- IS_DIGIT();
+ IS_CHAR('.');
+
+ for (i = 0; i != 3 * g_options.timestamp_fractions; ++i)
+ {
+ IS_DIGIT();
+ }
}
+
IS_CHAR(']');
}
else if (g_options.timestamp == EL_TS_SHORT)
{
IS_CHAR('[');
- while (*msg != '.' )
+
+ if (g_options.timestamp_fractions)
{
- IS_DIGIT();
- }
+ while (*msg != '.')
+ {
+ IS_DIGIT();
+ }
- ++msg; /* skip the '.' character */
+ ++msg; /* skip the '.' character */
- for (i = 0; i != 6; ++i)
+ for (i = 0; i != 3 * g_options.timestamp_fractions; ++i)
+ {
+ IS_DIGIT();
+ }
+ }
+ else
{
- IS_DIGIT();
+ while (*msg != ']')
+ {
+ IS_DIGIT();
+ }
}
+
IS_CHAR(']');
}
else if (g_options.timestamp == EL_TS_OFF)
@@ -315,8 +348,8 @@ static int print_check(void)
g_options.timestamp != EL_TS_OFF)
{
/*
- * file info or timestamp information is enabled, in that case
- * we check for additional space between info and log message
+ * prefix or timestamp information is enabled, in that case
+ * we check for additional space between info and log message
*/
if (*msg++ != ' ')
@@ -342,6 +375,27 @@ static int print_check(void)
}
}
+ /*
+ * check for prefix
+ */
+
+ if (g_options.prefix)
+ {
+ char expected_prefix[EL_PREFIX_LEN + 1] = {0};
+ size_t expected_prefix_len;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ strncat(expected_prefix, g_options.prefix, EL_PREFIX_LEN);
+ expected_prefix_len = strlen(expected_prefix);
+
+ if (strncmp(expected_prefix, msg, expected_prefix_len) != 0)
+ {
+ return -1;
+ }
+
+ msg += expected_prefix_len;
+ }
+
msglen = strlen(expected.msg);
if (strncmp(msg, expected.msg, msglen) != 0)
@@ -369,20 +423,25 @@ static int print_check(void)
msg += 4;
}
- if (*msg != '\n')
+ if (g_options.print_newline)
{
+ if (*msg != '\n')
+ {
+ /*
+ * when new line is enabled, log should end with new line
+ */
+
+ return -1;
+ }
+
/*
- * all logs should be ended with new line
+ * set msg to point to next message, there is no need to move
+ * msg pointer if newline is not printed as msg already points
+ * to next message
*/
- return -1;
+ ++msg;
}
-
- /*
- * set msg to point to next message
- */
-
- ++msg;
}
return 0;
@@ -469,6 +528,19 @@ static void print_simple_message(void)
========================================================================== */
+static void print_simple_message_no_newline(void)
+{
+ el_option(EL_PRINT_NL, 0);
+ add_log(ELF, "print simple no newline");
+ add_log(ELF, "print simple no newline second");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
static void print_simple_multiple_message(void)
{
add_log(ELF, "print_simple_multiple_message first");
@@ -505,7 +577,7 @@ static void print_log_level(void)
static void print_colorful_output(void)
{
el_option(EL_COLORS, 1);
- el_option(EL_LEVEL, EL_DBG + 2);
+ el_option(EL_LEVEL, EL_DBG);
add_log(ELF, "print_colorful_output fatal message");
add_log(ELA, "print_colorful_output alert message");
add_log(ELC, "print_colorful_output critical message");
@@ -527,7 +599,7 @@ static void print_colorful_output(void)
static void print_custom_log_level(void)
{
el_option(EL_PRINT_LEVEL, 1);
- el_option(EL_LEVEL, EL_DBG + 5);
+ el_option(EL_LEVEL, EL_DBG);
add_log(ELD + 4, "print_custom_log_level custom debug 4");
add_log(ELD + 5, "print_custom_log_level custom debug 5");
add_log(ELD + 6, "print_custom_log_level custom debug 6");
@@ -565,6 +637,118 @@ static void print_timestamp_long(void)
========================================================================== */
+static void print_timestamp_short_no_fractions(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_OFF);
+ el_option(EL_TS, EL_TS_SHORT);
+ add_log(ELF, "first meaningless message");
+ add_log(ELF, "second stupid log");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_timestamp_long_no_fractions(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_OFF);
+ el_option(EL_TS, EL_TS_LONG);
+ add_log(ELF, "they don't even care");
+ add_log(ELF, "what I put in here");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_timestamp_short_fractions_ms(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_MS);
+ el_option(EL_TS, EL_TS_SHORT);
+ add_log(ELF, "first meaningless message");
+ add_log(ELF, "second stupid log");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_timestamp_long_fractions_ms(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_MS);
+ el_option(EL_TS, EL_TS_LONG);
+ add_log(ELF, "they don't even care");
+ add_log(ELF, "what I put in here");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_timestamp_short_fractions_us(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_US);
+ el_option(EL_TS, EL_TS_SHORT);
+ add_log(ELF, "first meaningless message");
+ add_log(ELF, "second stupid log");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_timestamp_long_fractions_us(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_US);
+ el_option(EL_TS, EL_TS_LONG);
+ add_log(ELF, "they don't even care");
+ add_log(ELF, "what I put in here");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_timestamp_short_fractions_ns(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_NS);
+ el_option(EL_TS, EL_TS_SHORT);
+ add_log(ELF, "first meaningless message");
+ add_log(ELF, "second stupid log");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_timestamp_long_fractions_ns(void)
+{
+ el_option(EL_TS_FRACT, EL_TS_FRACT_NS);
+ el_option(EL_TS, EL_TS_LONG);
+ add_log(ELF, "they don't even care");
+ add_log(ELF, "what I put in here");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
static void print_finfo(void)
{
el_option(EL_FINFO, 1);
@@ -607,21 +791,29 @@ static void print_mix_of_everything(void)
int printlevel;
int finfo;
int colors;
+ int prefix;
+ int fract;
+ int nl;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
- for (level = EL_FATAL; level <= EL_DBG; ++level)
+ for (fract = EL_TS_FRACT_OFF; fract != EL_TS_FRACT_ERROR; ++fract)
for (timestamp = EL_TS_OFF; timestamp != EL_TS_ERROR; ++timestamp)
+ for (level = EL_FATAL; level <= EL_DBG; ++level)
for (printlevel = 0; printlevel <= 1; ++printlevel)
- for (finfo = 0; finfo <= 1; ++finfo)
for (colors = 0; colors <= 1; ++colors)
+ for (prefix = 0; prefix <= 1; ++prefix)
+ for (finfo = 0; finfo <= 1; ++finfo)
+ for (nl = 0; nl <= 1; ++nl)
{
test_prepare();
el_option(EL_LEVEL, level);
el_option(EL_TS, timestamp);
el_option(EL_PRINT_LEVEL, printlevel);
+ el_option(EL_PRINT_NL, nl);
el_option(EL_FINFO, finfo);
el_option(EL_COLORS, colors);
+ el_option(EL_PREFIX, prefix ? "prefix" : NULL);
add_log(ELF, "fatal message");
add_log(ELA, "alert message");
@@ -631,6 +823,7 @@ static void print_mix_of_everything(void)
add_log(ELN, "notice message");
add_log(ELI, "info message");
add_log(ELD, "debug message");
+
mt_fok(print_check());
test_cleanup();
@@ -653,7 +846,41 @@ static void print_too_long_print_truncate(void)
msg[sizeof(msg) - 2] = '3';
msg[sizeof(msg) - 3] = '2';
msg[sizeof(msg) - 4] = '1';
- msg[sizeof(msg) - 4] = '0';
+ msg[sizeof(msg) - 5] = '0';
+
+ add_log(ELI, "not truncated");
+ add_log(ELI, msg);
+
+ /*
+ * while el_print will make copy of msg, our test print_check function
+ * will just use pointer to our msg here, and since we expect message to
+ * be truncated, we truncate it here and print_check will take this
+ * truncated message as expected one.
+ */
+
+ msg[sizeof(msg) - 3] = '\0';
+
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_too_long_print_truncate_no_newline(void)
+{
+ char msg[EL_LOG_MAX + 3];
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ el_option(EL_PRINT_NL, 0);
+ memset(msg, 'a', sizeof(msg));
+ msg[sizeof(msg) - 1] = '\0';
+ msg[sizeof(msg) - 2] = '3';
+ msg[sizeof(msg) - 3] = '2';
+ msg[sizeof(msg) - 4] = '1';
+ msg[sizeof(msg) - 5] = '0';
add_log(ELI, "not truncated");
add_log(ELI, msg);
@@ -704,14 +931,26 @@ static void print_truncate_with_date(void)
static void print_truncate_with_all_options(void)
{
- char msg[EL_LOG_MAX + 3];
+ char msg[EL_LOG_MAX + 3];
+ char finfo[EL_FLEN_MAX + 1];
+ char prefix[EL_PREFIX_MAX + 1];
+ size_t fline;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
el_option(EL_TS, EL_TS_LONG);
+ el_option(EL_TS_FRACT, EL_TS_FRACT_NS);
+ el_option(EL_TS_TM, EL_TS_TM_REALTIME);
el_option(EL_FINFO, 1);
+ el_option(EL_COLORS, 1);
+ el_option(EL_PREFIX, prefix);
el_option(EL_PRINT_LEVEL, 1);
memset(msg, 'a', sizeof(msg));
+ memset(finfo, 'b', sizeof(finfo));
+ memset(prefix, 'c', sizeof(prefix));
+ finfo[sizeof(finfo) - 1] = '\0';
+ prefix[sizeof(prefix) - 1] = '\0';
+ fline = (size_t)pow(10, EL_PRE_FINFO_LINE_MAX_LEN) - 1;
msg[sizeof(msg) - 1] = '\0';
msg[sizeof(msg) - 2] = '3';
msg[sizeof(msg) - 3] = '2';
@@ -719,7 +958,7 @@ static void print_truncate_with_all_options(void)
msg[sizeof(msg) - 4] = '0';
add_log(ELI, "not truncated");
- add_log(ELI, msg);
+ add_log(finfo, fline, EL_FATAL, msg);
msg[sizeof(msg) - 3] = '\0';
@@ -781,6 +1020,59 @@ static void print_null(void)
mt_ferr(el_print(ELA, NULL), EINVAL);
}
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_prefix(void)
+{
+ el_option(EL_PREFIX, "prefix");
+ add_log(ELI, "message with prefix");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_prefix_full(void)
+{
+ char p[EL_PREFIX_LEN + 1] = {0};
+ int i;
+
+ for (i = 0; i != EL_PREFIX_LEN; ++i)
+ {
+ p[i] = '0' + (i % 32);
+ }
+
+ el_option(EL_PREFIX, p);
+ add_log(ELI, "message with fill prefix");
+ mt_fok(print_check());
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
+static void print_prefix_overflow(void)
+{
+ char p[EL_PREFIX_LEN + 10] = {0};
+ int i;
+
+ for (i = 0; i != sizeof(p) - 1; ++i)
+ {
+ p[i] = '0' + (i % 32);
+ }
+
+ el_option(EL_PREFIX, p);
+ add_log(ELI, "message with overflown prefix");
+ mt_fok(print_check());
+}
+
+
/* ==========================================================================
__ __
/ /_ ___ _____ / /_ ____ _ _____ ____ __ __ ____
@@ -800,14 +1092,24 @@ void el_print_test_group(void)
mt_cleanup_test = &test_cleanup;
mt_run(print_simple_message);
+ mt_run(print_simple_message_no_newline);
mt_run(print_simple_multiple_message);
mt_run(print_log_level);
mt_run(print_colorful_output);
mt_run(print_custom_log_level);
mt_run(print_timestamp_short);
mt_run(print_timestamp_long);
+ mt_run(print_timestamp_short_no_fractions);
+ mt_run(print_timestamp_long_no_fractions);
+ mt_run(print_timestamp_short_fractions_ms);
+ mt_run(print_timestamp_long_fractions_ms);
+ mt_run(print_timestamp_short_fractions_us);
+ mt_run(print_timestamp_long_fractions_us);
+ mt_run(print_timestamp_short_fractions_ns);
+ mt_run(print_timestamp_long_fractions_ns);
mt_run(print_finfo);
mt_run(print_too_long_print_truncate);
+ mt_run(print_too_long_print_truncate_no_newline);
mt_run(print_truncate_with_date);
mt_run(print_truncate_with_all_options);
mt_run(print_with_no_output_available);
@@ -815,4 +1117,7 @@ void el_print_test_group(void)
mt_run(print_finfo_path);
mt_run(print_nofinfo);
mt_run(print_null);
+ mt_run(print_prefix);
+ mt_run(print_prefix_full);
+ mt_run(print_prefix_overflow);
}
diff --git a/tst/test-group-list.h b/tst/test-group-list.h
index 4429a42..cd478cf 100644
--- a/tst/test-group-list.h
+++ b/tst/test-group-list.h
@@ -8,6 +8,7 @@
void el_options_test_group(void);
void el_print_test_group(void);
+void el_pbinary_test_group(void);
void el_file_test_group(void);
void el_perror_test_group(void);
void el_pmemory_test_group(void);