📄 log.c
字号:
/** Add a log handler named <b>name</b> to send all messages in <b>severity</b>
* to <b>stream</b>. Copies <b>severity</b>. Helper: does no locking. */
static void
add_stream_log_impl(log_severity_list_t *severity,
const char *name, FILE *stream)
{
logfile_t *lf;
lf = tor_malloc_zero(sizeof(logfile_t));
lf->filename = tor_strdup(name);
lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
lf->file = stream;
lf->next = logfiles;
logfiles = lf;
_log_global_min_severity = get_min_log_level();
}
/** Add a log handler named <b>name</b> to send all messages in <b>severity</b>
* to <b>stream</b>. Steals a reference to <b>severity</b>; the caller must
* not use it after calling this function. */
void
add_stream_log(log_severity_list_t *severity,
const char *name, FILE *stream)
{
LOCK_LOGS();
add_stream_log_impl(severity, name, stream);
UNLOCK_LOGS();
}
/** Initialize the global logging facility */
void
init_logging(void)
{
if (!log_mutex)
log_mutex = tor_mutex_new();
}
/** Add a log handler to receive messages during startup (before the real
* logs are initialized).
*/
void
add_temp_log(int min_severity)
{
log_severity_list_t *s = tor_malloc_zero(sizeof(log_severity_list_t));
set_log_severity_config(min_severity, LOG_ERR, s);
LOCK_LOGS();
add_stream_log_impl(s, "<temp>", stdout);
tor_free(s);
logfiles->is_temporary = 1;
UNLOCK_LOGS();
}
/**
* Add a log handler to send messages in <b>severity</b>
* to the function <b>cb</b>.
*/
int
add_callback_log(log_severity_list_t *severity, log_callback cb)
{
logfile_t *lf;
lf = tor_malloc_zero(sizeof(logfile_t));
lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
lf->filename = tor_strdup("<callback>");
lf->callback = cb;
lf->next = logfiles;
LOCK_LOGS();
logfiles = lf;
_log_global_min_severity = get_min_log_level();
UNLOCK_LOGS();
return 0;
}
/** Adjust the configured severity of any logs whose callback function is
* <b>cb</b>. */
void
change_callback_log_severity(int loglevelMin, int loglevelMax,
log_callback cb)
{
logfile_t *lf;
log_severity_list_t severities;
set_log_severity_config(loglevelMin, loglevelMax, &severities);
LOCK_LOGS();
for (lf = logfiles; lf; lf = lf->next) {
if (lf->callback == cb) {
memcpy(lf->severities, &severities, sizeof(severities));
}
}
_log_global_min_severity = get_min_log_level();
UNLOCK_LOGS();
}
/** Close any log handlers added by add_temp_log() or marked by
* mark_logs_temp(). */
void
close_temp_logs(void)
{
logfile_t *lf, **p;
LOCK_LOGS();
for (p = &logfiles; *p; ) {
if ((*p)->is_temporary) {
lf = *p;
/* we use *p here to handle the edge case of the head of the list */
*p = (*p)->next;
close_log(lf);
log_free(lf);
} else {
p = &((*p)->next);
}
}
_log_global_min_severity = get_min_log_level();
UNLOCK_LOGS();
}
/** Make all currently temporary logs (set to be closed by close_temp_logs)
* live again, and close all non-temporary logs. */
void
rollback_log_changes(void)
{
logfile_t *lf;
LOCK_LOGS();
for (lf = logfiles; lf; lf = lf->next)
lf->is_temporary = ! lf->is_temporary;
UNLOCK_LOGS();
close_temp_logs();
}
/** Configure all log handles to be closed by close_temp_logs(). */
void
mark_logs_temp(void)
{
logfile_t *lf;
LOCK_LOGS();
for (lf = logfiles; lf; lf = lf->next)
lf->is_temporary = 1;
UNLOCK_LOGS();
}
/**
* Add a log handler to send messages to <b>filename</b>. If opening
* the logfile fails, -1 is returned and errno is set appropriately
* (by fopen).
*/
int
add_file_log(log_severity_list_t *severity, const char *filename)
{
FILE *f;
logfile_t *lf;
f = fopen(filename, "a");
if (!f) return -1;
LOCK_LOGS();
add_stream_log_impl(severity, filename, f);
logfiles->needs_close = 1;
lf = logfiles;
_log_global_min_severity = get_min_log_level();
UNLOCK_LOGS();
if (log_tor_version(lf, 0) < 0) {
LOCK_LOGS();
delete_log(lf);
UNLOCK_LOGS();
}
return 0;
}
#ifdef HAVE_SYSLOG_H
/**
* Add a log handler to send messages to they system log facility.
*/
int
add_syslog_log(log_severity_list_t *severity)
{
logfile_t *lf;
if (syslog_count++ == 0)
/* This is the first syslog. */
openlog("Tor", LOG_PID | LOG_NDELAY, LOGFACILITY);
lf = tor_malloc_zero(sizeof(logfile_t));
lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
lf->filename = tor_strdup("<syslog>");
lf->is_syslog = 1;
LOCK_LOGS();
lf->next = logfiles;
logfiles = lf;
_log_global_min_severity = get_min_log_level();
UNLOCK_LOGS();
return 0;
}
#endif
/** If <b>level</b> is a valid log severity, return the corresponding
* numeric value. Otherwise, return -1. */
int
parse_log_level(const char *level)
{
if (!strcasecmp(level, "err"))
return LOG_ERR;
if (!strcasecmp(level, "warn"))
return LOG_WARN;
if (!strcasecmp(level, "notice"))
return LOG_NOTICE;
if (!strcasecmp(level, "info"))
return LOG_INFO;
if (!strcasecmp(level, "debug"))
return LOG_DEBUG;
return -1;
}
/** Return the string equivalent of a given log level. */
const char *
log_level_to_string(int level)
{
return sev_to_string(level);
}
/** DOCDOC */
static const char *domain_list[] = {
"GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
"HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
"OR", "EDGE", "ACCT", NULL
};
/** DOCDOC */
static log_domain_mask_t
parse_log_domain(const char *domain)
{
int i;
for (i=0; domain_list[i]; ++i) {
if (!strcasecmp(domain, domain_list[i]))
return (1u<<i);
}
return 0;
}
#if 0
/** DOCDOC */
static const char *
domain_to_string(log_domain_mask_t domain)
{
int bit = tor_log2(domain);
if ((bit == 0 && domain == 0) || bit >= N_LOGGING_DOMAINS)
return NULL;
return domain_list[bit];
}
#endif
/** Parse a log severity pattern in *<b>cfg_ptr</b>. Advance cfg_ptr after
* the end of the severityPattern. Set the value of <b>severity_out</b> to
* the parsed pattern. Return 0 on success, -1 on failure.
*
* The syntax for a SeverityPattern is:
* <pre>
* SeverityPattern = *(DomainSeverity SP)* DomainSeverity
* DomainSeverity = (DomainList SP)? SeverityRange
* SeverityRange = MinSeverity ("-" MaxSeverity )?
* DomainList = "[" (SP? DomainSpec SP? ",") SP? DomainSpec "]"
* DomainSpec = "*" | Domain | "~" Domain
* </pre>
* A missing MaxSeverity defaults to ERR. Severities and domains are
* case-insensitive. "~" indicates negation for a domain; negation happens
* last inside a DomainList. Only one SeverityRange without a DomainList is
* allowed per line.
*/
int
parse_log_severity_config(const char **cfg_ptr,
log_severity_list_t *severity_out)
{
const char *cfg = *cfg_ptr;
int got_anything = 0;
int got_an_unqualified_range = 0;
memset(severity_out, 0, sizeof(*severity_out));
cfg = eat_whitespace(cfg);
while (*cfg) {
const char *dash, *space;
char *sev_lo, *sev_hi;
int low, high, i;
log_domain_mask_t domains = ~0u;
if (*cfg == '[') {
int err = 0;
char *domains_str;
smartlist_t *domains_list;
log_domain_mask_t neg_domains = 0;
const char *closebracket = strchr(cfg, ']');
if (!closebracket)
return -1;
domains = 0;
domains_str = tor_strndup(cfg+1, closebracket-cfg-1);
domains_list = smartlist_create();
smartlist_split_string(domains_list, domains_str, ",", SPLIT_SKIP_SPACE,
-1);
tor_free(domains_str);
SMARTLIST_FOREACH(domains_list, const char *, domain,
{
if (!strcmp(domain, "*")) {
domains = ~0u;
} else {
int d;
int negate=0;
if (*domain == '~') {
negate = 1;
++domain;
}
d = parse_log_domain(domain);
if (!d) {
log_warn(LD_CONFIG, "No such loggging domain as %s", domain);
err = 1;
} else {
if (negate)
neg_domains |= d;
else
domains |= d;
}
}
});
SMARTLIST_FOREACH(domains_list, char *, d, tor_free(d));
smartlist_free(domains_list);
if (err)
return -1;
domains &= ~neg_domains;
cfg = eat_whitespace(closebracket+1);
} else {
++got_an_unqualified_range;
}
if (!strcasecmpstart(cfg, "file") ||
!strcasecmpstart(cfg, "stderr") ||
!strcasecmpstart(cfg, "stdout") ||
!strcasecmpstart(cfg, "syslog")) {
goto done;
}
if (got_an_unqualified_range > 1)
return -1;
space = strchr(cfg, ' ');
dash = strchr(cfg, '-');
if (!space)
space = strchr(cfg, '\0');
if (dash && dash < space) {
sev_lo = tor_strndup(cfg, dash-cfg);
sev_hi = tor_strndup(dash+1, space-(dash+1));
} else {
sev_lo = tor_strndup(cfg, space-cfg);
sev_hi = tor_strdup("ERR");
}
low = parse_log_level(sev_lo);
high = parse_log_level(sev_hi);
tor_free(sev_lo);
tor_free(sev_hi);
if (low == -1)
return -1;
if (high == -1)
return -1;
got_anything = 1;
for (i=low; i >= high; --i)
severity_out->masks[SEVERITY_MASK_IDX(i)] |= domains;
cfg = eat_whitespace(space);
}
done:
*cfg_ptr = cfg;
return got_anything ? 0 : -1;
}
/** Return the least severe log level that any current log is interested in. */
int
get_min_log_level(void)
{
logfile_t *lf;
int i;
int min = LOG_ERR;
for (lf = logfiles; lf; lf = lf->next) {
for (i = LOG_DEBUG; i > min; --i)
if (lf->severities->masks[SEVERITY_MASK_IDX(i)])
min = i;
}
return min;
}
/** Switch all logs to output at most verbose level. */
void
switch_logs_debug(void)
{
logfile_t *lf;
int i;
LOCK_LOGS();
for (lf = logfiles; lf; lf=lf->next) {
for (i = LOG_DEBUG; i >= LOG_ERR; --i)
lf->severities->masks[SEVERITY_MASK_IDX(i)] = ~0u;
}
UNLOCK_LOGS();
}
#ifdef HAVE_EVENT_SET_LOG_CALLBACK
/** A string which, if it appears in a libevent log, should be ignored. */
static const char *suppress_msg = NULL;
/** Callback function passed to event_set_log() so we can intercept
* log messages from libevent. */
static void
libevent_logging_callback(int severity, const char *msg)
{
char buf[1024];
size_t n;
if (suppress_msg && strstr(msg, suppress_msg))
return;
n = strlcpy(buf, msg, sizeof(buf));
if (n && n < sizeof(buf) && buf[n-1] == '\n') {
buf[n-1] = '\0';
}
switch (severity) {
case _EVENT_LOG_DEBUG:
log(LOG_DEBUG, LD_NET, "Message from libevent: %s", buf);
break;
case _EVENT_LOG_MSG:
log(LOG_INFO, LD_NET, "Message from libevent: %s", buf);
break;
case _EVENT_LOG_WARN:
log(LOG_WARN, LD_GENERAL, "Warning from libevent: %s", buf);
break;
case _EVENT_LOG_ERR:
log(LOG_ERR, LD_GENERAL, "Error from libevent: %s", buf);
break;
default:
log(LOG_WARN, LD_GENERAL, "Message [%d] from libevent: %s",
severity, buf);
break;
}
}
/** Set hook to intercept log messages from libevent. */
void
configure_libevent_logging(void)
{
event_set_log_callback(libevent_logging_callback);
}
/** Ignore any libevent log message that contains <b>msg</b>. */
void
suppress_libevent_log_msg(const char *msg)
{
suppress_msg = msg;
}
#else
void
configure_libevent_logging(void)
{
}
void
suppress_libevent_log_msg(const char *msg)
{
(void)msg;
}
#endif
#if 0
static void
dump_log_info(logfile_t *lf)
{
const char *tp;
if (lf->filename) {
printf("=== log into \"%s\" (%s-%s) (%stemporary)\n", lf->filename,
sev_to_string(lf->min_loglevel),
sev_to_string(lf->max_loglevel),
lf->is_temporary?"":"not ");
} else if (lf->is_syslog) {
printf("=== syslog (%s-%s) (%stemporary)\n",
sev_to_string(lf->min_loglevel),
sev_to_string(lf->max_loglevel),
lf->is_temporary?"":"not ");
} else {
printf("=== log (%s-%s) (%stemporary)\n",
sev_to_string(lf->min_loglevel),
sev_to_string(lf->max_loglevel),
lf->is_temporary?"":"not ");
}
}
void
describe_logs(void)
{
logfile_t *lf;
printf("==== BEGIN LOGS ====\n");
for (lf = logfiles; lf; lf = lf->next)
dump_log_info(lf);
printf("==== END LOGS ====\n");
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -