📄 rvlog.c
字号:
#if (0)
************************************************************************
Filename:
Description:
************************************************************************
Copyright (c) 1999 RADVision Inc.
************************************************************************
NOTICE:
This document contains information that is proprietary to RADVision LTD.
No part of this publication may be reproduced in any form whatsoever
without written prior approval by RADVision LTD..
RADVision LTD. reserves the right to revise this publication and make
changes without obligation to notify any person of such revisions or
changes.
************************************************************************
************************************************************************
$Revision:$
$Date:$
$Author: S. Cipolli$
************************************************************************
#endif
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include "rvstr.h"
#include "rvint.h"
#include "rvptrlist.h"
#include "rvmem.h"
#include "rvdefalloc.h"
#include "rvthread_.h"
#include "rvmutex_.h"
#include "rvlog.h"
RvLog rvLog;
#if (RV_LOGMASK != RV_LOGMASK_NONE)
const char* rvLoglevelString[] = {
"", "FATAL", "ERROR", "WARNING",
"SEND", "RECEIVE", "ENTER", "LEAVE",
"INFO1", "INFO2", "INFO3", "INFO4",
"UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"
};
static const char xlate[] = { 0, 1, 2, 0, 3, 0, 0, 0, 4 , 0, 0, 0, 0, 0, 0};
/* Internal logging functions */
void rvLogInt_(RvLog* l, RvLogLevel level, const char* name, int value, int radix) {
char buf[64];
char* p;
p = rvStrCopy(buf, name);
p = rvStrCopy(p, "=");
rvIntToStr(value, p, radix);
rvLogText_(l, level, buf);
}
void rvLogStr_(RvLog* l, RvLogLevel level, const char* name, const char* value) {
char buf[128];
char* p;
assert((strlen(name) + strlen(value) + 1) < 128);
p = rvStrCopy(buf, name);
p = rvStrCopy(p, "=");
p = rvStrCopy(p, value);
rvLogText_(l, level, buf);
}
void rvLogText_(RvLog* l, RvLogLevel level, const char* text) {
RvLogRecord rec;
RvPtrListIter i;
RvBool constructed = rvFalse;
/* If log mask matches and there are listeners */
if ((l->mask & level) && rvPtrListSize(&l->listeners)) {
rvMutexLock_(&l->mutex);
/* Send log message to each listener */
for (i = rvPtrListBegin(&l->listeners);
i != rvPtrListEnd(&l->listeners);
i = rvPtrListIterNext(i)) {
RvLogListener* listener;
listener = (RvLogListener*)rvPtrListIterData(i);
/* If listeners mask matches level, send to listener */
if (listener->mask & level) {
/* If no other listener has been triggered yet... */
if (!constructed) {
constructed = rvTrue;
/* Construct log record */
rvLogRecordConstruct(&rec, level, text);
}
listener->output(&rec, listener->data);
}
}
rvMutexUnlock_(&l->mutex);
}
}
/* Public */
/*$
{function:
{name: rvLogConstruct }
{class: RvLog}
{include: rvlog.h}
{description:
{p: Construct an instance of a log.}
}
{proto: RvLog* rvLogConstruct(RvLog* l);}
{params:
{param: {n: l} {d: A pointer to the log.}}
}
{returns: A pointer to the constructed log or NULL on error. }
}
$*/
RvLog* rvLogConstruct(RvLog* l) {
rvMutexConstruct_(&l->mutex);
l->mask = RV_LOGMASKDEFAULT;
rvPtrListConstruct(&l->listeners, &rvDefaultAlloc);
return l;
}
/*$
{function:
{name: rvLogDestruct }
{class: RvLog}
{include: rvlog.h}
{description:
{p: Destruct an instance of a log.}
}
{proto: void rvLogDestruct(RvLog* l);}
{params:
{param: {n: l} {d: A pointer to the log.}}
}
}
$*/
void rvLogDestruct(RvLog* l) {
rvMutexDestruct_(&l->mutex);
rvPtrListDestruct(&l->listeners);
}
/*$
{function:
{name: rvLogSetMask }
{class: RvLog}
{include: rvlog.h}
{description:
{p: Set the log mask of the log.}
}
{proto: RvLogMask rvLogSetMask(RvLog* l, RvLogMask mask);}
{params:
{param: {n: l} {d: A pointer to the log.}}
{param: {n: mask} {d: The log mask}}
}
{returns: The old log mask of the log. }
}
$*/
RvLogMask rvLogSetMask(RvLog* l, RvLogMask mask) {
RvLogMask old = l->mask;
l->mask = mask;
return old;
}
RvLogRecord* rvLogRecordConstruct(RvLogRecord* r, RvLogLevel level, const char* text) {
rvTimeGetEpochTime(&r->timestamp);
strcpy(r->threadName, rvThreadName_(rvThreadCurrent_()));
r->level = level;
r->text = text;
return r;
}
/*$
{function:
{name: rvLogRecordGetTimeAsString }
{class: RvLogRecord}
{include: rvlog.h}
{description:
{p: Get the timestamp of a log record as string in GMT.}
}
{proto: const char* rvLogRecordGetTimeAsString(const RvLogRecord* r, char* buf);}
{params:
{param: {n: r} {d: A pointer to the log record.}}
{param: {n: buf} {d: A pointer to a buffer to hold the time as string.}}
}
{returns: The timestamp of the log record as a string. }
}
$*/
const char* rvLogRecordGetTimeAsString(const RvLogRecord* lr, OUT char* buf) {
RvTm utcTime;
int strsize;
rvTimeUTC((RvTime)rvTimespecSecs(&lr->timestamp), &utcTime);
strsize = rvTimeStrTm(buf, RV_LOG_TIMESTAMP_BUFSIZE,
"%b %d %Y %H:%M:%S.", &utcTime);
sprintf(&(buf[strsize]), "%03ld",
(rvTimespecNsecs(&lr->timestamp) / RV_TIME_NSECPERMSEC));
return buf;
}
/*$
{function:
{name: rvLogRecordGetLevelString}
{class: RvLogRecord}
{include: rvlog.h}
{description:
{p: Get the log level of a log record as a string.}
}
{proto: const char *rvLogRecordGetLevelString(const RvLogRecord *rec);}
{params:
{param: {n: rec} {d: A pointer to the log record.}}
}
{returns: The log level of the log record as a string. }
}
$*/
const char *rvLogRecordGetLevelString(const RvLogRecord *rec) {
RvLogLevel level = rvLogRecordGetLevel(rec);
int levelIndex = 0;
int i;
/* Read a nibble at a time and map the level bit to an index */
for (i = 0; (i < (sizeof(RvLogLevel) * CHAR_BIT)) && (levelIndex == 0); ++i) {
levelIndex = xlate[level & 0x0000000f];
level >>= 4;
}
return rvLoglevelString[levelIndex + ((i - 1) * 4)];
}
/*$
{function:
{name: rvLogRegisterListener }
{class: RvLog}
{include: rvlog.h}
{description:
{p: Register a log listener with the log. A log listener allows an application
to specify how to manage log records. }
}
{proto: void rvLogRegisterListener(RvLog* l, RvLogListener* li);}
{params:
{param: {n: l} {d: A pointer to the log.}}
{param: {n: li} {d: The log listener to register.}}
}
}
$*/
void rvLogRegisterListener(RvLog* l, RvLogListener* li) {
rvMutexLock_(&l->mutex);
rvPtrListPushBack(&l->listeners, li);
rvMutexUnlock_(&l->mutex);
}
/*$
{function:
{name: rvLogUnregisterListener }
{class: RvLog}
{include: rvlog.h}
{description:
{p: Unregister a log listener with the log. A log listener allows an application
to specify how to manage log records. }
}
{proto: void rvLogUnregisterListener(RvLog* l, RvLogListener* li);}
{params:
{param: {n: l} {d: A pointer to the log.}}
{param: {n: li} {d: The log listener to unregister.}}
}
}
$*/
void rvLogUnregisterListener(RvLog* l, RvLogListener* li) {
rvMutexLock_(&l->mutex);
rvPtrListRemove(&l->listeners, li);
rvMutexUnlock_(&l->mutex);
}
/*$
{function:
{name: rvLogListenerConstruct }
{class: RvLogListener}
{include: rvlog.h}
{description:
{p: Construct an instance of a log listener. Log listeners allow the application
to specify how log records are captured and managed. The listener specifies a log
mask which filters what log records the listener will recognize and an output
function callback which is called with each recognized log record allowing the
application to customize how the log record is processed. }
}
{proto: RvLogListener* rvLogListenerConstruct(RvLogListener* li, RvLogMask mask,
void (*output)(const RvLogRecord*, void*), void* data); }
{params:
{param: {n: li} {d: The log listener. }}
{param: {n: mask} {d: The log mask. The mask is a bit mask that determines
what log records will be handled by the listener}}
{param: {n: output} {d: The output function. The output function is called for every
log record meeting the listeners mask. This function typically provides some mechanism
to record the log record. }}
{param: {n: data} {d: A point to user defined data passed to the output function when
a log record is processed by the listener.}}
}
{returns: A pointer to the constructed log listener or NULL if it could not be constructed.}
}
$*/
RvLogListener* rvLogListenerConstruct(RvLogListener* l,
RvLogMask mask, void (*output)(const RvLogRecord*, void*), void* data) {
l->mask = mask;
l->output = output;
l->data = data;
return l;
}
/*$
{function:
{name: rvLogListenerSetMask }
{class: RvLogListener}
{include: rvlog.h}
{description:
{p: Set the log mask of the listener.}
}
{proto: RvLogMask rvLogListenerSetMask(RvLogListener* li, RvLogMask mask);}
{params:
{param: {n: li} {d: A pointer to the log listener.}}
{param: {n: mask} {d: The log mask. The mask is a bit mask that determines
what log records will be handled by the listener}}
}
{returns: The old log mask of the listener. }
}
$*/
RvLogMask rvLogListenerSetMask(RvLogListener* l, RvLogMask mask)
{
RvLogMask old = l->mask;
l->mask = mask;
return old;
}
/*$
{function:
{name: rvLogListenerGetMask }
{class: RvLogListener}
{include: rvlog.h}
{description:
{p: Get the log mask of the listener.}
}
{proto: RvLogMask rvLogListenerGetMask(RvLogListener* li);}
{params:
{param: {n: li} {d: A pointer to the log listener.}}
}
{returns: The current log mask of the listener. }
}
$*/
RvLogMask rvLogListenerGetMask(RvLogListener* l)
{
return l->mask;
}
/* Various standard listeners */
/*$
{function:
{name: rvLogStdio }
{superpackage: Kernel}
{include: rvlog.h}
{description:
{p: Standard log listener output function. Log to file descriptor passed as
"user data". This listener is typically used to log to standard out (stdout)
or standard error (stderr), but others are possible.}
}
{proto: void rvLogStdio(const RvLogRecord* rec, void* data);}
{params:
{param: {n: rec} {d: A pointer to the log record to output.}}
{param: {n: data} {d: User data which is interpreted as a FILE pointer.
A default file descriptor (usually stderr) is used if this parameter is NULL. }}
}
{examples:
{example:
{description:
{p: This example shows how to use this log output funtion to listen to
the global log and output to standard error. The listener filter is
set to listen to errors and warnings.}
}
{code:
#include "rvlog.h"
RvLogListener listener;
rvLogListenerConstruct(&listener,RV_LOGLEVEL_ERROR|RV_LOGLEVEL_WARNING, rvLogStdio,(void*)stderr);
rvLogRegisterListener(&rvLog,&listener);
}
}
}
{see_also:
{n: RvLogListener}
{n: void rvLogFile(const RvLogRecord* rec, void* data);}
}
}
$*/
void rvLogStdio(const RvLogRecord* rec, void* data) {
#if defined(RV_IO_ANSI)
char timeString[RV_LOG_TIMESTAMP_BUFSIZE];
FILE* fp = (FILE*)data;
if (fp == NULL) fp = stderr;
fprintf(fp, "%s|%s|%s|%s\n",
rvLogRecordGetTimeAsString(rec, timeString),
rvLogRecordGetThreadName(rec),
rvLogRecordGetLevelString(rec),
rvLogRecordGetText(rec));
fflush(fp);
#endif
}
/*$
{function:
{name: rvLogFile}
{superpackage: Kernel}
{include: rvlog.h}
{description:
{p: Standard log listener output function. Log to double buffered files. The log
records are written to the first file (rv1.txt) until N records are written at which
point subsequent records are written to the second file (rv2.txt). After N records
have been written to the second file, the process is repeated starting back at the
first file. The "user data" pointer is assumed to be an integer indicating the number
of lines (N) to be written into each file.}
{p: The purpose of the double buffering is to allow ongoing logging without fear of
exhausting the disk space.}
}
{proto: void rvLogFile(const RvLogRecord* rec, void* data);}
{params:
{param: {n: rec} {d: A pointer to the log record to output.}}
{param: {n: data} {d: User data which is interpreted as an integer num of lines to
store per file.}}
}
{examples:
{example:
{description:
{p: This example shows how to use this log output funtion to listen to
the global log and output to files with a maximum of 512 lines.
The listener filter is set to listen to errors and warnings.}
}
{code:
#include "rvlog.h"
RvLogListener listener;
rvLogListenerConstruct(&listener,RV_LOGLEVEL_ERROR|RV_LOGLEVEL_WARNING, rvLogFile,(void*)512);
rvLogRegisterListener(&rvLog,&listener);
}
}
}
{see_also:
{n: RvLogListener}
{n: void rvLogStdio(const RvLogRecord* rec, void* data);}
}
}
$*/
void rvLogFile(const RvLogRecord* rec, void* data) {
#if defined(RV_FILEIO_ANSI)
static FILE* fp = NULL;
static int count = 0;
static int toggle = 0;
static RvBool enabled = rvTrue;
static const char* name[] = { "rv1.txt", "rv2.txt" };
int lines = (int)data;
if (!enabled)
return;
if (fp == NULL)
fp = fopen(name[toggle], "w");
if (fp == NULL) /* if we can't open the file then */
{
enabled = rvFalse; /* set flag so that we don't keep trying */
return;
}
rvLogStdio(rec, fp);
count++;
if (count == lines) {
fclose(fp);
fp = NULL;
count = 0;
toggle = (toggle) ? 0 : 1;
}
#endif
}
#else
char* rvNullString = "";
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -