2 * Copyright (C) 2007 Mark Pustjens <pustjens@dds.nl>
3 * Copyright (C) 2010-2015 Petr Pavlu <setup@dagobah.cz>
5 * This file is part of CenterIM.
7 * CenterIM is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * CenterIM is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include <cppconsui/HorizontalListBox.h>
25 #include <cppconsui/Spacer.h>
29 Log
*Log::my_instance
= NULL
;
36 #define WRITE_METHOD(name, level) \
37 void Log::name(const char *fmt, ...) \
42 if (log_level_cim < level) \
43 return; /* we don't want to see this log message */ \
45 va_start(args, fmt); \
46 text = g_strdup_vprintf(fmt, args); \
49 write(TYPE_CIM, level, text); \
53 WRITE_METHOD(error
, LEVEL_ERROR
)
54 WRITE_METHOD(critical
, LEVEL_CRITICAL
)
55 WRITE_METHOD(warning
, LEVEL_WARNING
)
56 WRITE_METHOD(message
, LEVEL_MESSAGE
)
57 WRITE_METHOD(info
, LEVEL_INFO
)
58 WRITE_METHOD(debug
, LEVEL_DEBUG
)
62 Log::LogWindow::LogWindow() : Window(0, 0, 80, 24, NULL
, TYPE_NON_FOCUSABLE
)
64 setColorScheme("log");
66 CppConsUI::HorizontalListBox
*lbox
=
67 new CppConsUI::HorizontalListBox(AUTOSIZE
, AUTOSIZE
);
68 addWidget(*lbox
, 1, 1);
70 lbox
->appendWidget(*(new CppConsUI::Spacer(1, AUTOSIZE
)));
71 textview
= new CppConsUI::TextView(AUTOSIZE
, AUTOSIZE
, true);
72 lbox
->appendWidget(*textview
);
73 lbox
->appendWidget(*(new CppConsUI::Spacer(1, AUTOSIZE
)));
78 void Log::LogWindow::onScreenResized()
80 moveResizeRect(CENTERIM
->getScreenArea(CenterIM::LOG_AREA
));
83 void Log::LogWindow::append(const char *text
)
85 textview
->append(text
);
87 // shorten the window text
88 size_t lines_num
= textview
->getLinesNumber();
90 if (lines_num
> 200) {
91 // remove 40 extra lines
92 textview
->erase(0, lines_num
- 200 + 40);
96 Log::LogBufferItem::LogBufferItem(Type type_
, Level level_
, const char *text_
)
97 : type(type_
), level(level_
)
99 text
= g_strdup(text_
);
102 Log::LogBufferItem::~LogBufferItem()
108 : log_window(NULL
), phase2_active(false), logfile(NULL
),
109 log_level_cim(LEVEL_DEBUG
), log_level_glib(LEVEL_DEBUG
),
110 log_level_purple(LEVEL_DEBUG
)
112 #define REGISTER_G_LOG_HANDLER(name, handler) \
113 g_log_set_handler((name), (GLogLevelFlags)G_LOG_LEVEL_MASK, (handler), this)
115 // register the glib log handlers
116 default_handler
= REGISTER_G_LOG_HANDLER(NULL
, default_log_handler_
);
117 glib_handler
= REGISTER_G_LOG_HANDLER("GLib", glib_log_handler_
);
118 gmodule_handler
= REGISTER_G_LOG_HANDLER("GModule", glib_log_handler_
);
119 glib_gobject_handler
=
120 REGISTER_G_LOG_HANDLER("GLib-GObject", glib_log_handler_
);
121 gthread_handler
= REGISTER_G_LOG_HANDLER("GThread", glib_log_handler_
);
126 g_log_remove_handler(NULL
, default_handler
);
127 g_log_remove_handler("GLib", glib_handler
);
128 g_log_remove_handler("GModule", gmodule_handler
);
129 g_log_remove_handler("GLib-GObject", glib_gobject_handler
);
130 g_log_remove_handler("GThread", gthread_handler
);
132 outputBufferMessages();
137 g_assert(!my_instance
);
139 my_instance
= new Log
;
144 g_assert(my_instance
);
150 void Log::initPhase2()
152 /* Phase 2 is called after CppConsUI and libpurple has been initialized.
153 * Logging to file is part of the phase 2. */
154 g_assert(!phase2_active
);
157 purple_prefs_add_none(CONF_PREFIX
"/log");
158 purple_prefs_add_bool(CONF_PREFIX
"/log/debug", false);
159 purple_prefs_add_string(CONF_PREFIX
"/log/filename", "debug.log");
160 purple_prefs_add_string(CONF_PREFIX
"/log/log_level_cim", "info");
161 purple_prefs_add_string(CONF_PREFIX
"/log/log_level_purple", "critical");
162 purple_prefs_add_string(CONF_PREFIX
"/log/log_level_glib", "warning");
164 updateCachedPreference(CONF_PREFIX
"/log/debug");
165 updateCachedPreference(CONF_PREFIX
"/log/log_level_cim");
166 updateCachedPreference(CONF_PREFIX
"/log/log_level_purple");
167 updateCachedPreference(CONF_PREFIX
"/log/log_level_glib");
170 purple_prefs_connect_callback(
171 this, CONF_PREFIX
"/log", log_pref_change_
, this);
173 // create the log window
174 g_assert(!log_window
);
175 log_window
= new LogWindow
;
178 // phase 2 is now active
179 phase2_active
= true;
181 // output buffered messages
182 for (LogBufferItems::iterator i
= log_items
.begin(); i
!= log_items
.end();
184 LogBufferItem
*item
= *i
;
186 // determine if this message should be displayed
187 Level loglevel
= getLogLevel(item
->getType());
189 if (loglevel
>= item
->getLevel())
190 write(item
->getType(), item
->getLevel(), item
->getText());
197 void Log::finalizePhase2()
199 /* Phase 2 finalization is done before CppConsUI and libpurple is finalized.
200 * Note that log levels are unchanged after phase 2 finishes. */
201 g_assert(phase2_active
);
203 // delete the log window
204 g_assert(log_window
);
208 purple_prefs_disconnect_by_handle(this);
210 // close the log file (if it is opened)
212 g_io_channel_unref(logfile
);
215 phase2_active
= false;
218 void Log::purple_print(
219 PurpleDebugLevel purplelevel
, const char *category
, const char *arg_s
)
221 Level level
= convertPurpleDebugLevel(purplelevel
);
222 if (log_level_purple
< level
)
223 return; // we don't want to see this log message
227 warning(_("centerim/log: purple_print() parameter category was "
231 char *text
= g_strdup_printf("libpurple/%s: %s", category
, arg_s
);
232 write(TYPE_PURPLE
, level
, text
);
236 gboolean
Log::purple_is_enabled(
237 PurpleDebugLevel purplelevel
, const char * /*category*/)
239 Level level
= convertPurpleDebugLevel(purplelevel
);
241 if (log_level_purple
< level
)
247 void Log::default_log_handler(
248 const char *domain
, GLogLevelFlags flags
, const char *msg
)
253 Level level
= convertGLibDebugLevel(flags
);
254 if (log_level_glib
< level
)
255 return; // we don't want to see this log message
257 char *text
= g_strdup_printf("%s: %s", domain
? domain
: "g_log", msg
);
258 write(TYPE_GLIB
, level
, text
);
262 void Log::glib_log_handler(
263 const char *domain
, GLogLevelFlags flags
, const char *msg
)
268 Level level
= convertGLibDebugLevel(flags
);
269 if (log_level_glib
< level
)
270 return; // we don't want to see this log message
272 char *text
= g_strdup_printf("%s: %s", domain
? domain
: "g_log", msg
);
273 write(TYPE_GLIB
, level
, text
);
277 void Log::log_pref_change(
278 const char *name
, PurplePrefType
/*type*/, gconstpointer
/*val*/)
280 // log/* preference changed
281 updateCachedPreference(name
);
284 void Log::updateCachedPreference(const char *name
)
286 if (!strcmp(name
, CONF_PREFIX
"/log/debug")) {
287 bool logfile_enabled
= purple_prefs_get_bool(name
);
289 if (logfile_enabled
&& !logfile
) {
290 char *filename
= g_build_filename(purple_user_dir(),
291 purple_prefs_get_string(CONF_PREFIX
"/log/filename"), NULL
);
294 if (!(logfile
= g_io_channel_new_file(filename
, "a", &err
))) {
295 error(_("centerim/log: Error opening logfile '%s' (%s)."), filename
,
301 else if (!logfile_enabled
&& logfile
) {
302 // debug was disabled so close logfile if it's opened
303 g_io_channel_unref(logfile
);
307 else if (!strcmp(name
, CONF_PREFIX
"/log/log_level_cim"))
308 log_level_cim
= stringToLevel(purple_prefs_get_string(name
));
309 else if (!strcmp(name
, CONF_PREFIX
"/log/log_level_purple"))
310 log_level_purple
= stringToLevel(purple_prefs_get_string(name
));
311 else if (!strcmp(name
, CONF_PREFIX
"/log/log_level_glib"))
312 log_level_glib
= stringToLevel(purple_prefs_get_string(name
));
315 void Log::write(Type type
, Level level
, const char *text
)
317 // if phase 2 is not active buffer the message
318 if (!phase2_active
) {
319 LogBufferItem
*item
= new LogBufferItem(type
, level
, text
);
320 log_items
.push_back(item
);
324 g_assert(log_window
);
325 log_window
->append(text
);
329 void Log::writeErrorToWindow(const char *fmt
, ...)
331 // can be called only if phase 2 is active
332 g_assert(phase2_active
);
333 g_assert(log_window
);
337 if (log_level_cim
< LEVEL_ERROR
)
338 return; // we don't want to see this log message
341 char *text
= g_strdup_vprintf(fmt
, args
);
344 log_window
->append(text
);
348 void Log::writeToFile(const char *text
)
350 // writing to file is enabled only in phase 2
359 // write text into logfile
361 if (g_io_channel_write_chars(logfile
, text
, -1, NULL
, &err
) !=
362 G_IO_STATUS_NORMAL
) {
364 _("centerim/log: Error writing to logfile (%s)."), err
->message
);
368 // if necessary write missing EOL character
369 size_t len
= strlen(text
);
370 if (len
&& text
[len
- 1] != '\n') {
372 g_io_channel_write_chars(logfile
, "\n", -1, NULL
, NULL
);
376 if (g_io_channel_flush(logfile
, &err
) != G_IO_STATUS_NORMAL
) {
378 _("centerim/log: Error flushing logfile (%s)."), err
->message
);
384 void Log::outputBufferMessages()
386 // output all buffered messages to stderr
387 for (LogBufferItems::iterator i
= log_items
.begin(); i
!= log_items
.end();
389 LogBufferItem
*item
= *i
;
391 // determine if this message should be displayed
392 Level loglevel
= getLogLevel(item
->getType());
394 if (loglevel
>= item
->getLevel()) {
395 const char *text
= item
->getText();
398 std::fprintf(stderr
, text
);
400 // if necessary write missing EOL character
401 if (text
[strlen(text
) - 1] != '\n')
402 std::fprintf(stderr
, "\n");
411 Log::Level
Log::convertPurpleDebugLevel(PurpleDebugLevel purplelevel
)
413 switch (purplelevel
) {
414 case PURPLE_DEBUG_MISC
:
416 case PURPLE_DEBUG_INFO
:
418 case PURPLE_DEBUG_WARNING
:
419 return LEVEL_WARNING
;
420 case PURPLE_DEBUG_ERROR
:
421 return LEVEL_CRITICAL
;
422 case PURPLE_DEBUG_FATAL
:
424 case PURPLE_DEBUG_ALL
:
425 return LEVEL_ERROR
; // use error level so this message is always printed
429 _("centerim/log: Unknown libpurple logging level '%d'."), purplelevel
);
433 Log::Level
Log::convertGLibDebugLevel(GLogLevelFlags gliblevel
)
435 if (gliblevel
& G_LOG_LEVEL_DEBUG
)
437 if (gliblevel
& G_LOG_LEVEL_INFO
)
439 if (gliblevel
& G_LOG_LEVEL_MESSAGE
)
440 return LEVEL_MESSAGE
;
441 if (gliblevel
& G_LOG_LEVEL_WARNING
)
442 return LEVEL_WARNING
;
443 if (gliblevel
& G_LOG_LEVEL_CRITICAL
)
444 return LEVEL_CRITICAL
;
445 if (gliblevel
& G_LOG_LEVEL_ERROR
)
448 warning(_("centerim/log: Unknown GLib logging level '%d'."), gliblevel
);
452 Log::Level
Log::stringToLevel(const char *slevel
)
454 if (!g_ascii_strcasecmp(slevel
, "none"))
456 else if (!g_ascii_strcasecmp(slevel
, "debug"))
458 else if (!g_ascii_strcasecmp(slevel
, "info"))
460 else if (!g_ascii_strcasecmp(slevel
, "message"))
461 return LEVEL_MESSAGE
;
462 else if (!g_ascii_strcasecmp(slevel
, "warning"))
463 return LEVEL_WARNING
;
464 else if (!g_ascii_strcasecmp(slevel
, "critical"))
465 return LEVEL_CRITICAL
;
466 else if (!g_ascii_strcasecmp(slevel
, "error"))
471 Log::Level
Log::getLogLevel(Type type
)
475 return log_level_cim
;
477 return log_level_glib
;
479 return log_level_purple
;
481 g_assert_not_reached();
485 /* vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab : */