Debug API How-To

One question that we used to see quite frequently with plugin development was, "How do I get debugging messages?" Libpurple already provides a facility for this. It's called the Debug API. In this document we'll dig into this API with a brand-new plugin.

About Debugging with Libpurple's Debug API

The beauty of the debug API in libpurple is that you can print as much information as is necessary to help you find bugs in your code without worrying about distracting the user--the user interface decides when and where to display the debugging information. Once you have the bugs worked out, you can still leave the debugging messages there and it won't hurt anything.

Libpurple provides a generic purple_debug() function for use in printing messages, but at the same time provides convenience functions for each debugging level. This How-To will focus on using the convenience functions for the convenience and simplicity they provide, as well as for their attractiveness to the lazy typist.

Debug Levels

The documentation for the debug.h header file shows an enumeration listing each of the 6 debug levels defined. Only the last five are typically used when writing debug messages, leaving PURPLE_DEBUG_ALL primarily for the UI's use.

The other debugging levels are fairly straightforward, and I'll leave it to you to read the enum's documentation for the brief descriptions. For the most part we're going to want to use PURPLE_DEBUG_MISC and PURPLE_DEBUG_INFO for plugin messages, but there are obviously situations in which we'll need other levels.

Using the Debug Functions

If you browse the debug.h doxygen documentation further, you will see that all of the debug functions provided show a ... for their final parameter. This means that the function is a variadic function, which can take a variable number of parameters. These functions are defined as such because they behave like the standard library's printf() function in many ways. You will see the evidence of this once we show the actual source of the plugin.

You will also notice that the functions take a category parameter. The category makes it easy to tell what generated each specific debug message when reading the debug log. You can use pretty much anything you want here, but for the purposes of demonstration, we're going to stick with the plugin ID for the category. The recommended best practice is to use the plugin's ID or static name.

Writing the Debug Example Plugin

This plugin will eventually be included as an example plugin in the libpurple source, but it is not part of Pidgin 2.0.2, which the rest of the How-To documents assume you are using. We are going to continue this assumption here. In fact, we're going to continue all the assumptions set forth in the Basic C Plugin How-To

Go back to ~/development/pidgin-2.0.2/libpurple/plugins and fire up your favorite editor on a new file called debug_example.c. The contents of the file will be this:

/* We're including glib.h again for the gboolean type. */
#include <glib.h>

/* This is the required definition of PURPLE_PLUGINS as required for a plugin */
#define PURPLE_PLUGINS

/* Here we're including the necessary libpurple headers for this plugin.  Note
 * that we're including them in alphabetical order.  This isn't necessary but
 * we do this throughout our source for consistency. */
#include "debug.h"
#include "plugin.h"
#include "version.h"

/* It's more convenient to type PLUGIN_ID all the time than it is to type
 * "core-debugexample", so define this convenience macro. */
#define PLUGIN_ID "core-debugexample"

/* Common practice in third-party plugins is to define convenience macros for
 * many of the fields of the plugin info struct, so we'll do that for the
 * purposes of demonstration. */
#define PLUGIN_AUTHOR "John Bailey <rekkanoryo@rekkanoryo.org>"

/* As we've covered before, libpurple calls this function, if present, when it
 * loads the plugin.  Here we're using it to show off the capabilities of the
 * debug API and just blindly returning TRUE to tell libpurple it's safe to
 * continue loading. */
static gboolean
plugin_load(PurplePlugin *plugin)
{
    /* Define these for convenience--we're just using them to show the
     * similarities of the debug functions to the standard printf(). */
    gint i = 256;
    gfloat f = 512.1024;
    const gchar *s = "example string";

    /* Introductory message */
    purple_debug_info(PLUGIN_ID,
            "Called plugin_load.  Beginning debug demonstration\n");

    /* Show off the debug API a bit */
    purple_debug_misc(PLUGIN_ID,
            "MISC level debug message.  i = %d, f = %f, s = %s\n", i, f, s);

    purple_debug_info(PLUGIN_ID,
            "INFO level debug message.  i = %d, f = %f, s = %s\n", i, f, s);

    purple_debug_warning(PLUGIN_ID,
            "WARNING level debug message.  i = %d, f = %f, s = %s\n", i, f, s);

    purple_debug_error(PLUGIN_ID,
            "ERROR level debug message.  i = %d, f = %f, s = %s\n", i, f, s);

    purple_debug_fatal(PLUGIN_ID,
            "FATAL level debug message. i = %d, f = %f, s = %s\n", i, f, s);

    /* Now just return TRUE to tell libpurple to finish loading. */
    return TRUE;
}

