libmsngr  1.1
 All Data Structures Files Functions Variables Enumerations Enumerator Macros Groups
msngr_log.c
Go to the documentation of this file.
1 /*******************************************************************************
2 *
3 * COPYRIGHT (C) 2010 Battelle Memorial Institute. All Rights Reserved.
4 *
5 ********************************************************************************
6 *
7 * Author:
8 * name: Brian Ermold
9 * phone: (509) 375-2277
10 * email: brian.ermold@pnl.gov
11 *
12 ********************************************************************************
13 *
14 * REPOSITORY INFORMATION:
15 * $Revision: 13168 $
16 * $Author: ermold $
17 * $Date: 2012-03-24 01:49:45 +0000 (Sat, 24 Mar 2012) $
18 *
19 ********************************************************************************
20 *
21 * NOTE: DOXYGEN is used to generate documentation for this file.
22 *
23 *******************************************************************************/
24 
25 /** @file msngr_log.c
26  * Log File Functions.
27  */
28 
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <unistd.h>
33 
34 #include "messenger.h"
35 
36 /**
37  * @defgroup LOG_FILES Log Files
38  */
39 /*@{*/
40 
41 /*******************************************************************************
42  * Private Functions
43  */
44 /** @privatesection */
45 
46 /**
47  * PRIVATE: Free all memory used by a LogFile.
48  *
49  * This function will also close the log file if log->fp
50  * is not NULL, and it is not stdout or stderr.
51  *
52  * @param log - pointer to the LogFile
53  */
54 static void _log_free(LogFile *log)
55 {
56  if (log) {
57 
58  if (log->fp && (log->fp != stdout) && (log->fp != stderr)) {
59  fclose(log->fp);
60  }
61 
62  if (log->path) free(log->path);
63  if (log->name) free(log->name);
64  if (log->full_path) free(log->full_path);
65  if (log->errstr) free(log->errstr);
66 
67  free(log);
68  }
69 }
70 
71 /*******************************************************************************
72  * Public Functions
73  */
74 /** @publicsection */
75 
76 /**
77  * Close a LogFile.
78  *
79  * This function will close a log file and free all memory
80  * used by the LogFile structure.
81  *
82  * If the pointer to the LogFile is NULL this function will
83  * do nothing and return successfully.
84  *
85  * If the the log file is not open (log->fp == NULL),
86  * all messeges will be printed to stdout.
87  *
88  * If the LOG_STATS flag is set, the process stats will be
89  * printed to the log file before it is closed.
90  *
91  * If the LOG_TAGS flag is set, the run time and the
92  * "**** CLOSED: YYYY-MM-DD hh:mm:ss" lines will be printd
93  * to the log file before it is closed.
94  *
95  * The space pointed to by errstr should be large enough to
96  * hold MAX_LOG_ERROR bytes. Any less than that and the error
97  * message could be truncated.
98  *
99  * If errlen is 0 then no error message is written to errstr
100  * and errstr can be NULL.
101  *
102  * @param log - pointer to the LogFile
103  * @param errlen - length of the error message buffer
104  * @param errstr - output: error message
105  *
106  * @return
107  * - 1 if successful
108  * - 0 if an error occurred
109  */
111  LogFile *log,
112  size_t errlen,
113  char *errstr)
114 {
115  FILE *log_fp;
116  ProcStats *proc_stats;
117  time_t close_time;
118  time_t run_time;
119  char time_string[32];
120  int nbytes;
121  int log_errno;
122  char *full_path;
123 
124  if (!log) {
125  return(1);
126  }
127 
128  log_fp = (log->fp) ? log->fp : stdout;
129  log_errno = 0;
130  run_time = 0;
131 
132  /* Log process stats */
133 
134  if (log->flags & LOG_STATS) {
135 
136  proc_stats = procstats_get();
137 
138  if (proc_stats->errstr[0] != '\0') {
139 
140  nbytes = fprintf(log_fp,
141  "\n%s", proc_stats->errstr);
142 
143  if (nbytes < 0) {
144  log_errno = errno;
145  }
146  }
147 
148  nbytes = fprintf(log_fp,
149  "\n"
150  "Executable File Name: %s\n"
151  "Process Image Size: %u Kbytes\n"
152  "Resident Set Size: %u Kbytes\n"
153  "Total CPU Time: %g seconds\n",
154  proc_stats->exe_name,
155  proc_stats->image_size,
156  proc_stats->rss_size,
157  proc_stats->cpu_time);
158 
159  if (nbytes < 0) {
160  log_errno = errno;
161  }
162 
163  if (proc_stats->total_rw_io > 0) {
164 
165  nbytes = fprintf(log_fp,
166  "Total read/write IO: %lu bytes\n",
167  proc_stats->total_rw_io);
168 
169  if (nbytes < 0) {
170  log_errno = errno;
171  }
172  }
173 
174  if (proc_stats->run_time > 0) {
175  run_time = (time_t)(proc_stats->run_time + 0.5);
176  }
177  }
178 
179  /* Log run time and closed tag */
180 
181  if (log->flags & LOG_TAGS) {
182 
183  close_time = time(NULL);
184 
185  if (!run_time && log->open_time) {
186  run_time = close_time - log->open_time;
187  }
188 
189  nbytes = fprintf(log_fp,
190  "\nRun time: %d seconds\n"
191  "**** CLOSED: %s\n",
192  (int)run_time,
193  msngr_format_time(close_time, time_string));
194 
195  if (nbytes < 0) {
196  log_errno = errno;
197  }
198  }
199 
200  /* Close the log file */
201 
202  if (log_fp != stdout &&
203  log_fp != stderr) {
204 
205  if (log->flags & LOG_LOCKF) {
206  lockf(fileno(log_fp), F_ULOCK, 0);
207  }
208 
209  if (fclose(log_fp) != 0) {
210  log_errno = errno;
211  }
212  }
213 
214  /* Check for errors */
215 
216  if (log_errno) {
217 
218  if (log->full_path) {
219  full_path = log->full_path;
220  }
221  else if (log_fp == stdout) {
222  full_path = "stdout";
223  }
224  else if (log_fp == stderr) {
225  full_path = "stderr";
226  }
227  else {
228  full_path = "unknown";
229  }
230 
231  snprintf(errstr, errlen,
232  "Could not write to log file: %s\n"
233  " -> %s\n",
234  full_path, strerror(log_errno));
235  }
236 
237  /* Free the LogFile structure */
238 
239  log->fp = (FILE *)NULL;
240 
241  _log_free(log);
242 
243  return((log_errno) ? 0 : 1 );
244 }
245 
246 /**
247  * Clear the last error message for a LogFile.
248  *
249  * @param log - pointer to the LogFile
250  */
252 {
253  if (log) {
254  log->errstr[0] = '\0';
255  }
256 }
257 
258 /**
259  * Get the last error message for a LogFile.
260  *
261  * @param log - pointer to the LogFile
262  *
263  * @return
264  * - the last error message that occurred
265  * - NULL if no errors have occurred
266  */
267 const char *log_get_error(LogFile *log)
268 {
269  if (log->errstr[0] == '\0') {
270  return((const char *)NULL);
271  }
272  else {
273  return((const char *)log->errstr);
274  }
275 }
276 
277 /**
278  * Open a LogFile.
279  *
280  * This function will create the log file path with permissions 00775
281  * if it does not already exist. It will then open a log file and place
282  * an advisory lock on it using lockf(). If the log cannot be locked
283  * it probably means another process is using it.
284  *
285  * If the LOG_TAGS flag is set, the "**** OPENED: YYYY-MM-DD hh:mm:ss"
286  * line will be printd to the log file after it is opened.
287  *
288  * The space pointed to by errstr should be large enough to
289  * hold MAX_LOG_ERROR bytes. Any less than that and the error
290  * message could be truncated.
291  *
292  * If errlen is 0 then no error message is written to errstr
293  * and errstr can be NULL.
294  *
295  * @param path - path to the directory to open the log file in
296  * @param name - log file name
297  * @param flags - control flags
298  * @param errlen - length of the error message buffer
299  * @param errstr - output: error message
300  *
301  * Control Flags:
302  *
303  * - LOG_TAGS - Print the "**** OPENED: " line when the log is opened,
304  * and the "**** CLOSED: " line when the log is closed.
305  *
306  * - LOG_STATS - Log process stats before closing the log file.
307  *
308  * - LOG_LOCKF - Place an advisory lock on the log file using lockf().
309  *
310  * @return
311  * - pointer to the open LogFile
312  * - NULL if:
313  * - a memory allocation error occurred
314  * - the log path did not exist and it could not be created
315  * - the log file could not be opened
316  * - the log file could not be locked
317  * - the log file could not be written to
318  */
320  const char *path,
321  const char *name,
322  int flags,
323  size_t errlen,
324  char *errstr)
325 {
326  LogFile *log;
327  char time_string[32];
328  int nbytes;
329 
330  if (!msngr_make_path(path, 00775, errlen, errstr)) {
331  return(0);
332  }
333 
334  log = (LogFile *)calloc(1, sizeof(LogFile));
335 
336  if (!log) {
337 
338  snprintf(errstr, errlen,
339  "Could not open log file: %s/%s\n"
340  " -> memory allocation error\n",
341  path, name);
342 
343  return((LogFile *)NULL);
344  }
345 
346  log->name = msngr_copy_string(name);
347  log->path = msngr_copy_string(path);
348  log->full_path = msngr_create_string("%s/%s", path, name);
349  log->errstr = (char *)calloc(MAX_LOG_ERROR, sizeof(char));
350 
351  if (!log->name || !log->path || !log->full_path || !log->errstr) {
352 
353  _log_free(log);
354 
355  snprintf(errstr, errlen,
356  "Could not open log file: %s/%s\n"
357  " -> memory allocation error\n",
358  path, name);
359 
360  return((LogFile *)NULL);
361  }
362 
363  log->fp = fopen(log->full_path, "a");
364 
365  if (!log->fp) {
366 
367  _log_free(log);
368 
369  snprintf(errstr, errlen,
370  "Could not open log file: %s/%s\n"
371  " -> %s\n",
372  path, name, strerror(errno));
373 
374  return((LogFile *)NULL);
375  }
376 
377  if (flags & LOG_LOCKF) {
378 
379  if (lockf(fileno(log->fp), F_TLOCK, 0) == -1) {
380 
381  _log_free(log);
382 
383  snprintf(errstr, errlen,
384  "Could not get lock on log file: %s/%s\n"
385  " -> %s\n",
386  path, name, strerror(errno));
387 
388  return((LogFile *)NULL);
389  }
390  }
391 
392  log->flags = flags;
393  log->open_time = time(NULL);
394 
395  if (log->flags & LOG_TAGS) {
396 
397  nbytes = fprintf(log->fp,
398  "**** OPENED: %s\n",
399  msngr_format_time(log->open_time, time_string));
400 
401  if (nbytes < 0) {
402 
403  _log_free(log);
404 
405  snprintf(errstr, errlen,
406  "Could not write to log file: %s/%s\n"
407  " -> %s\n",
408  path, name, strerror(errno));
409 
410  return((LogFile *)NULL);
411  }
412  }
413 
414 #if LINUX /* Update Process Stats */
415  if (log->flags & LOG_STATS) {
416  procstats_get();
417  }
418 #endif
419 
420  return(log);
421 }
422 
423 /**
424  * Print a message to a LogFile.
425  *
426  * This function will print a message to a log file under the
427  * control of the format argument. Alternatively, an array of
428  * strings can be passed into this function by specifying
429  * MSNGR_MESSAGE_BLOCK for the format argument. In this case
430  * the next argument after format must be a pointer to a NULL
431  * terminted array of strings (char **).
432  *
433  * If the pointer to the LogFile is NULL or the log file
434  * is not open, all messeges will be printed to stdout.
435  *
436  * @param log - pointer to the LogFile
437  * @param line_tag - line tag to print before the message is printed
438  * @param format - format string (see printf)
439  * @param ... - arguments for the format string
440  *
441  * @return
442  * - 1 if successful
443  * - 0 if an error occurred
444  *
445  * @see log_get_error()
446  */
448  LogFile *log,
449  const char *line_tag,
450  const char *format, ...)
451 {
452  va_list args;
453  int retval;
454 
455  va_start(args, format);
456  retval = log_vprintf(log, line_tag, format, args);
457  va_end(args);
458 
459  return(retval);
460 }
461 
462 /**
463  * Print a message to a LogFile.
464  *
465  * This function will print a message to a log file under the
466  * control of the format argument. Alternatively, an array of
467  * strings can be passed into this function by specifying
468  * MSNGR_MESSAGE_BLOCK for the format argument. In this case
469  * the first argument in the args list must be a pointer to
470  * a NULL terminted array of strings (char **).
471  *
472  * If the pointer to the LogFile is NULL or the log file
473  * is not open, all messeges will be printed to stdout.
474  *
475  * @param log - pointer to the LogFile
476  * @param line_tag - line tag to print before the message is printed
477  * @param format - format string (see printf)
478  * @param args - arguments for the format string
479  *
480  * @return
481  * - 1 if successful
482  * - 0 if an error occurred
483  *
484  * @see log_get_error()
485  */
487  LogFile *log,
488  const char *line_tag,
489  const char *format,
490  va_list args)
491 {
492  FILE *log_fp;
493  int nbytes;
494  int log_errno;
495  char *full_path;
496  char **msg_block;
497  va_list args_copy;
498  size_t length;
499  int i;
500 
501  log_fp = (log && log->fp) ? log->fp : stdout;
502  log_errno = 0;
503  nbytes = 0;
504 
505  /* Print the line tag if one was specified */
506 
507  if (line_tag) {
508  nbytes = fprintf(log_fp, line_tag);
509  if (nbytes < 0) {
510  log_errno = errno;
511  }
512  }
513 
514  /* Print the message to the log file */
515 
516  if (strcmp(format, "MSNGR_MESSAGE_BLOCK") == 0) {
517 
518  va_copy(args_copy, args);
519  msg_block = va_arg(args_copy, char **);
520  va_end(args_copy);
521 
522  for (i = 0; msg_block[i] != (char *)NULL; i++) {
523 
524  length = strlen(msg_block[i]);
525  nbytes = fprintf(log_fp, msg_block[i]);
526  if (nbytes < 0) {
527  log_errno = errno;
528  }
529  else {
530  if ((length == 0) || (msg_block[i][length-1] != '\n')) {
531  fprintf(log_fp, "\n");
532  }
533  }
534  }
535  }
536  else {
537  length = strlen(format);
538  nbytes = msngr_vfprintf(log_fp, format, args);
539  if ((length == 0) || (format[length-1] != '\n')) {
540  fprintf(log_fp, "\n");
541  }
542  }
543 
544  if (nbytes < 0) {
545  log_errno = errno;
546  }
547 
548  /* Flush the log file buffer */
549 
550  if (fflush(log_fp) != 0) {
551  log_errno = errno;
552  }
553 
554  /* Check for errors */
555 
556  if (log_errno) {
557 
558  if (log_fp == stdout) {
559  full_path = "stdout";
560  }
561  else if (log_fp == stderr) {
562  full_path = "stderr";
563  }
564  else if (log->full_path) {
565  full_path = log->full_path;
566  }
567  else {
568  full_path = "";
569  }
570 
571  snprintf(log->errstr, MAX_LOG_ERROR,
572  "Could not write to log file: %s\n"
573  " -> %s\n",
574  full_path, strerror(log_errno));
575  }
576 
577 #if LINUX /* Update Process Stats */
578  if (log->flags & LOG_STATS) {
579  procstats_get();
580  }
581 #endif
582 
583  return((log_errno) ? 0 : 1 );
584 }
585 
586 /*@}*/