[ Previous ] [ Contents ] [ Index ] [ Next ]

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

[ Previous ] [ Contents ] [ Index ] [ Next ]
Copyright © 1998-99 America Online, Inc.