Allow single log file with fixed name rather than starting a new one each day.

This commit is contained in:
WB2OSZ 2017-06-17 19:39:59 -04:00
parent 647d698656
commit 52927ce54a
8 changed files with 241 additions and 95 deletions

View File

@ -9,10 +9,11 @@ This is a snapshot of ongoing development towards version of 1.5. Some features
- Time slots for beaconing.
- V20 configuration item for listing stations known not to understaand AX.25 v2.2. This will speed up connection by going right to SABM and not trying SABME first and failing.
- Documentation updates describing cheap SDR frequency inaccuracy and how to compensate for it.
- Allow single log file with fixed name rather than starting a new one each day.
### Bugs Fixed: ###
- PACLEN configuration item no longer restricts length of received frames.
@ -31,7 +32,7 @@ This is a snapshot of ongoing development towards version of 1.5. Some features
- decode_aprs utility can now accept KISS frames and AX.25 frames as series of two digit hexadecimal numbers.
- New configuration option, V20, for listing stations known to not understand AX.25 v2.2.
- New configuration option, V20, for listing stations known to not understand AX.25 v2.2. This will speed up connection by going right to SABM and not trying SABME first and failing.
### Bugs Fixed: ###

View File

@ -877,7 +877,8 @@ void config_init (char *fname, struct audio_s *p_audio_config,
strlcpy (p_misc_config->gpsnmea_port, "", sizeof(p_misc_config->gpsnmea_port));
strlcpy (p_misc_config->waypoint_port, "", sizeof(p_misc_config->waypoint_port));
strlcpy (p_misc_config->logdir, "", sizeof(p_misc_config->logdir));
p_misc_config->log_daily_names = 0;
strlcpy (p_misc_config->log_path, "", sizeof(p_misc_config->log_path));
/* connected mode. */
@ -4283,7 +4284,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
}
/*
* LOGDIR - Directory name for storing log files. Use "." for current working directory.
* LOGDIR - Directory name for automatically named daily log files. Use "." for current working directory.
*/
else if (strcasecmp(t, "logdir") == 0) {
t = split(NULL,0);
@ -4293,7 +4294,12 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue;
}
else {
strlcpy (p_misc_config->logdir, t, sizeof(p_misc_config->logdir));
if (strlen(p_misc_config->log_path) > 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: LOGDIR on line %d is replacing an earlier LOGDIR or LOGFILE.\n", line);
}
p_misc_config->log_daily_names = 1;
strlcpy (p_misc_config->log_path, t, sizeof(p_misc_config->log_path));
}
t = split(NULL,0);
if (t != NULL) {
@ -4302,6 +4308,31 @@ void config_init (char *fname, struct audio_s *p_audio_config,
}
}
/*
* LOGFILE - Log file name, including any directory part.
*/
else if (strcasecmp(t, "logfile") == 0) {
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: Missing file name for LOGFILE on line %d.\n", line);
continue;
}
else {
if (strlen(p_misc_config->log_path) > 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: LOGFILE on line %d is replacing an earlier LOGDIR or LOGFILE.\n", line);
}
p_misc_config->log_daily_names = 0;
strlcpy (p_misc_config->log_path, t, sizeof(p_misc_config->log_path));
}
t = split(NULL,0);
if (t != NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: LOGFILE on line %d should have file name and nothing more.\n", line);
}
}
/*
* BEACON channel delay every message
*

View File

@ -77,7 +77,9 @@ struct misc_config_s {
#define WPT_FORMAT_KENWOOD 0x08 /* K $PKWDWPL */
char logdir[80]; /* Directory for saving activity logs. */
int log_daily_names; /* True to generate new log file each day. */
char log_path[80]; /* Either directory or full file name depending on above. */
int sb_configured; /* TRUE if SmartBeaconing is configured. */
int sb_fast_speed; /* MPH */

View File

