C Examples -- Example 4: stats

### Example 4: stats

The following example provides several cooperating facilities to implement a simple connection statistics system. The module includes a trace to collect the statistics on each connection, a scheduled procedure to aggregate the statistics, the /NS/Stat request function to print the statistics, and a Tcl command to access the statistics in a script.

This example can be found in the `examples/c/stats` directory.

        /*
         * stat.c - Example C module.
         *
         * This example incorporates many of the features of the AOLserver
         * C API in a single simple, but useful, statistics gathering module.
         * The module maitains statistics on the number of connections and
         * bytes sent by a server.  The data is stored in a simple
         * `StatContext' structure which is allocated when the module is
         * loaded.  The StatContext stores the current, last, and total numbers
         * and is updated after each connection by the `StatTrace' connection
         * trace procedure.  At a regular interval (the interval in seconds
         * is configurable), the `StatUpdate' funcation is called to log the
         * current and total number to the AOLserver log file and then
         * makes the current data the last data.  Access to these data is
         * provided in two forms:
         *
         * 1.  A simple C request function, `StatRequest', will return the
         * current data as a simple notice page.  The StatRequest function
         * is registered at the GET /NS/Stat URL of the server.
         *
         * 2.  The `ns_stat' Tcl command is added to each Tcl interpreter
         * of the server interpreter pool.  This allows a Tcl script
         * to access the current, last, or total data at any time.
         *
         * Because the AOLserver is completely multithreaded, the statistics
         * data can be updated and queried simultaneously.  An Ns_Mutex is
         * included in the ServerContext structure to keep multiple threads
         * from accessing the data at the same time.
         *
         * Finally, on shutdown, the `StatShutdown' function is called to
         * print out a final tally and clean up the ServerContext structure.
         *
         * Note that all the features of this module are specific to the
         * server it is loaded into.  The server handle, `hServer',
         * is used to makes sure the trace, request, Tcl command, and
         * shutdown functions are all run in the context of the single
         * server.  Other virtaul servers may ignore or load the
         * stat module as well and the AOLserver guarantees the data
         * will be keep separate for each server.
        */

        #include "ns.h"
        #include "nstcl.h"

        /*
         * The default statistics update interval is 1 minute or 60 seconds.
         */
        #define DEFAULT_INTERVAL	60

        /*
         * Definitions of the StatContext structure which is allocated
         * on a per-server basis.
         */
        typedef struct {
        	int	bytes;
        	int conns;
        } StatData;

        typedef struct {
        	Ns_Mutex lock;
        	int interval;
        	char *hServer;
        	StatData current;
        	StatData last;
        	StatData total;
        } StatContext;

        /*
         * Forward declarations of functions which will be referenced
         * in the stat module initialization function.
         */
        static Tcl_CmdProc StatCmd;
        static Ns_TclInterpInitProc StatTclInit;
        static Ns_TraceProc StatTrace;
        static Ns_Callback StatUpdate;
        static Ns_Callback StatShutdown;
        static Ns_OpProc StatRequest;

        /*
         * The Ns_ModuleVersion exported integer is used to verify
         * this module version when loaded.  For AOLserver 2.0,
         * 1 (one) is the only valid value for this variable.
         */
        int Ns_ModuleVersion = 1;

        /*
         * The Ns_ModuleInit function is the function the AOLserver
         * will call each time the module is loaded into a
         * server.  The function is passed two parameters:
         *
         * hServer:   The server `handle' as a string. This is the
         *            short name given to the virutal server such
         *            as `server1'.
         *
         * hModule:   The module `handle' as a string. This is the
         *            short name given to the module such as `stat'
         *
         * For example, if this module is known as `stat' and loaded
         * into the `server1' server with entries similar to the following
         * in the nsd.ini file:
         *
         * [ns\servers]
         * server1=My First Server
         *
         * [ns\server1\modules]
         * stat=stat.dll
         *
         * This function would be called with "server1" and "stat" as
         * its arguments.
         *
         */
        int
        Ns_ModuleInit(char *hServer, char *hModule)
        {
        	char *configPath;
        	StatContext *ctx;

        	/*
        	 * Create and initalize the statistics context.
        	 */
        	ctx = ns_malloc(sizeof(StatContext));
        	Ns_InitializeMutex(&ctx->lock);
        	ctx->hServer = hServer;
        	ctx->current.bytes = 0;
        	ctx->current.conns = 0;
        	ctx->last.bytes = 0;
        	ctx->last.conns = 0;
        	ctx->total.bytes = 0;
        	ctx->total.conns = 0;

        	/*
        	 * Determine the statistics interval from the config file.
        	 * The Ns_ConfigGetPath function will expand to the
        	 * `server module specific' configuration section, e.g.,
        	 * [ns\server1\module\stat].
        	 */
        	configPath = Ns_ConfigGetPath(hServer, hModule, NULL);
        	if (!Ns_ConfigGetInt(configPath, "Interval", &ctx->interval)) {
        		ctx->interval = DEFAULT_INTERVAL;
        	}

        	/*
        	 * Register the trace to accumulate the statistics.
        	 */
        	Ns_RegisterServerTrace(hServer, StatTrace, ctx);

        	/*
        	 * Register the statistics update function to run at
        	 * regular intervals.
        	 */
        	Ns_ScheduleProc(StatUpdate, ctx, 0, ctx->interval);

        	/*
        	 * Add the GET /NS/Stat request function.
        	 */
        	Ns_RegisterRequest(hServer, "GET", "/NS/Stat", StatRequest,
                                                                        NULL, ctx, 0);

        	/*
        	 * Add the Tcl "ns_stat" command to the interpreter pool.
        	 * Note how the context is passed to StatTclInit which
        	 * then passes it to the Tcl_CreateCommand function.
        	 */
        	Ns_TclInitInterps(hServer, StatTclInit, ctx);

        	/*
        	 * Register the statistics shutdown procedure which
        	 * cleans up the context on server shutdown.
        	 */
        	Ns_RegisterServerShutdown(hServer, StatShutdown, ctx);

        	return NS_OK;
        }

        /*
         * StatTrace is called after each connection and accumulates the
         * statistics.
         */
        static void
        StatTrace(void *ctx, Ns_Conn *conn)
        {
        	StatContext *sc;
        	int bytes;

        	sc = (StatContext *) ctx;
        	Ns_LockMutex(&sc->lock);
        	bytes = Ns_ConnResponseLength(conn);
        	sc->current.bytes += bytes;
        	sc->total.bytes += bytes;
        	++sc->current.conns;
        	++sc->total.conns;
        	Ns_UnlockMutex(&sc->lock);
        }

        /*
         * StatUpdate aggregates the statistics and logs the data to
         * the AOLserver server log.
         */
        static void
        StatUpdate(void *ctx)
        {
        	StatContext *sc;

        	sc = (StatContext *) ctx;

        	Ns_LockMutex(&sc->lock);
        	Ns_Log(Notice,
                                "StatUpdate(%s):  Last: conns %d, bytes %d  Total: conns %d, bytes %d",
        		sc->hServer, sc->current.conns, sc->current.bytes,
        		sc->total.conns, sc->total.bytes);
        	sc->last.conns = sc->current.conns;
        	sc->last.bytes = sc->current.bytes;
        	sc->current.conns = 0;
        	sc->current.bytes = 0;
        	Ns_UnlockMutex(&sc->lock);
        }

        /*
         * StatTclInit is called once for each Tcl interpreter
         * in the virutal server's Tcl interpreter pool.
         */
        static int
        StatTclInit(Tcl_Interp *interp, void *ctx)
        {
        	Tcl_CreateCommand(interp, "ns_stat", StatCmd, ctx, NULL);
        	return NS_OK;
        }

        /*
         * StatCmd implements the Tcl "ns_stat" command.
         */
        static int
        StatCmd(ClientData ctx, Tcl_Interp *interp, int argc, char **argv)
        {
        	StatContext *sc;
        	StatData *sd;

        	sc = (StatContext *) ctx;
        	if (argc != 2) {
        		Tcl_AppendResult(interp, "wrong # args: should be \"",
        			argv[0], " command\"", NULL);
        		return TCL_ERROR;
        	}
        	if (strcmp(argv[1], "current") == 0) {
        		sd = &sc->current;
        	} else if (strcmp(argv[1], "last") == 0) {
        		sd = &sc->last;
        	} else if (strcmp(argv[1], "total") == 0) {
        		sd = &sc->total;
        	} else {
        		Tcl_AppendResult(interp, "unknown command \"",
        			argv[1], "\":  should be current, last, or total", NULL);
        		return TCL_ERROR;
        	}

        	Ns_LockMutex(&sc->lock);
        	sprintf(interp->result, "%d %d", sd->conns, sd->bytes);
        	Ns_UnlockMutex(&sc->lock);
        	return TCL_OK;
        }

        /*
         * StatRequest is a simple AOLserver request function which returns
         * the current data in a simple HTML page.  The page is generated
         * with the Ns_ConnReturnNotice() function which is used throughout the
         * AOLserver to generate simple HTML page responses with the
         * AOLserver banner logo.  Ns_ConnReturnNotice() takes a string as
         * the HTML page content.  We use an Ns_DString to quickly build
         * up the string - Ns_DString's grow as needed so we don't have
         * to worry about buffer overflow.  The HTML page is a simple
         * HTML3 <TABLE> which formats the current, last, and total
         * statistics for the server.
         */
        static int
        StatRequest(void *ctx, Ns_Conn *conn)
        {
        	StatContext *sc;
        	Ns_DString ds;
        	int retcode;

        	sc = (StatContext *) ctx;
        	Ns_DStringInit(&ds);
        	/*
        	 * Build up the HTML3 <TABLE> with the latest data.
        	 */
        	Ns_DStringAppend(&ds, "<table border cellpadding=\"10\">");
        	Ns_DStringVarAppend(&ds, "<tr><th>", sc->hServer, "</th>", NULL);
        	Ns_DStringAppend(&ds,
                                                      "<th>Current</th><th>Last</th><th>Total</th></tr>");
        	Ns_LockMutex(&sc->lock);
        	Ns_DStringPrintf(&ds,
                                      "<tr><th># connections</th><td>%d</td><td>%d</td><td>%d</td></tr>",
        		sc->current.conns, sc->last.conns, sc->total.conns);
        	Ns_DStringPrintf(&ds,
                                                "<tr><th># bytes</th><td>%d</td><td>%d</td><td>%d</td></tr>",
        		          sc->current.bytes, sc->last.bytes, sc->total.bytes);
        	Ns_UnlockMutex(&sc->lock);
        	Ns_DStringAppend(&ds, "</table>");
        	/*
        	 * Return the HTML page using Ns_ConnReturnNotice().
        	 */
        	retcode = Ns_ConnReturnNotice(conn, 200, "Server Statistics",
                                                                                          ds.string);
        	/*
        	 * Don't forget to free the dstring!
        	 */
        	Ns_DStringFree(&ds);

        	return retcode;
        }

        /*
         * StatShutdown simple prints a final statistics entry to the
         * server log and then cleans up the StatContext structure.
         */
        static void
        StatShutdown(void *ctx)
        {
        	StatContext *sc;

        	sc = (StatContext *) ctx;
        	Ns_Log(Notice, "StatShutdown(%s):  Total:  conns %d, bytes %d.",
        		sc->hServer, sc->total.conns, sc->total.bytes);
        	Ns_DestroyMutex(&sc->lock);
        	ns_free(ctx);
        }

Top of Page
Copyright © 1998-99 America Online, Inc.