Tips_tutorials   >   Studiojs202   >   Non-Visual Web App Monitoring

Non-Visual Web App Monitoring

Based on some of the code in rtWizard_Monitor and wMonitor we will build a non-visual web app monitoring object class.

Other features we would like to add to the web monitoring object class:

For the rest of the tutorial we will build a web monitoring object class with these features.

Data to Capture

Before we create the web monitoring object we need to decide what web app data we want to capture.

Here is the information which we are going to record in our database.

  1. Each new connection, the remote task class name, client's IP address.
  2. Each busy connection.
  3. Each rejected connection and the error message.

If the above information is saved to the database we can then report the following summary statistics from the database.

  1. Total connections. (Number of requests)
  2. Total busy connections.
  3. Total rejected connections.
  4. Number connections per client IP address.
  5. Number connections per remote task class.

In our database we will need a Webappstat table, with the following columns:

  1. ConnectionTime - Timestamp when the client request started
  2. ClientAddress - the IP address of the client
  3. RemoteTaskClassName - the name of the remote task which was instantiated
  4. ResponseSeconds - the number of seconds it took to process the request.
  5. EventCode - evRejected or evBusy if applicable.

Create Schema Class

  1. F2 Browser > select ContactsWeb library > New Class > Schema > name it sWebappstat
  2. Double-click sWebappstat to add the columns.
  3. Set the table name field to Webappstat
  4. Add the following columns to the schema class:
    1. ConnectionTime - Date Time - D m Y H:N:S
    2. ClientAddress - Character 15
    3. ResponseSeconds - Integer
    4. RemoteTaskClassName - Character 50
    5. EventCode - Character 15

  5. Set the No nulls property to kTrue for all of the columns.


We are doing something a bit unorthodox by not including a primary key in this table. This is done to avoid the overhead caused by setting the primary key. We will only be inserting records into this table, no updates, so we can get away with skipping the primary key column.

Create Servertable

Create the Webappstat servertable by dragging the sWebappstat schema class onto the CONTACTS_01 session node of the SQL Browser root node in the F2 Browser treelist.

You can check your work by clicking the Tables child node of the CONTACTS_01 node and then double-clicking the Webappstat table. An Alter Table window opens showing the table columns.

Create Table Class

We should have a table class mapped to the sWebappstat schema class.

  1. Select the tCountry table class.
  2. Right-click Duplicate. Name the copy tWebappstat
  3. F6 Properties > set the $sqlclassname to sWebappstat
  4. Double-click tWebappstat.
  5. Change the $:DefaultOrderBy method to:

    Quit method "ORDER BY ConnectionTime DESC"

Create Web Monitor Object Class