@ -194,7 +194,8 @@ int main (int argc, char *argv[])
struct igate_config_s igate_config;
int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0; /* Command line options. */
char P_opt[16];
char l_opt[80];
char l_opt_logdir[80];
char L_opt_logfile[80];
char input_file[80];
// char timestamp[16];
@ -215,7 +216,8 @@ int main (int argc, char *argv[])
int E_tx_opt = 0; /* "-E n" Error rate % for clobbering trasmit frames. */
int E_rx_opt = 0; /* "-E Rn" Error rate % for clobbering receive frames. */
strlcpy(l_opt, "", sizeof(l_opt));
strlcpy(l_opt_logdir, "", sizeof(l_opt_logdir));
strlcpy(L_opt_logfile, "", sizeof(L_opt_logfile));
strlcpy(P_opt, "", sizeof(P_opt));
#if __WIN32__
@ -356,7 +358,7 @@ int main (int argc, char *argv[])
/* ':' following option character means arg is required. */
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:Sa:E:",
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:L:Sa:E:",
long_options, &option_index);
if (c == -1)
break;
@ -532,11 +534,17 @@ int main (int argc, char *argv[])
exit (0);
break;
case 'l': /* -l for log file directory name */
case 'l': /* -l for log directory with daily files */
strlcpy (l_opt, optarg, sizeof(l_opt));
strlcpy (l_opt_logdir, optarg, sizeof(l_opt_logdir));
break;
case 'L': /* -L for log file name with full path */
strlcpy (L_opt_logfile, optarg, sizeof(L_opt_logfile));
break;
case 'S': /* Print symbol tables and exit. */
symbols_init ();
@ -674,8 +682,19 @@ int main (int argc, char *argv[])
audio_config.recv_error_rate = E_rx_opt;
if (strlen(l_opt) > 0) {
strlcpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir));
if (strlen(l_opt_logdir) > 0 && strlen(L_opt_logfile) > 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Logging options -l and -L can't be used together. Pick one or the other.\n");
exit(1);
}
if (strlen(L_opt_logfile) > 0) {
misc_config.log_daily_names = 0;
strlcpy (misc_config.log_path, L_opt_logfile, sizeof(misc_config.log_path));
}
else if (strlen(l_opt_logdir) > 0) {
misc_config.log_daily_names = 1;
strlcpy (misc_config.log_path, l_opt_logdir, sizeof(misc_config.log_path));
}
misc_config.enable_kiss_pt = enable_pseudo_terminal;
@ -794,7 +813,7 @@ int main (int argc, char *argv[])
* log the tracker beacon transmissions with fake channel 999.
*/
log_init(misc_config.logdir);
log_init(misc_config.log_daily_names, misc_config.log_path);
mheard_init (d_m_opt);
beacon_init (&audio_config, &misc_config, &igate_config);

Binary file not shown.

243
log.c
View File

