First cut of the output streams
This commit was SVN r360.
Этот коммит содержится в:
родитель
904e4f0d58
Коммит
1140112b9b
@ -13,8 +13,8 @@ headers = \
|
|||||||
argv.h \
|
argv.h \
|
||||||
cmd_line.h \
|
cmd_line.h \
|
||||||
few.h \
|
few.h \
|
||||||
lam_log.h \
|
|
||||||
malloc.h \
|
malloc.h \
|
||||||
|
output.h \
|
||||||
path.h \
|
path.h \
|
||||||
reactor.h \
|
reactor.h \
|
||||||
strncpy.h
|
strncpy.h
|
||||||
@ -24,7 +24,7 @@ libutil_la_SOURCES = \
|
|||||||
argv.c \
|
argv.c \
|
||||||
cmd_line.c \
|
cmd_line.c \
|
||||||
few.c \
|
few.c \
|
||||||
lam_log.c \
|
output.c \
|
||||||
path.c \
|
path.c \
|
||||||
reactor.c \
|
reactor.c \
|
||||||
strncpy.c
|
strncpy.c
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* $HEADER$
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lam/util/lam_log.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
#define FILE_LINE_MAX 255
|
|
||||||
static char file_line[FILE_LINE_MAX + 1] = "";
|
|
||||||
|
|
||||||
void _lam_log(FILE *fd, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
/* Write to a file descriptor and the log file */
|
|
||||||
|
|
||||||
if (fd != NULL) {
|
|
||||||
fprintf(fd, file_line);
|
|
||||||
vfprintf(fd, fmt, ap);
|
|
||||||
fflush(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
file_line[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
void _lam_print(FILE * fd, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
/* Write to a file descriptor (usually stdout or stderr) */
|
|
||||||
|
|
||||||
if (fd != NULL) {
|
|
||||||
fprintf(fd, file_line);
|
|
||||||
vfprintf(fd, fmt, ap);
|
|
||||||
fflush(fd);
|
|
||||||
}
|
|
||||||
file_line[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
void _lam_set_file_line(const char *name, int line)
|
|
||||||
{
|
|
||||||
sprintf(file_line, "LAM/MPI:%s:%d: ", name, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _lam_err(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
_lam_log(stderr, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
void _lam_warn(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
_lam_log(stderr, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _lam_dbg(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
_lam_log(stdout, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _lam_exit(int status, const char* fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
if (fmt) {
|
|
||||||
va_start(ap, fmt);
|
|
||||||
if (status != 0) {
|
|
||||||
_lam_log(stderr, fmt, ap);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_lam_print(stdout, fmt, ap);
|
|
||||||
}
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(status);
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* $HEADER$
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef LAM_LOG_H
|
|
||||||
#define LAM_LOG_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define lam_dbg(x) \
|
|
||||||
do { \
|
|
||||||
if (OPT_DBG) { \
|
|
||||||
_lam_set_file_line( __FILE__, __LINE__) ; \
|
|
||||||
_lam_dbg x ; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define lam_err(x) \
|
|
||||||
do { \
|
|
||||||
_lam_set_file_line(__FILE__, __LINE__) ; \
|
|
||||||
_lam_err x ; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define lam_warn(x) \
|
|
||||||
do { \
|
|
||||||
_lam_set_file_line(__FILE__, __LINE__) ; \
|
|
||||||
_lam_warn x ; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define lam_exit(x) \
|
|
||||||
do { \
|
|
||||||
_lam_set_file_line(__FILE__, __LINE__) ; \
|
|
||||||
_lam_exit x ; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* Error condition */
|
|
||||||
void _lam_err(const char* fmt, ...);
|
|
||||||
|
|
||||||
/* Warning condition */
|
|
||||||
void _lam_warn(const char* fmt, ...);
|
|
||||||
|
|
||||||
/* Debugging message */
|
|
||||||
void _lam_dbg(const char* fmt, ...);
|
|
||||||
|
|
||||||
/* Exit with error message */
|
|
||||||
void _lam_exit(int status, const char* fmt, ...);
|
|
||||||
|
|
||||||
/* Set file and line info */
|
|
||||||
void _lam_set_file_line(const char *file, int lineno);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* LAM_LOG_H */
|
|
||||||
|
|
||||||
|
|
740
src/lam/util/output.c
Обычный файл
740
src/lam/util/output.c
Обычный файл
@ -0,0 +1,740 @@
|
|||||||
|
/*
|
||||||
|
* $HEADER$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lam_config.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#if __STDC__
|
||||||
|
#include <stdarg.h>
|
||||||
|
#else
|
||||||
|
#include <varargs.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lam/constants.h"
|
||||||
|
#include "lam/util/malloc.h"
|
||||||
|
#include "lam/util/output.h"
|
||||||
|
#include "lam/threads/mutex.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Private data
|
||||||
|
*/
|
||||||
|
int verbose_stream = -1;
|
||||||
|
int verbose_level = 0;
|
||||||
|
lam_output_stream_t verbose = {
|
||||||
|
/* debugging */
|
||||||
|
false,
|
||||||
|
/* verbose level */
|
||||||
|
0,
|
||||||
|
/* syslog */
|
||||||
|
false, 0, NULL, NULL,
|
||||||
|
/* stdout */
|
||||||
|
false,
|
||||||
|
/* stderr */
|
||||||
|
true,
|
||||||
|
/* file */
|
||||||
|
false, false, NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Private functions
|
||||||
|
*/
|
||||||
|
static void output(int output_id, char *format, va_list arglist);
|
||||||
|
static char *lam_vsnprintf(char *format, va_list arglist);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal data structures and helpers for the generalized output
|
||||||
|
* stream mechanism.
|
||||||
|
*/
|
||||||
|
struct output_desc_t {
|
||||||
|
bool ldi_used;
|
||||||
|
bool ldi_enabled;
|
||||||
|
int ldi_verbose_level;
|
||||||
|
|
||||||
|
bool ldi_syslog;
|
||||||
|
int ldi_syslog_priority;
|
||||||
|
char *ldi_syslog_ident;
|
||||||
|
|
||||||
|
char *ldi_prefix;
|
||||||
|
int ldi_prefix_len;
|
||||||
|
|
||||||
|
bool ldi_stdout;
|
||||||
|
bool ldi_stderr;
|
||||||
|
|
||||||
|
int ldi_fd;
|
||||||
|
char *ldi_file_suffix;
|
||||||
|
};
|
||||||
|
typedef struct output_desc_t output_desc_t;
|
||||||
|
|
||||||
|
#define LAM_OUTPUT_MAX_STREAMS 32
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local state
|
||||||
|
*/
|
||||||
|
static bool initialized = false;
|
||||||
|
static output_desc_t info[LAM_OUTPUT_MAX_STREAMS];
|
||||||
|
static char *temp_str = 0;
|
||||||
|
static int temp_str_len = 0;
|
||||||
|
static lam_mutex_t mutex;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the output stream system and opens a default
|
||||||
|
* "verbose" stream.
|
||||||
|
*
|
||||||
|
* @retval true Upon success.
|
||||||
|
* @retval false Upon failure.
|
||||||
|
*
|
||||||
|
* This should be the first function invoked in the output subsystem.
|
||||||
|
* After this call, the default "verbose" stream is open and can be
|
||||||
|
* written to via calls to lam_output_verbose() and
|
||||||
|
* lam_output_error().
|
||||||
|
*
|
||||||
|
* By definition, the default verbose stream has a handle ID of 0, and
|
||||||
|
* has a verbose level of 0.
|
||||||
|
*/
|
||||||
|
bool lam_output_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < LAM_OUTPUT_MAX_STREAMS; ++i) {
|
||||||
|
info[i].ldi_used = false;
|
||||||
|
info[i].ldi_enabled = false;
|
||||||
|
|
||||||
|
info[i].ldi_syslog = false;
|
||||||
|
info[i].ldi_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the mutex that protects the output */
|
||||||
|
|
||||||
|
lam_mutex_init(&mutex);
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
/* Open the default verbose stream */
|
||||||
|
|
||||||
|
verbose_stream = lam_output_open(&verbose);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens output streams.
|
||||||
|
*
|
||||||
|
* @param lds A pointer to lam_output_stream_t describing what the
|
||||||
|
* characteristics of the output stream should be.
|
||||||
|
*
|
||||||
|
* This function opens an output stream and returns an integer handle.
|
||||||
|
* The caller is responsible for maintaining the handle and using it
|
||||||
|
* in successive calls to LAM_OUTPUT(), lam_output(),
|
||||||
|
* lam_output_switch(), and lam_output_close().
|
||||||
|
*
|
||||||
|
* It is safe to have multiple threads invoke this function
|
||||||
|
* simultaneously; their execution will be serialized in an
|
||||||
|
* unspecified manner.
|
||||||
|
*/
|
||||||
|
int lam_output_open(lam_output_stream_t *lds)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int flags;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
/* Setup */
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
lam_output_init();
|
||||||
|
|
||||||
|
/* Find an available stream, or return LAM_ERROR */
|
||||||
|
|
||||||
|
THREAD_LOCK(&mutex);
|
||||||
|
for (i = 0; i < LAM_OUTPUT_MAX_STREAMS; ++i)
|
||||||
|
if (!info[i].ldi_used)
|
||||||
|
break;
|
||||||
|
if (i >= LAM_OUTPUT_MAX_STREAMS) {
|
||||||
|
THREAD_UNLOCK(&mutex);
|
||||||
|
return LAM_ERR_OUT_OF_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Got a stream -- now initialize it and open relevant outputs */
|
||||||
|
|
||||||
|
info[i].ldi_used = true;
|
||||||
|
THREAD_UNLOCK(&mutex);
|
||||||
|
info[i].ldi_enabled = lds->lds_is_debugging ? (bool) LAM_ENABLE_DEBUG : true;
|
||||||
|
info[i].ldi_verbose_level = 0;
|
||||||
|
|
||||||
|
info[i].ldi_syslog = lds->lds_want_syslog;
|
||||||
|
if (lds->lds_want_syslog) {
|
||||||
|
if (NULL != lds->lds_syslog_ident) {
|
||||||
|
info[i].ldi_syslog_ident = strdup(lds->lds_syslog_ident);
|
||||||
|
openlog(lds->lds_syslog_ident, LOG_PID, LOG_USER);
|
||||||
|
} else {
|
||||||
|
info[i].ldi_syslog_ident = NULL;
|
||||||
|
openlog("lam", LOG_PID, LOG_USER);
|
||||||
|
}
|
||||||
|
info[i].ldi_syslog_priority = lds->lds_syslog_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL != lds->lds_prefix) {
|
||||||
|
info[i].ldi_prefix = strdup(lds->lds_prefix);
|
||||||
|
info[i].ldi_prefix_len = strlen(lds->lds_prefix);
|
||||||
|
} else {
|
||||||
|
info[i].ldi_prefix = NULL;
|
||||||
|
info[i].ldi_prefix_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
info[i].ldi_stdout = lds->lds_want_stdout;
|
||||||
|
info[i].ldi_stderr = lds->lds_want_stderr;
|
||||||
|
|
||||||
|
info[i].ldi_fd = -1;
|
||||||
|
if (lds->lds_want_file) {
|
||||||
|
|
||||||
|
/* Setup the filename and open flags */
|
||||||
|
|
||||||
|
#if NEED_TO_IMPLEMENT_SESSION_DIRECTORY
|
||||||
|
filename = lam_get_tmpdir();
|
||||||
|
#else
|
||||||
|
filename = LAM_MALLOC(256);
|
||||||
|
strcpy(filename, "/tmp");
|
||||||
|
#endif
|
||||||
|
strcat(filename, "/lam-");
|
||||||
|
if (lds->lds_file_suffix != NULL) {
|
||||||
|
info[i].ldi_file_suffix = strdup(lds->lds_file_suffix);
|
||||||
|
strcat(filename, lds->lds_file_suffix);
|
||||||
|
} else {
|
||||||
|
info[i].ldi_file_suffix = NULL;
|
||||||
|
strcat(filename, "output.txt");
|
||||||
|
}
|
||||||
|
flags = O_CREAT | O_RDWR;
|
||||||
|
if (!lds->lds_want_file_append)
|
||||||
|
flags |= O_TRUNC;
|
||||||
|
|
||||||
|
/* Actually open the file */
|
||||||
|
|
||||||
|
info[i].ldi_fd = open(filename, flags, 0644);
|
||||||
|
if (-1 == info[i].ldi_fd) {
|
||||||
|
info[i].ldi_used = false;
|
||||||
|
return LAM_ERR_IN_ERRNO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make the file be close-on-exec to prevent child inheritance
|
||||||
|
problems */
|
||||||
|
|
||||||
|
fcntl(info[i].ldi_fd, F_SETFD, 1);
|
||||||
|
LAM_FREE(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables and disables output streams.
|
||||||
|
*
|
||||||
|
* @param output_id Stream handle to switch
|
||||||
|
* @param enable Boolean indicating whether to enable the stream
|
||||||
|
* output or not.
|
||||||
|
*
|
||||||
|
* @returns The previous enable state of the stream (true == enabled,
|
||||||
|
* false == disabled).
|
||||||
|
*
|
||||||
|
* The output of a stream can be temporarily disabled by passing an
|
||||||
|
* enable value to false, and later resumed by passing an enable value
|
||||||
|
* of true. This does not close the stream -- it simply tells the
|
||||||
|
* lam_output subsystem to intercept and discard any output sent to
|
||||||
|
* the stream via LAM_OUTPUT() or lam_output() until the output is
|
||||||
|
* re-enabled.
|
||||||
|
*/
|
||||||
|
bool lam_output_switch(int output_id, bool enable)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
/* Setup */
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
lam_output_init();
|
||||||
|
|
||||||
|
if (output_id >= 0 && output_id < LAM_OUTPUT_MAX_STREAMS) {
|
||||||
|
ret = info[output_id].ldi_enabled;
|
||||||
|
info[output_id].ldi_enabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
*
|
||||||
|
* Reopens all existing output streams.
|
||||||
|
*
|
||||||
|
* This function should never be called by user applications; it is
|
||||||
|
* typically only invoked after a restart (i.e., in a new process)
|
||||||
|
* where output streams need to be re-initialized.
|
||||||
|
*/
|
||||||
|
void lam_output_reopen_all(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
lam_output_stream_t lds;
|
||||||
|
|
||||||
|
for (i = 0; i < LAM_OUTPUT_MAX_STREAMS; ++i) {
|
||||||
|
|
||||||
|
/* scan till we find ldi_used == 0, which is the end-marker */
|
||||||
|
if (!info[i].ldi_used)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set this to zero to ensure that lam_output_open will return this same
|
||||||
|
* index as the output stream id
|
||||||
|
*/
|
||||||
|
info[i].ldi_used = false;
|
||||||
|
|
||||||
|
lds.lds_want_syslog = info[i].ldi_syslog;
|
||||||
|
lds.lds_syslog_priority = info[i].ldi_syslog_priority;
|
||||||
|
lds.lds_syslog_ident = info[i].ldi_syslog_ident;
|
||||||
|
lds.lds_prefix = info[i].ldi_prefix;
|
||||||
|
lds.lds_want_stdout = info[i].ldi_stdout;
|
||||||
|
lds.lds_want_stderr = info[i].ldi_stderr;
|
||||||
|
lds.lds_want_file = (-1 == info[i].ldi_fd) ? false: true;
|
||||||
|
/* open all streams in append mode */
|
||||||
|
lds.lds_want_file_append = true;
|
||||||
|
lds.lds_file_suffix = info[i].ldi_file_suffix;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call lam_output_open to open the stream. The return value is
|
||||||
|
* guaranteed to be i. So we can ignore it.
|
||||||
|
*/
|
||||||
|
lam_output_open(&lds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close an output stream.
|
||||||
|
*
|
||||||
|
* @param output_id Handle of the stream to close.
|
||||||
|
*
|
||||||
|
* Close an output stream. No output will be sent to the stream after
|
||||||
|
* it is closed. Be aware that output handles tend to be re-used; it
|
||||||
|
* is possible that after a stream is closed, if another stream is
|
||||||
|
* opened, it will get the same handle value.
|
||||||
|
*/
|
||||||
|
void lam_output_close(int output_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
output_desc_t *ldi;
|
||||||
|
|
||||||
|
/* Setup */
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
lam_output_init();
|
||||||
|
|
||||||
|
/* If it's valid, used, enabled, and has an open file descriptor,
|
||||||
|
close it */
|
||||||
|
|
||||||
|
if (output_id >= 0 && output_id < LAM_OUTPUT_MAX_STREAMS &&
|
||||||
|
info[output_id].ldi_used &&
|
||||||
|
info[output_id].ldi_enabled) {
|
||||||
|
|
||||||
|
ldi = &info[output_id];
|
||||||
|
|
||||||
|
if (-1 != ldi->ldi_fd)
|
||||||
|
close(ldi->ldi_fd);
|
||||||
|
ldi->ldi_used = false;
|
||||||
|
|
||||||
|
/* If we strduped a prefix, suffix, or syslog ident, free it */
|
||||||
|
|
||||||
|
if (NULL != ldi->ldi_prefix)
|
||||||
|
LAM_FREE(ldi->ldi_prefix);
|
||||||
|
ldi->ldi_prefix = NULL;
|
||||||
|
|
||||||
|
if (NULL != ldi->ldi_file_suffix)
|
||||||
|
LAM_FREE(ldi->ldi_file_suffix);
|
||||||
|
ldi->ldi_file_suffix = NULL;
|
||||||
|
|
||||||
|
if (NULL != ldi->ldi_syslog_ident)
|
||||||
|
LAM_FREE(ldi->ldi_syslog_ident);
|
||||||
|
ldi->ldi_syslog_ident = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If no one has the syslog open, we should close it */
|
||||||
|
|
||||||
|
THREAD_LOCK(&mutex);
|
||||||
|
for (i = 0; i < LAM_OUTPUT_MAX_STREAMS; ++i)
|
||||||
|
if (info[i].ldi_used && info[i].ldi_syslog)
|
||||||
|
break;
|
||||||
|
if (i >= LAM_OUTPUT_MAX_STREAMS)
|
||||||
|
closelog();
|
||||||
|
|
||||||
|
/* Somewhat of a hack to free up the temp_str */
|
||||||
|
|
||||||
|
if (NULL != temp_str) {
|
||||||
|
LAM_FREE(temp_str);
|
||||||
|
temp_str = NULL;
|
||||||
|
temp_str_len = 0;
|
||||||
|
}
|
||||||
|
THREAD_UNLOCK(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main function to send output to a stream.
|
||||||
|
*
|
||||||
|
* @param output_id Stream id returned from lam_output_open().
|
||||||
|
* @param format printf-style format string.
|
||||||
|
* @param varargs printf-style varargs list to fill the string
|
||||||
|
* specified by the format parameter.
|
||||||
|
*
|
||||||
|
* This is the main function to send output to custom streams (note
|
||||||
|
* that output to the default "verbose" stream is handled through
|
||||||
|
* lam_output_verbose() and lam_output_error()).
|
||||||
|
*
|
||||||
|
* It is never necessary to send a trailing "\n" in the strings to
|
||||||
|
* this function; some streams requires newlines, others do not --
|
||||||
|
* this function will append newlines as necessary.
|
||||||
|
*
|
||||||
|
* Verbosity levels are ignored in this function.
|
||||||
|
*/
|
||||||
|
void lam_output(int output_id, char *format, ...)
|
||||||
|
{
|
||||||
|
va_list arglist;
|
||||||
|
#if __STDC__
|
||||||
|
va_start(arglist, format);
|
||||||
|
#else
|
||||||
|
va_start(arglist);
|
||||||
|
#endif
|
||||||
|
output(output_id, format, arglist);
|
||||||
|
va_end(arglist);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send output to a stream only if the passed verbosity level is high
|
||||||
|
* enough.
|
||||||
|
*
|
||||||
|
* @param output_id Stream id returned from lam_output_open().
|
||||||
|
* @param level Target verbosity level.
|
||||||
|
* @param format printf-style format string.
|
||||||
|
* @param varargs printf-style varargs list to fill the string
|
||||||
|
* specified by the format parameter.
|
||||||
|
*
|
||||||
|
* Output is only sent to the stream if the current verbosity level is
|
||||||
|
* greater than or equal to the level parameter. This mechanism can
|
||||||
|
* be used to send "information" kinds of output to user applications,
|
||||||
|
* but only when the user has asked for a high enough verbosity level.
|
||||||
|
*
|
||||||
|
* It is never necessary to send a trailing "\n" in the strings to
|
||||||
|
* this function; some streams requires newlines, others do not --
|
||||||
|
* this function will append newlines as necessary.
|
||||||
|
*
|
||||||
|
* This function is really a convenience wrapper around checking the
|
||||||
|
* current verbosity level set on the stream, and if the passed level
|
||||||
|
* is less than or equal to the stream's verbosity level, this
|
||||||
|
* function will effectively invoke lam_output to send the output to
|
||||||
|
* the stream.
|
||||||
|
*
|
||||||
|
* @see lam_output_set_verbosity()
|
||||||
|
*/
|
||||||
|
void lam_output_verbose(int output_id, int level, char *format, ...)
|
||||||
|
{
|
||||||
|
if (info[output_id].ldi_verbose_level >= level) {
|
||||||
|
va_list arglist;
|
||||||
|
#if __STDC__
|
||||||
|
va_start(arglist, format);
|
||||||
|
#else
|
||||||
|
va_start(arglist);
|
||||||
|
#endif
|
||||||
|
output(output_id, format, arglist);
|
||||||
|
va_end(arglist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the verbosity level for a stream.
|
||||||
|
*
|
||||||
|
* @param output_id Stream id returned from lam_output_open().
|
||||||
|
* @param level New verbosity level
|
||||||
|
*
|
||||||
|
* This function sets the verbosity level on a given stream. It will
|
||||||
|
* be used for all future invocations of lam_output_verbose().
|
||||||
|
*/
|
||||||
|
void lam_output_set_verbosity(int output_id, int level)
|
||||||
|
{
|
||||||
|
info[output_id].ldi_verbose_level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shut down the output stream system.
|
||||||
|
*
|
||||||
|
* Shut down the output stream system, including the default verbose
|
||||||
|
* stream.
|
||||||
|
*/
|
||||||
|
void lam_output_finalize(void)
|
||||||
|
{
|
||||||
|
if (initialized) {
|
||||||
|
if (verbose_stream != -1)
|
||||||
|
lam_output_close(verbose_stream);
|
||||||
|
verbose_stream = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do the actual output. Take a va_list so that we can be called from
|
||||||
|
* multiple different places, even functions that took "..." as input
|
||||||
|
* arguments.
|
||||||
|
*/
|
||||||
|
static void output(int output_id, char *format, va_list arglist)
|
||||||
|
{
|
||||||
|
int len, total_len;
|
||||||
|
bool want_newline = false;
|
||||||
|
char *str;
|
||||||
|
output_desc_t *ldi;
|
||||||
|
|
||||||
|
/* Setup */
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
lam_output_init();
|
||||||
|
|
||||||
|
/* If it's valid, used, and enabled, output */
|
||||||
|
|
||||||
|
if (output_id >= 0 && output_id < LAM_OUTPUT_MAX_STREAMS &&
|
||||||
|
info[output_id].ldi_used &&
|
||||||
|
info[output_id].ldi_enabled) {
|
||||||
|
ldi = &info[output_id];
|
||||||
|
|
||||||
|
/* Make the formatted string */
|
||||||
|
|
||||||
|
THREAD_LOCK(&mutex);
|
||||||
|
str = lam_vsnprintf(format, arglist);
|
||||||
|
total_len = len = strlen(str);
|
||||||
|
if ('\n' != str[len - 1]) {
|
||||||
|
want_newline = true;
|
||||||
|
++total_len;
|
||||||
|
}
|
||||||
|
if (NULL != ldi->ldi_prefix)
|
||||||
|
total_len += strlen(ldi->ldi_prefix);
|
||||||
|
if (temp_str_len < total_len + want_newline) {
|
||||||
|
if (NULL != temp_str)
|
||||||
|
LAM_FREE(temp_str);
|
||||||
|
temp_str = malloc(total_len * 2);
|
||||||
|
temp_str_len = total_len * 2;
|
||||||
|
}
|
||||||
|
if (NULL != ldi->ldi_prefix) {
|
||||||
|
if (want_newline)
|
||||||
|
snprintf(temp_str, temp_str_len, "%s%s\n", ldi->ldi_prefix, str);
|
||||||
|
else
|
||||||
|
snprintf(temp_str, temp_str_len, "%s%s", ldi->ldi_prefix, str);
|
||||||
|
} else {
|
||||||
|
if (want_newline)
|
||||||
|
snprintf(temp_str, temp_str_len, "%s\n", str);
|
||||||
|
else
|
||||||
|
snprintf(temp_str, temp_str_len, "%s", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Syslog output */
|
||||||
|
|
||||||
|
if (ldi->ldi_syslog)
|
||||||
|
syslog(ldi->ldi_syslog_priority, str);
|
||||||
|
|
||||||
|
/* stdout output */
|
||||||
|
|
||||||
|
if (ldi->ldi_stdout) {
|
||||||
|
printf(temp_str);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stderr output */
|
||||||
|
|
||||||
|
if (ldi->ldi_stderr) {
|
||||||
|
fprintf(stderr, temp_str);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File output */
|
||||||
|
|
||||||
|
if (ldi->ldi_fd != -1)
|
||||||
|
write(ldi->ldi_fd, temp_str, total_len);
|
||||||
|
THREAD_UNLOCK(&mutex);
|
||||||
|
|
||||||
|
LAM_FREE(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lam_vsnprintf
|
||||||
|
*
|
||||||
|
* Make a good guess about how long a printf-style varags formatted
|
||||||
|
* string will be once all the % escapes are filled in. We don't
|
||||||
|
* handle every % escape here, but we handle enough, and then add a
|
||||||
|
* fudge factor in at the end. When we have that, alloc out a buffer
|
||||||
|
* and snprintf into it. The whole reason for this routine is because
|
||||||
|
* we can't count on vsnprintf to be on every system. :-( Ok, it's
|
||||||
|
* not exactly the same as vsnprintf, but it's in the same spirit, so
|
||||||
|
* it's ok to use a derrivative of the name...
|
||||||
|
*/
|
||||||
|
static char *lam_vsnprintf(char *format, va_list arglist)
|
||||||
|
{
|
||||||
|
int i, len;
|
||||||
|
char *sarg;
|
||||||
|
int iarg;
|
||||||
|
long larg;
|
||||||
|
double darg;
|
||||||
|
float farg;
|
||||||
|
|
||||||
|
/* Important: keep a copy of the original arglist because when we
|
||||||
|
traverse through the original arglist, it has internal state that
|
||||||
|
knows where it is in the list of args. At the end of this
|
||||||
|
routine, we'll need the whole list in order to call vsprintf(),
|
||||||
|
and there doesn't appear to be a way to "rewind" a va_alist.
|
||||||
|
|
||||||
|
Copy order taken from Autoconf docs
|
||||||
|
*/
|
||||||
|
va_list arglist2;
|
||||||
|
#if LAM_HAVE_VA_COPY
|
||||||
|
va_copy(arglist2, arglist);
|
||||||
|
#elif LAM_HAVE_UNDERSCORE_VA_COPY
|
||||||
|
__va_copy(arglist2, arglist);
|
||||||
|
#else
|
||||||
|
memcpy (&arglist2, &arglist, sizeof(va_list));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Start off with a fudge factor of 128 to handle the % escapes that
|
||||||
|
we aren't calculating here */
|
||||||
|
|
||||||
|
len = strlen(format) + 128;
|
||||||
|
for (i = 0; i < strlen(format); ++i) {
|
||||||
|
if ('%' == format[i] && i + 1 < strlen(format) && '%' != format[i + 1]) {
|
||||||
|
++i;
|
||||||
|
switch(format[i]) {
|
||||||
|
case 's':
|
||||||
|
sarg = va_arg(arglist, char*);
|
||||||
|
/* If there's an arg, get the strlen, otherwise we'll use (null) */
|
||||||
|
if (NULL != sarg)
|
||||||
|
len += strlen(sarg);
|
||||||
|
else
|
||||||
|
len += 5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
case 'i':
|
||||||
|
iarg = va_arg(arglist, int);
|
||||||
|
/* Alloc for minus sign */
|
||||||
|
if (iarg < 0)
|
||||||
|
++len;
|
||||||
|
/* Now get the log10 */
|
||||||
|
do {
|
||||||
|
++len;
|
||||||
|
iarg /= 10;
|
||||||
|
} while (0 != iarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
iarg = va_arg(arglist, int);
|
||||||
|
/* Now get the log16 */
|
||||||
|
do {
|
||||||
|
++len;
|
||||||
|
iarg /= 16;
|
||||||
|
} while (0 != iarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
farg = va_arg(arglist, int);
|
||||||
|
/* Alloc for minus sign */
|
||||||
|
if (farg < 0) {
|
||||||
|
++len;
|
||||||
|
farg = -farg;
|
||||||
|
}
|
||||||
|
/* Alloc for 3 decimal places + '.' */
|
||||||
|
len += 4;
|
||||||
|
/* Now get the log10 */
|
||||||
|
do {
|
||||||
|
++len;
|
||||||
|
farg /= 10.0;
|
||||||
|
} while (0 != farg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
darg = va_arg(arglist, int);
|
||||||
|
/* Alloc for minus sign */
|
||||||
|
if (darg < 0) {
|
||||||
|
++len;
|
||||||
|
darg = -darg;
|
||||||
|
}
|
||||||
|
/* Alloc for 3 decimal places + '.' */
|
||||||
|
len += 4;
|
||||||
|
/* Now get the log10 */
|
||||||
|
do {
|
||||||
|
++len;
|
||||||
|
darg /= 10.0;
|
||||||
|
} while (0 != darg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
/* Get %ld %lx %lX %lf */
|
||||||
|
if (i + 1 < strlen(format)) {
|
||||||
|
++i;
|
||||||
|
switch(format[i]) {
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
larg = va_arg(arglist, int);
|
||||||
|
/* Now get the log16 */
|
||||||
|
do {
|
||||||
|
++len;
|
||||||
|
larg /= 16;
|
||||||
|
} while (0 != larg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
darg = va_arg(arglist, int);
|
||||||
|
/* Alloc for minus sign */
|
||||||
|
if (darg < 0) {
|
||||||
|
++len;
|
||||||
|
darg = -darg;
|
||||||
|
}
|
||||||
|
/* Alloc for 3 decimal places + '.' */
|
||||||
|
len += 4;
|
||||||
|
/* Now get the log10 */
|
||||||
|
do {
|
||||||
|
++len;
|
||||||
|
darg /= 10.0;
|
||||||
|
} while (0 != darg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
default:
|
||||||
|
larg = va_arg(arglist, int);
|
||||||
|
/* Now get the log10 */
|
||||||
|
do {
|
||||||
|
++len;
|
||||||
|
larg /= 10;
|
||||||
|
} while (0 != larg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wasn't that simple? Now malloc out a string and do the final
|
||||||
|
formatting into that string. */
|
||||||
|
|
||||||
|
sarg = LAM_MALLOC(len);
|
||||||
|
vsprintf(sarg, format, arglist2);
|
||||||
|
|
||||||
|
/* Return the new string */
|
||||||
|
|
||||||
|
return sarg;
|
||||||
|
}
|
223
src/lam/util/output.h
Обычный файл
223
src/lam/util/output.h
Обычный файл
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* $HEADER$
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* LAM output stream facility.
|
||||||
|
*
|
||||||
|
* The LAM output stream facility is used to send output from the LAM
|
||||||
|
* libraries to output devices. It is meant to fully replace all
|
||||||
|
* forms of printf() (and friends). Output streams are opened via the
|
||||||
|
* lam_output_open() function call, and then sent output via
|
||||||
|
* lam_output_verbose(), LAM_OUTPUT(), and lam_output(). Streams are
|
||||||
|
* closed with lam_output_close().
|
||||||
|
*
|
||||||
|
* Streams can multiplex output to several kinds of outputs (one of
|
||||||
|
* each):
|
||||||
|
*
|
||||||
|
* - the syslog
|
||||||
|
* - standard output
|
||||||
|
* - standard error
|
||||||
|
* - file
|
||||||
|
*
|
||||||
|
* Which outputs to use are specified during lam_output_open().
|
||||||
|
*
|
||||||
|
* lam_output_open() returns an integer handle that is used in
|
||||||
|
* successive calls to LAM_OUTPUT() and lam_output() to send output to
|
||||||
|
* the stream.
|
||||||
|
*
|
||||||
|
* The default "verbose" stream is opened after invoking
|
||||||
|
* lam_output_init() (and closed after invoking
|
||||||
|
* lam_output_finalize()). This stream outputs to stderr only, and
|
||||||
|
* has a stream handle ID of 0.
|
||||||
|
*
|
||||||
|
* It is erroneous to have one thread close a stream and have another
|
||||||
|
* try to write to it. Multiple threads writing to a single stream
|
||||||
|
* will be serialized in an unspecified order.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LAM_OUTPUT_H_
|
||||||
|
#define LAM_OUTPUT_H_
|
||||||
|
|
||||||
|
#include "lam_config.h"
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
#include <stdarg.h>
|
||||||
|
#else
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <stdarg.h>
|
||||||
|
#else
|
||||||
|
#include <varargs.h>
|
||||||
|
#endif
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class lam_output_stream_t
|
||||||
|
*
|
||||||
|
* Structure used to request the opening of a LAM output stream. A
|
||||||
|
* pointer to this structure is passed to lam_output_open() to tell
|
||||||
|
* the lam_output subsystem where to send output for a given stream.
|
||||||
|
* It is valid to specify multiple destinations of output for a stream
|
||||||
|
* -- output streams can be multiplexed to multiple different
|
||||||
|
* destinations through the lam_output facility.
|
||||||
|
*
|
||||||
|
* Note that all strings in this struct are cached on the stream by
|
||||||
|
* value; there is no need to keep them allocated after the return
|
||||||
|
* from lam_output_open().
|
||||||
|
*
|
||||||
|
* @see output.h
|
||||||
|
*/
|
||||||
|
struct lam_output_stream_t {
|
||||||
|
/**
|
||||||
|
* Indicates whether the output of the stream is
|
||||||
|
* debugging/developer-only output or not.
|
||||||
|
*
|
||||||
|
* This field should be "true" if the output is for debugging
|
||||||
|
* purposes only. In that case, the output will never be sent to
|
||||||
|
* the stream unless LAM was configured with --enable-debug.
|
||||||
|
*/
|
||||||
|
bool lds_is_debugging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate the starting verbosity level of the stream.
|
||||||
|
*
|
||||||
|
* Verbose levels are a convenience mechanisms, and are only
|
||||||
|
* consulted when output is sent to a stream through the
|
||||||
|
* lam_output_verbose() function. Verbose levels are ignored in
|
||||||
|
* LAM_OUTPUT() and lam_output().
|
||||||
|
*
|
||||||
|
* Valid verbose levels typically start at 0 (meaning "minimal
|
||||||
|
* information"). Higher verbosity levels generally indicate that
|
||||||
|
* more output and diagnostics should be displayed.
|
||||||
|
*/
|
||||||
|
int lda_verbose_level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether output of the stream should be sent to the
|
||||||
|
* syslog or not.
|
||||||
|
*
|
||||||
|
* If this field is true, output from this stream is sent to the
|
||||||
|
* syslog, and the following fields are also examined:
|
||||||
|
*
|
||||||
|
* - lds_syslog_priority
|
||||||
|
* - lds_syslog_ident
|
||||||
|
* - lds_prefix
|
||||||
|
*
|
||||||
|
* If this field is false, the above three fields are ignored.
|
||||||
|
*/
|
||||||
|
bool lds_want_syslog;
|
||||||
|
/**
|
||||||
|
* When lam_output_stream_t::lds_want_syslog is true, this field is
|
||||||
|
* examined to see what priority output from the stream should be
|
||||||
|
* sent to the syslog.
|
||||||
|
*
|
||||||
|
* This value should be set as per the syslog(3) man page. It is
|
||||||
|
* typically the OR value of "facilty" and "level" values described
|
||||||
|
* in the man page.
|
||||||
|
*/
|
||||||
|
int lds_syslog_priority;
|
||||||
|
/**
|
||||||
|
* When lam_output_stream_t::lds_want_syslog is true, this field is
|
||||||
|
* examined to see what ident value should be passed to openlog(3).
|
||||||
|
*
|
||||||
|
* If a NULL value is given, the string "lam" is used.
|
||||||
|
*/
|
||||||
|
char *lds_syslog_ident;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String prefix added to all output on the stream.
|
||||||
|
*
|
||||||
|
* When this field is non-NULL, it is prefixed to all lines of
|
||||||
|
* output on the stream. When this field is NULL, no prefix is
|
||||||
|
* added to each line of output in the stream.
|
||||||
|
*/
|
||||||
|
char *lds_prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to send stream output to stdout or not.
|
||||||
|
*
|
||||||
|
* If this field is true, stream output is sent to stdout.
|
||||||
|
*/
|
||||||
|
bool lds_want_stdout;
|
||||||
|
/**
|
||||||
|
* Whether to send stream output to stderr or not.
|
||||||
|
*
|
||||||
|
* If this field is true, stream output is sent to stderr.
|
||||||
|
*/
|
||||||
|
bool lds_want_stderr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to send stream output to a file or not.
|
||||||
|
*
|
||||||
|
* When this field is true, stream output is sent to a file, and the
|
||||||
|
* following fields are also examined:
|
||||||
|
*
|
||||||
|
* - lds_want_file_append
|
||||||
|
* - lda_file_suffix
|
||||||
|
*/
|
||||||
|
bool lds_want_file;
|
||||||
|
/**
|
||||||
|
* When lam_output_stream_t::lds_want_file is true, this field
|
||||||
|
* indicates whether to append the file (if it exists) or overwrite
|
||||||
|
* it.
|
||||||
|
*
|
||||||
|
* If false, the file is opened with the O_TRUNC flag.
|
||||||
|
*/
|
||||||
|
bool lds_want_file_append;
|
||||||
|
/**
|
||||||
|
* When lam_output_stream_t::lds_want_file is true, this field
|
||||||
|
* indicates the string suffix to add to the filename.
|
||||||
|
*
|
||||||
|
* The output file will be in the LAM session directory and have a
|
||||||
|
* LAM-generated prefix (generally "$sessiondir/lam-"). The suffix
|
||||||
|
* is intended to give stream users a chance to write their output
|
||||||
|
* into unique files. If this field is NULL, the suffix
|
||||||
|
* "output.txt" is used.
|
||||||
|
*/
|
||||||
|
char *lds_file_suffix;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct lam_output_stream_t lam_output_stream_t;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
bool lam_output_init(void);
|
||||||
|
void lam_output_finalize(void);
|
||||||
|
|
||||||
|
int lam_output_open(lam_output_stream_t *lds);
|
||||||
|
bool lam_output_switch(int output_id, bool enable);
|
||||||
|
void lam_output_reopen_all(void);
|
||||||
|
void lam_output_close(int output_id);
|
||||||
|
|
||||||
|
void lam_output(int output_id, char *format, ...);
|
||||||
|
void lam_output_verbose(int output_id, int verbose_level, char *format, ...);
|
||||||
|
void lam_output_set_verbosity(int output_id, int level);
|
||||||
|
|
||||||
|
#if LAM_ENABLE_DEBUG
|
||||||
|
/**
|
||||||
|
* Main macro for use in sending debugging output to output streams;
|
||||||
|
* will be "compiled out" when LAM is configured without
|
||||||
|
* --enable-debug.
|
||||||
|
*
|
||||||
|
* @see lam_output()
|
||||||
|
*/
|
||||||
|
#define LAM_OUTPUT(a) lam_output a
|
||||||
|
#else
|
||||||
|
/**
|
||||||
|
* Main macro for use in sending debugging output to output streams;
|
||||||
|
* will be "compiled out" when LAM is configured without
|
||||||
|
* --enable-debug.
|
||||||
|
*
|
||||||
|
* @see lam_output()
|
||||||
|
*/
|
||||||
|
#define LAM_OUTPUT(a)
|
||||||
|
#endif
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* LAM_OUTPUT_H_ */
|
||||||
|
|
Загрузка…
x
Ссылка в новой задаче
Block a user