We are ready to create the non-visual web monitor object class.

  1. F2 Browser > select ContactsWeb library > New Class > Object > name it oWebMonitor
  2. Double-click oWebMonitor to go to the class methods.
  3. Rename the $construct method to $initialize and enter the following code:

    Do iRow.$definefromsqlclass('tWebappstat')
    Do iRow.$sessionobject.$assign($ctask.dbsessionobj)
    Quit method kTrue


    The reason we are using a $initialize method rather than a $construct method in oWebMonitor object is the $intialize gives error checking control to the class which instantiates the object. You'll see this in more detail later in the tutorial.

  4. Add a method and name it $addConnection.
  5. Add a parameter, prTask, Item reference data type to the method.
  6. Add a parameter, pEventCode_opt, Long Integer data type to the method.
  7. Add the following code to the method.

    Calculate iRow.ConnectionTime as prTask.$connectiontime
    Calculate iRow.ClientAddress as prTask.$clientaddress
    Calculate iRow.ResponseSeconds as ddiff(kSecond,prTask.$connectiontime,#D)
    Calculate iRow.RemoteTaskClassName as prTask().$class().$name

    ; Set the event code to blank, unless it is evBusy or evRejected.
    If isnull(pEventCode_opt)
       Calculate iRow.EventCode as ''
    Else If pEventCode_opt=evBusy|pEventCode_opt=evRejected
       Calculate iRow.EventCode as pEventCode_opt
       Calculate iRow.EventCode as ''
    End If

    ; Insert the record in the database.
    Do iRow.$insert() Returns FlagOK
    If not(FlagOK)
       ; Log the SQL error.
       Calculate ErrText as iRow.$statementobject().$nativeerrortext
       Calculate Mssg as con('SQL Error on Insert',kCr,kCr,ErrText)
       Do errhndlr.$logError($cmethod,Mssg)
    End If

    Quit method FlagOK

Add webmon Startup_Task Variable

To avoid having to initialize the oWebMonitor object each time a request comes through the Omnis Web App Server we will initialize it once in the Startup_Task and then simply reference it when each remote task is initialized.

  1. Add the task variable, webmon, to the Startup_Task class.
  2. Set the data type to Object and point it to the oWebMonitor object class.
  3. Remove the following line of code which was added to the end of $construct method of the Startup_Task by the Class Wizard.

    Do $clib.$windows.wMonitor.$openonce() ;; added by Monitor remote task wizard
  4. Add the following code to the end of $construct method of the Startup_Task:

    ; Initialize the web monitoring object.
    Do webmon.$initialize() Returns FlagOK
    If not(FlagOK)
       Do errhndlr.$promptonceLastError()
    End If
    Quit method FlagOK

    The oWebMonitor class is initialized just once, when the library is opened. If there is an initialization error it is immediately reported.

The developer has little control over the $construct methods. Omnis Studio automatically calls the $construct method when the object is instantiated. If an error is logged by a $construct method, we really don't have a way of receiving the return flag from the $construct method, so the error would go by unnoticed. By using an $initialize method rather than a $construct method, the developer has control over when the initialization code is run. The developer receives the return flag and can properly notify the user of the error.

Create Remote Task Superclass

We will now create a remote task superclass which other remote tasks will be subclassed from. The remote task superclass will have all of the remote task - task variables and the web monitoring calls to the oWebMonitor object.

  1. F2 Browser > select ContactsWeb library > New Class > Remote Task > name it rtBase_abstract
  2. Double-click rtBase_abstract to go to the class methods.
  3. Add the instance variable, ioHTMLTools, Object type. Point it to oHTMLTools.
  4. Add the following task variables:
    1. dbsessionobj - Item reference
    2. errhndlr - Item reference
    3. webmon - Item reference

  5. Copy the setTaskVars method from rtCountryList to rtBase_abstract and modify the code so that it reads as follows:

    Do $itasks.[$clib().$name].$getTaskVarRef('errhndlr',errhndlr) Returns FlagOK
    If FlagOK
       Do $itasks.[$clib().$name].$getTaskVarRef('dbsessionobj',dbsessionobj) Returns FlagOK
       If FlagOK
          Do $itasks.[$clib().$name].$getTaskVarRef('webmon',webmon) Returns FlagOK
       End If
    End If
    Quit method FlagOK

  6. Add the following code to the $construct method.

    Do method setTaskVars Returns FlagOK
    Quit method FlagOK

  7. Add the following code to the $destruct method.

    Do webmon.$addConnection($cinst,pEventCode) Returns FlagOK
    Quit method FlagOK

  8. Add an $event method to rtBase_abstract.

  9. Add the following code to the $event method.

    On evBusy
    Do webmon.$addConnection($cinst,pEventCode)

    On evIdle

    On evRejected
    Do webmon.$addConnection($cinst,pEventCode)

Subclass rtCountryList

We now need to make rtCountryList a subclass of rtBase_abstract.

  1. F2 Brower > select rtCountryList > F6 Properties
  2. Set the $superclass property to rtBase_abstract

There are task variables and methods in the superclass which we need to inherit in the subclass.

  1. Double-click rtCountryList to get to the class methods.
  2. Right-click the instance variable ioHTMLTools > select Inherit Variable... > click Yes
  3. Right-click the task variable dbsessionobj > select Inherit Variable... > click Yes
  4. Repeat the above step for errhndlr.
  5. Setting the task variables is being handled by the superclass, so we can delete the setTaskVars method in rtCountry. Right-click the setTaskVars method and select Delete Selected Methods...
  6. Modify the start of the $construct method as follows so that it no longer calls the setTaskVars method, and instead calls the superclass $construct method.

    Do inherited Returns FlagOK
    If FlagOK

We are ready for testing our non-visual web monitoring object.

Web Monitoring Sequence

Here is the sequence of what happens with our web monitoring classes and methods.

  1. The ContactsWeb library is opened causing the $construct method of the Startup_Task class to execute.
  2. Near the end of the $construct method the oWebMonitor object class is instantiated by the task variable webmon and sent an $initialize message.

    The web app is now ready to receive HTTP requests.
  3. An HTTP request specifying the rtCountyList remote task class is received by the Omnis Web App Server.
  4. rtCountryList is instantiated by the Omnis Web App Server and a $construct message is sent to it.
  5. The $construct method of rtCountryList calls the superclass $construct method of rtBase_abstract which sets all of the remote task task variables by referencing them to the matching Startup_Task task variables.
  6. The $construct method of rtCountryList processes the request and returns the results as an HTML web page to the client via the Omnis Web App Server.
  7. A $destruct message is sent to the rtCountryList instance by the Omnis Web App Server. The superclass $destruct method is immediately called. The superclass $destruct method sends an $addConnection message to oWebMonitor.
  8. The $addConnection method prepares the row variable and inserts a record into the Webappstat table in the database.

I tried to get my ContactsWeb to generate an evBusy or evRejected event, but was unsuccessful in doing so. Should either or those events occur the following sequence would happen:

  1. If an evBusy or evRejected event occurs it would be caught by the $event method of rtBase_abstract
  2. An $addConnection message is sent to oWebMonitor.
  3. The $addConnection method prepares the row variable including with the pEventCode, and inserts a record into the Webappstat table in the database.

Web Monitoring Test

Time to test our web monitor classes and code.

  1. Close and reopen the ContactsWeb library in order to initialize the the oWebMonitor object class with the webmon task variable of the Startup_Task class.
  2. Put a blue breakpoint at the top of the $construct method of the rtCountry remote task.
  3. Put a blue breakpoint at the top of the $addConnection method of the oWebMonitor object class.
  4. Using your web browser open the searchcountries.htm web page which you created in the Studio JS 201 tutorial.

  5. Enter the first letter of a country (e.g. 'C') and click the Submit button on the web page.
  6. All going well you should hit the blue breakpoint in the $construct method of rtCountry.
  7. Click the Go button in the IDE toolbar.
  8. All going well you should hit the blue breakpoint in the $addConnection method of oWebMonitor.
  9. Step through the code and check to make sure the record is successfully inserted into the database.
  10. Click the Go button to finish
  11. Do a couple more searches from the searchcountries.htm web page.

We can now look at the database and view the Webappstat table records.

  1. F2 Browser > SQL Browser > CONTACT_01 session > Tables > Webappstat
  2. Click Show Data
  3. The Interactive SQL window will open displaying all of the Webappstat records.
There you have it! Any requests made to our Omnis Studio web app are being recorded in the database. We can now generate summary reports on the data to measure the useage of our web app.