static PurplePluginInfo info = {
    PURPLE_PLUGIN_MAGIC,        /* magic number */
    PURPLE_MAJOR_VERSION,       /* purple major */
    PURPLE_MINOR_VERSION,       /* purple minor */
    PURPLE_PLUGIN_STANDARD,     /* plugin type */
    NULL,                       /* UI requirement */
    0,                          /* flags */
    NULL,                       /* dependencies */
    PURPLE_PRIORITY_DEFAULT,    /* priority */

    PLUGIN_ID,                  /* id */
    "Debug API Example",        /* name */
    "1.0",                      /* version */
    "Debug API Example",        /* summary */
    "Debug API Example",        /* description */
    PLUGIN_AUTHOR,              /* author */
    "http://pidgin.im",         /* homepage */

    plugin_load,                /* load */
    NULL,                       /* unload */
    NULL,                       /* destroy */

    NULL,                       /* ui info */
    NULL,                       /* extra info */
    NULL,                       /* prefs info */
    NULL,                       /* actions */
    NULL,                       /* reserved */
    NULL,                       /* reserved */
    NULL,                       /* reserved */
    NULL                        /* reserved */
};

static void
init_plugin(PurplePlugin *plugin)
{
}

PURPLE_INIT_PLUGIN(debugexample, init_plugin, info)

Now that we have the plugin, we need to compile and install it.

Compiling, Installing, and Testing the Plugin

As before, run make debug_example.so (make -f Makefile.mingw debug_example.dll for you Windows types) to build the plugin. Copy the resulting debug_example.so to ~/.purple/plugins, or copy the resulting debug_example.dll to %APPDATA%\.purple\plugins.

Since we're continuing our assumptions, we're going to still assume that you're using Pidgin. Open the Debug Window (located on the Help menu). Resize it so it's rather large so we can easily see all the messages. Now open the Plugins dialog. In the Debug Window you'll see a lot of information about plugins scroll by as libpurple reprobes all the plugins available to it. Now find the "Debug API Example" entry in the dialog. Check the box and watch what appears in the Debug Window. You may need to scroll back just a bit to see the six messages that print. You should see something like this:

(10:17:36) core-debugexample: Called plugin_load. Beginning debug demonstration
(10:17:36) core-debugexample: MISC level debug message. i = 256, f = 512.102417, s = example string
(10:17:36) core-debugexample: INFO level debug message. i = 256, f = 512.102417, s = example string
(10:17:36) core-debugexample: WARNING level debug message. i = 256, f = 512.102417, s = example string
(10:17:36) core-debugexample: ERROR level debug message. i = 256, f = 512.102417, s = example string
(10:17:36) core-debugexample: FATAL level debug message. i = 256, f = 512.102417, s = example string

As you can see, the similarities to printf() are obvious and quite useful. You probably also noticed that we assigned the value of 512.1024 to the variable f, yet the debug statement printed 512.102417 (or similar, depending on a number of factors). This is expected; floating-point values are not exact. Most C tutorials will cover this in much better detail than we can here, so if this is confusing or you're a bit rusty in that area, your favorite search engine can turn up information for your consumption on the subject. The value of f being slightly off here doesn't really matter--all this was for demonstrative purposes only, and could have been corrected by using %.4f as the conversion specifier instead of simply %f.

The output in the debug window shows that the different debug levels are handled differently depending on their severity, with colors changing, and finally bold-faced type at the FATAL level. Finch and other UIs will handle this differently.

Additional Functions in the Debug API

There are additional functions in the Debug API that have not been covered here. The functions purple_debug_set_enabled(), purple_debug_is_enabled(), purple_debug_get_ui_ops(), purple_debug_set_ui_ops(), and purple_debug_init() are all functions that plugins generally won't need or want to touch. These functions are mainly intended for use in libpurple UIs. In theory, a plugin could replace the UI's existing debug handling by replacing the ui_ops structure that the UI supplied to libpurple, but that is beyond the scope of this document and likely completely unnecessary.

Beyond the Debug API

The Debug API is a quite useful subset of the libpurple API, but it is far from the end of things you can do in plugins. This document is finished, but other documents in the C Plugin How-To will build upon these examples and the previous examples in the series while introducing more features of libpurple.

Last modified 5 years ago Last modified on 04/15/12 02:15:07
All information, including names and email addresses, entered onto this website or sent to mailing lists affiliated with this website will be public. Do not post confidential information, especially passwords!