2006-01-08 11:51:29 -06:00
|
|
|
/* **************************************************************************
|
|
|
|
* qoflog.c
|
|
|
|
*
|
|
|
|
* Mon Nov 21 14:41:59 2005
|
2006-02-05 06:25:45 -06:00
|
|
|
* Author: Rob Clark (rclark@cs.hmc.edu)
|
2006-01-08 11:51:29 -06:00
|
|
|
* Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org>
|
|
|
|
* Copyright 2005 Neil Williams
|
|
|
|
* linux@codehelp.co.uk
|
|
|
|
*************************************************************************** */
|
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
* 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#else
|
|
|
|
#warning unistd required.
|
|
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include "qof.h"
|
|
|
|
#include "qoflog.h"
|
|
|
|
|
|
|
|
#define QOF_LOG_MAX_CHARS 50
|
|
|
|
#define QOF_LOG_INDENT_WIDTH 4
|
|
|
|
#define NUM_CLOCKS 10
|
|
|
|
|
|
|
|
static FILE *fout = NULL;
|
|
|
|
static gchar* filename = NULL;
|
|
|
|
static gchar* function_buffer = NULL;
|
|
|
|
static const int MAX_TRACE_FILENAME = 100;
|
|
|
|
static GHashTable *log_table = NULL;
|
|
|
|
static gint qof_log_num_spaces = 0;
|
|
|
|
|
|
|
|
/* uses the enum_as_string macro.
|
|
|
|
Lookups are done on the string. */
|
|
|
|
AS_STRING_FUNC(QofLogLevel, LOG_LEVEL_LIST)
|
|
|
|
|
|
|
|
FROM_STRING_FUNC(QofLogLevel, LOG_LEVEL_LIST)
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_log_add_indent(void)
|
|
|
|
{
|
|
|
|
qof_log_num_spaces += QOF_LOG_INDENT_WIDTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
gint
|
|
|
|
qof_log_get_indent(void)
|
|
|
|
{
|
|
|
|
return qof_log_num_spaces;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_log_drop_indent(void)
|
|
|
|
{
|
2006-02-05 06:25:45 -06:00
|
|
|
qof_log_num_spaces = (qof_log_num_spaces < QOF_LOG_INDENT_WIDTH) ?
|
|
|
|
0 : qof_log_num_spaces - QOF_LOG_INDENT_WIDTH;
|
2006-01-08 11:51:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fh_printer (const gchar *log_domain,
|
|
|
|
GLogLevelFlags log_level,
|
|
|
|
const gchar *message,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
FILE *fh = user_data;
|
|
|
|
fprintf (fh, "%*s%s\n", qof_log_num_spaces, "", message);
|
|
|
|
fflush(fh);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_log_init (void)
|
|
|
|
{
|
|
|
|
if(!fout) /* allow qof_log_set_file */
|
|
|
|
{
|
|
|
|
fout = fopen ("/tmp/qof.trace", "w");
|
|
|
|
}
|
|
|
|
|
2006-02-05 06:25:45 -06:00
|
|
|
if(!fout && (filename = (gchar *)g_malloc(MAX_TRACE_FILENAME))) {
|
2006-01-08 11:51:29 -06:00
|
|
|
snprintf(filename, MAX_TRACE_FILENAME-1, "/tmp/qof.trace.%d",
|
|
|
|
getpid());
|
|
|
|
fout = fopen (filename, "w");
|
|
|
|
g_free(filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!fout)
|
|
|
|
fout = stderr;
|
|
|
|
|
|
|
|
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, fh_printer, fout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_log_set_level(QofLogModule log_module, QofLogLevel level)
|
|
|
|
{
|
|
|
|
gchar* level_string;
|
|
|
|
|
|
|
|
if(!log_module || level == 0) { return; }
|
|
|
|
level_string = g_strdup(QofLogLevelasString(level));
|
|
|
|
if(!log_table)
|
|
|
|
{
|
|
|
|
log_table = g_hash_table_new(g_str_hash, g_str_equal);
|
|
|
|
}
|
|
|
|
g_hash_table_insert(log_table, (gpointer)log_module, level_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
log_module_foreach(gpointer key, gpointer value, gpointer data)
|
|
|
|
{
|
|
|
|
g_hash_table_insert(log_table, key, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-02-05 06:25:45 -06:00
|
|
|
qof_log_set_level_registered(QofLogLevel level)
|
2006-01-08 11:51:29 -06:00
|
|
|
{
|
|
|
|
gchar* level_string;
|
|
|
|
|
|
|
|
if(!log_table || level == 0) { return; }
|
|
|
|
level_string = g_strdup(QofLogLevelasString(level));
|
|
|
|
g_hash_table_foreach(log_table, log_module_foreach, level_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_log_set_file (FILE *outfile)
|
|
|
|
{
|
|
|
|
if(!outfile) { fout = stderr; return; }
|
|
|
|
fout = outfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_log_init_filename (const gchar* logfilename)
|
|
|
|
{
|
|
|
|
if(!logfilename)
|
|
|
|
{
|
|
|
|
fout = stderr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
filename = g_strdup(logfilename);
|
|
|
|
fout = fopen(filename, "w");
|
|
|
|
}
|
|
|
|
qof_log_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_log_shutdown (void)
|
|
|
|
{
|
|
|
|
if(fout && fout != stderr) { fclose(fout); }
|
|
|
|
if(filename) { g_free(filename); }
|
|
|
|
if(function_buffer) { g_free(function_buffer); }
|
|
|
|
g_hash_table_destroy(log_table);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
qof_log_prettify (const char *name)
|
|
|
|
{
|
2006-02-05 06:25:45 -06:00
|
|
|
gchar *p, *buffer;
|
2006-01-08 11:51:29 -06:00
|
|
|
gint length;
|
|
|
|
|
|
|
|
if (!name) { return ""; }
|
|
|
|
buffer = g_strndup(name, QOF_LOG_MAX_CHARS - 1);
|
|
|
|
length = strlen(buffer);
|
|
|
|
p = g_strstr_len(buffer, length, "(");
|
|
|
|
if (p)
|
|
|
|
{
|
|
|
|
*(p+1) = ')';
|
|
|
|
*(p+2) = 0x0;
|
|
|
|
}
|
|
|
|
else { strcpy (&buffer[QOF_LOG_MAX_CHARS - 4], "...()"); }
|
|
|
|
function_buffer = g_strdup(buffer);
|
|
|
|
g_free(buffer);
|
|
|
|
return function_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
struct timeval qof_clock[NUM_CLOCKS] = {
|
|
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
|
|
};
|
|
|
|
|
|
|
|
static
|
|
|
|
struct timeval qof_clock_total[NUM_CLOCKS] = {
|
|
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_start_clock (int clockno, QofLogModule log_module, QofLogLevel log_level,
|
2006-02-05 06:25:45 -06:00
|
|
|
const gchar *function_name, const gchar *format, ...)
|
2006-01-08 11:51:29 -06:00
|
|
|
{
|
|
|
|
struct timezone tz;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if ((0>clockno) || (NUM_CLOCKS <= clockno)) return;
|
|
|
|
gettimeofday (&qof_clock[clockno], &tz);
|
|
|
|
|
|
|
|
if (!fout) qof_log_init();
|
|
|
|
|
|
|
|
fprintf (fout, "Clock %d Start: %s: ",
|
|
|
|
clockno, qof_log_prettify (function_name));
|
|
|
|
|
|
|
|
va_start (ap, format);
|
|
|
|
|
|
|
|
vfprintf (fout, format, ap);
|
|
|
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
fprintf (fout, "\n");
|
|
|
|
fflush (fout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-02-05 06:25:45 -06:00
|
|
|
qof_report_clock (gint clockno, QofLogModule log_module, QofLogLevel log_level,
|
|
|
|
const gchar *function_name, const gchar *format, ...)
|
2006-01-08 11:51:29 -06:00
|
|
|
{
|
|
|
|
struct timezone tz;
|
|
|
|
struct timeval now;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if ((0>clockno) || (NUM_CLOCKS <= clockno)) return;
|
|
|
|
gettimeofday (&now, &tz);
|
|
|
|
|
|
|
|
/* need to borrow to make difference */
|
|
|
|
if (now.tv_usec < qof_clock[clockno].tv_usec)
|
|
|
|
{
|
|
|
|
now.tv_sec --;
|
|
|
|
now.tv_usec += 1000000;
|
|
|
|
}
|
|
|
|
now.tv_sec -= qof_clock[clockno].tv_sec;
|
|
|
|
now.tv_usec -= qof_clock[clockno].tv_usec;
|
|
|
|
|
|
|
|
qof_clock_total[clockno].tv_sec += now.tv_sec;
|
|
|
|
qof_clock_total[clockno].tv_usec += now.tv_usec;
|
|
|
|
|
|
|
|
if (!fout) qof_log_init();
|
|
|
|
|
|
|
|
fprintf (fout, "Clock %d Elapsed: %ld.%06lds %s: ",
|
|
|
|
clockno, (long int) now.tv_sec, (long int) now.tv_usec,
|
|
|
|
qof_log_prettify (function_name));
|
|
|
|
|
|
|
|
va_start (ap, format);
|
|
|
|
|
|
|
|
vfprintf (fout, format, ap);
|
|
|
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
fprintf (fout, "\n");
|
|
|
|
fflush (fout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-02-05 06:25:45 -06:00
|
|
|
qof_report_clock_total (gint clockno,
|
2006-01-08 11:51:29 -06:00
|
|
|
QofLogModule log_module, QofLogLevel log_level,
|
2006-02-05 06:25:45 -06:00
|
|
|
const gchar *function_name, const gchar *format, ...)
|
2006-01-08 11:51:29 -06:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if ((0>clockno) || (NUM_CLOCKS <= clockno)) return;
|
|
|
|
|
|
|
|
/* need to normalize usec */
|
|
|
|
while (qof_clock_total[clockno].tv_usec >= 1000000)
|
|
|
|
{
|
|
|
|
qof_clock_total[clockno].tv_sec ++;
|
|
|
|
qof_clock_total[clockno].tv_usec -= 1000000;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fout) qof_log_init();
|
|
|
|
|
|
|
|
fprintf (fout, "Clock %d Total Elapsed: %ld.%06lds %s: ",
|
|
|
|
clockno,
|
|
|
|
(long int) qof_clock_total[clockno].tv_sec,
|
|
|
|
(long int) qof_clock_total[clockno].tv_usec,
|
|
|
|
qof_log_prettify (function_name));
|
|
|
|
|
|
|
|
va_start (ap, format);
|
|
|
|
|
|
|
|
vfprintf (fout, format, ap);
|
|
|
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
fprintf (fout, "\n");
|
|
|
|
fflush (fout);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
qof_log_check(QofLogModule log_module, QofLogLevel log_level)
|
|
|
|
{
|
|
|
|
gchar* log_string;
|
2006-02-05 06:25:45 -06:00
|
|
|
QofLogLevel maximum; /* Any positive log_level less than this will be logged. */
|
2006-01-08 11:51:29 -06:00
|
|
|
|
|
|
|
log_string = NULL;
|
2006-02-05 06:25:45 -06:00
|
|
|
if (log_level > QOF_LOG_TRACE) log_level = QOF_LOG_TRACE;
|
|
|
|
if(!log_table || log_module == NULL || log_level < 0) { return FALSE; }
|
2006-01-08 11:51:29 -06:00
|
|
|
log_string = (gchar*)g_hash_table_lookup(log_table, log_module);
|
2006-01-29 11:49:35 -06:00
|
|
|
/* if log_module not found, do not log. */
|
|
|
|
if(!log_string) { return FALSE; }
|
|
|
|
maximum = QofLogLevelfromString(log_string);
|
2006-01-08 11:51:29 -06:00
|
|
|
if(log_level <= maximum) { return TRUE; }
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qof_log_set_default(QofLogLevel log_level)
|
|
|
|
{
|
|
|
|
qof_log_set_level(QOF_MOD_BACKEND, log_level);
|
|
|
|
qof_log_set_level(QOF_MOD_CLASS, log_level);
|
|
|
|
qof_log_set_level(QOF_MOD_ENGINE, log_level);
|
|
|
|
qof_log_set_level(QOF_MOD_OBJECT, log_level);
|
|
|
|
qof_log_set_level(QOF_MOD_KVP, log_level);
|
|
|
|
qof_log_set_level(QOF_MOD_MERGE, log_level);
|
|
|
|
qof_log_set_level(QOF_MOD_QUERY, log_level);
|
|
|
|
qof_log_set_level(QOF_MOD_SESSION, log_level);
|
2006-02-05 04:58:13 -06:00
|
|
|
qof_log_set_level(QOF_MOD_CHOICE, log_level);
|
2006-01-08 11:51:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
struct hash_s
|
|
|
|
{
|
|
|
|
QofLogCB cb;
|
|
|
|
gpointer data;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void hash_cb (gpointer key, gpointer value, gpointer data)
|
|
|
|
{
|
|
|
|
struct hash_s *iter;
|
|
|
|
|
|
|
|
iter = (struct hash_s*)data;
|
|
|
|
if(!iter) { return; }
|
|
|
|
(iter->cb)(key, value, iter->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void qof_log_module_foreach(QofLogCB cb, gpointer data)
|
|
|
|
{
|
|
|
|
struct hash_s iter;
|
|
|
|
|
|
|
|
if(!cb) { return; }
|
|
|
|
iter.cb = cb;
|
|
|
|
iter.data = data;
|
|
|
|
g_hash_table_foreach(log_table, hash_cb, (gpointer)&iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
gint qof_log_module_count(void)
|
|
|
|
{
|
|
|
|
if(!log_table) { return 0; }
|
|
|
|
return g_hash_table_size(log_table);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************ END OF FILE **************************** */
|