@ -28,6 +28,13 @@
* unreadable, format, write separated properties into
* CSV format for easy reading and later processing.
*
* There are two alternatives here.
*
* -L logfile Specify full file path.
*
* -l logdir Daily names will be created here.
*
* Use one or the other but not both.
*
*------------------------------------------------------------------*/
@ -91,26 +98,39 @@ static void quote_for_csv (char *out, size_t outsize, const char *in) {
*
* Purpose: Initialization at start of application.
*
* Inputs: path - Path of log file directory.
* Inputs: daily_names - True if daily names should be generated.
* In this case path is a directory.
* When false, path would be the file name.
*
* path - Log file name or just directory.
* Use "." for current directory.
* Empty string disables feature.
*
* Global Out: g_log_dir - Save directory here for later use.
* Global Out: g_daily_names - True if daily names should be generated.
*
* g_log_path - Save directory or full name here for later use.
*
* g_log_fp - File pointer for writing.
* Note that file is kept open.
* We don't open/close for every new item.
*
* g_open_fname - Name of currently open file.
* Applicable only when g_daily_names is true.
*
*------------------------------------------------------------------*/
static char g_log_dir[80];
static int g_daily_names;
static char g_log_path[80];
static FILE *g_log_fp;
static char g_open_fname[20];
void log_init (char *path)
void log_init (int daily_names, char *path)
{
struct stat st;
strlcpy (g_log_dir, "", sizeof(g_log_dir));
g_daily_names = daily_names;
strlcpy (g_log_path, "", sizeof(g_log_path));
g_log_fp = NULL;
strlcpy (g_open_fname, "", sizeof(g_open_fname));
@ -118,42 +138,57 @@ void log_init (char *path)
return;
}
if (stat(path,&st) == 0) {
// Exists, but is it a directory?
if (S_ISDIR(st.st_mode)) {
// Specified directory exists.
strlcpy (g_log_dir, path, sizeof(g_log_dir));
if (g_daily_names) {
// Original strategy. Automatic daily file names.
if (stat(path,&st) == 0) {
// Exists, but is it a directory?
if (S_ISDIR(st.st_mode)) {
// Specified directory exists.
strlcpy (g_log_path, path, sizeof(g_log_path));
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Log file location \"%s\" is not a directory.\n", path);
dw_printf ("Using current working directory \".\" instead.\n");
strlcpy (g_log_path, ".", sizeof(g_log_path));
}
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Log file location \"%s\" is not a directory.\n", path);
dw_printf ("Using current working directory \".\" instead.\n");
strlcpy (g_log_dir, ".", sizeof(g_log_dir));
// Doesn't exist. Try to create it.
// parent directory must exist.
// We don't create multiple levels like "mkdir -p"
#if __WIN32__
if (_mkdir (path) == 0) {
#else
if (mkdir (path, 0777) == 0) {
#endif
// Success.
text_color_set(DW_COLOR_INFO);
dw_printf ("Log file location \"%s\" has been created.\n", path);
strlcpy (g_log_path, path, sizeof(g_log_path));
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Failed to create log file location \"%s\".\n", path);
dw_printf ("%s\n", strerror(errno));
dw_printf ("Using current working directory \".\" instead.\n");
strlcpy (g_log_path, ".", sizeof(g_log_path));
}
}
}
else {
// Doesn't exist. Try to create it.
// parent directory must exist.
// We don't create multiple levels like "mkdir -p"
#if __WIN32__
if (_mkdir (path) == 0) {
#else
if (mkdir (path, 0777) == 0) {
#endif
// Success.
text_color_set(DW_COLOR_INFO);
dw_printf ("Log file location \"%s\" has been created.\n", path);
strlcpy (g_log_dir, path, sizeof(g_log_dir));
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Failed to create log file location \"%s\".\n", path);
dw_printf ("%s\n", strerror(errno));
dw_printf ("Using current working directory \".\" instead.\n");
strlcpy (g_log_dir, ".", sizeof(g_log_dir));
}
// Added in version 1.5. Single file.
// Typically logrotate would be used to keep size under control.
text_color_set(DW_COLOR_INFO);
dw_printf ("Log file is \"%s\"\n", path);
strlcpy (g_log_path, path, sizeof(g_log_path));
}
}
} /* end log_init */
@ -177,71 +212,119 @@ void log_init (char *path)
void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries)
{
time_t now; // make 'now' a parameter so we can process historical data ???
char fname[20];
time_t now;
struct tm tm;
if (strlen(g_log_dir) == 0) return;
if (strlen(g_log_path) == 0) return;
// Generate the file name from current date, UTC.
now = time(NULL);
now = time(NULL); // Get current time.
(void)gmtime_r (&now, &tm);
// Microsoft doesn't recognize %F as equivalent to %Y-%m-%d
strftime (fname, sizeof(fname), "%Y-%m-%d.log", &tm);
if (g_daily_names) {
// Close current file if name has changed
// Original strategy. Automatic daily file names.
if (g_log_fp != NULL && strcmp(fname, g_open_fname) != 0) {
log_term ();
}
char fname[20];
// Open for append if not already open.
// Generate the file name from current date, UTC.
// Why UTC rather than local time? I don't recall the reasoning.
// It's been there a few years and no on complained so leave it alone for now.
if (g_log_fp == NULL) {
char full_path[120];
struct stat st;
int already_there;
// Microsoft doesn't recognize %F as equivalent to %Y-%m-%d
strlcpy (full_path, g_log_dir, sizeof(full_path));
strftime (fname, sizeof(fname), "%Y-%m-%d.log", &tm);
// Close current file if name has changed
if (g_log_fp != NULL && strcmp(fname, g_open_fname) != 0) {
log_term ();
}
// Open for append if not already open.
if (g_log_fp == NULL) {
char full_path[120];
struct stat st;
int already_there;
strlcpy (full_path, g_log_path, sizeof(full_path));
#if __WIN32__
strlcat (full_path, "\\", sizeof(full_path));
strlcat (full_path, "\\", sizeof(full_path));
#else
strlcat (full_path, "/", sizeof(full_path));
strlcat (full_path, "/", sizeof(full_path));
#endif
strlcat (full_path, fname, sizeof(full_path));
strlcat (full_path, fname, sizeof(full_path));
// See if it already exists.
// This is used later to write a header if it did not exist already.
// See if file already exists and not empty.
// This is used later to write a header if it did not exist already.
already_there = stat(full_path,&st) == 0;
already_there = (stat(full_path,&st) == 0) && (st.st_size > 0);
text_color_set(DW_COLOR_INFO);
dw_printf("Opening log file \"%s\".\n", fname);
text_color_set(DW_COLOR_INFO);
dw_printf("Opening log file \"%s\".\n", fname);
g_log_fp = fopen (full_path, "a");
g_log_fp = fopen (full_path, "a");
if (g_log_fp != NULL) {
strlcpy (g_open_fname, fname, sizeof(g_open_fname));
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf("Can't open log file \"%s\" for write.\n", full_path);
dw_printf ("%s\n", strerror(errno));
strlcpy (g_open_fname, "", sizeof(g_open_fname));
return;
}
if (g_log_fp != NULL) {
strlcpy (g_open_fname, fname, sizeof(g_open_fname));
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf("Can't open log file \"%s\" for write.\n", full_path);
dw_printf ("%s\n", strerror(errno));
strlcpy (g_open_fname, "", sizeof(g_open_fname));
return;
}
// Write a header suitable for importing into a spreadsheet
// only if this will be the first line.
// Write a header suitable for importing into a spreadsheet
// only if this will be the first line.
if ( ! already_there) {
fprintf (g_log_fp, "chan,utime,isotime,source,heard,level,error,dti,name,symbol,latitude,longitude,speed,course,altitude,frequency,offset,tone,system,status,telemetry,comment\n");
if ( ! already_there) {
fprintf (g_log_fp, "chan,utime,isotime,source,heard,level,error,dti,name,symbol,latitude,longitude,speed,course,altitude,frequency,offset,tone,system,status,telemetry,comment\n");
}
}
}
else {
// Added in version 1.5. Single file.
// Open for append if not already open.
if (g_log_fp == NULL) {
struct stat st;
int already_there;
// See if file already exists and not empty.
// This is used later to write a header if it did not exist already.
already_there = (stat(g_log_path,&st) == 0) && (st.st_size > 0);
text_color_set(DW_COLOR_INFO);
dw_printf("Opening log file \"%s\"\n", g_log_path);
g_log_fp = fopen (g_log_path, "a");
if (g_log_fp == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Can't open log file \"%s\" for write.\n", g_log_path);
dw_printf ("%s\n", strerror(errno));
strlcpy (g_log_path, "", sizeof(g_log_path));
return;
}
// Write a header suitable for importing into a spreadsheet
// only if this will be the first line.
if ( ! already_there) {
fprintf (g_log_fp, "chan,utime,isotime,source,heard,level,error,dti,name,symbol,latitude,longitude,speed,course,altitude,frequency,offset,tone,system,status,telemetry,comment\n");
}
}
}
// Add line to file if it is now open.
if (g_log_fp != NULL) {
@ -439,7 +522,13 @@ void log_term (void)
if (g_log_fp != NULL) {
text_color_set(DW_COLOR_INFO);
dw_printf("Closing log file \"%s\".\n", g_open_fname);
if (g_daily_names) {
dw_printf("Closing log file \"%s\".\n", g_open_fname);
}
else {
dw_printf("Closing log file \"%s\".\n", g_log_path);
}
fclose (g_log_fp);

2
log.h
View File

@ -10,7 +10,7 @@
void log_init (char *path);
void log_init (int daily_names, char *path);
void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries);

View File

@ -27,8 +27,12 @@ RMS Express, and many others.
Read configuration file from specified location rather than the default locations.
.TP
.BI "-l " "dir"
Generate log files in specified directory. Use "." for current directory.
.BI "-l " "logdir"
Generate daily log files in specified directory. Use "." for current directory.
.TP
.BI "-L " "logfile"
Generate single log file with fixed name.
.TP
.BI "-r " "n"