AJS_Logger User-Guide

Introduction

AJS_Logger is a client of the AJS proxy-based method-call-interception object, and can be used to instrument methods in a JavaScript application for the purposes of logging, profiling and debugging calls to such methods. Unlike AJS_Validator, and AJS_ODL, which are singleton objects, the term 'AJS_Logger' is a catch-all moniker that refers to the function createAJS_LoggerFactory, the factory function it creates, and the logging objects that calls to that factory generate. AJS_Logger objects possess just three methods through which you can apply 'logging definitions' to the methods of objects in your application.

When creating a logging object, you have the option to pass a 'stream object', which will write logging information to any target you choose (such as a debugging console). Moreover, you may equip multiple loggers with different streams, which allows you to log calls to a set of methods where only minimal information is recorded, and to generate more detailed reports for calls to a subset of those methods. This lends great flexibility and precision when instrumenting an application.

Furthermore, by using a stream object that uses XHR or a WebSockets connection, it is possible to log the execution of an application's methods on remote as well as local platforms. That is: should you need, say, to profile the execution of your application, in the context of a real user operating that application in some distant land, you can make an AJS_Logger object send the information it gathers back to the server for you to analyse.

Please note here that, where this guide uses the expression 'write to the log' (and its variants), it is referring to whatever the logger in question does when 'recording' a method's call and return. In the case of the default stream that loggers may create, 'recording' means 'writing to the console object'.

Note also that only the default Stream type that AJS_Logger defines uses a host-type (the console object). This means that the use of a proprietary stream on your part can render a given logging-object platform independent.

Note too that each Logger object that a given logger factory-function creates carries a 'LoggerNum' and an AJS_Validator_Tag property. The LoggerNum is an integer that the factory function generates automatically, and which, starting from zero, identifies different loggers according to the order in which they were created, whereas the AJS_Validator_Tag is a string that always has "AJS_Logger_" as a prefix. A given logger-factory function concatenates the LoggerNum value to this prefix, and this is of value where you are using AJS_Validator to validate, by means of the AllowTags ValidationDef-property, the loggers that your code passes to a given method.

Finally, in this section, do remember that, if you plan to deploy a minified codebase that uses AJS_Logger, you should read the Minification page, which apprises you of the issues that pertain here.

Contents Introduction
Importing AJS_Logger
LogDefs
Validating AJS_Logger Method-Calls
Enabling/Disabling AJS_Logger

 // -- Example 1: AJS_Logger-Instance Interface-Snapshot (pseudo code) --------
 //
 //    See the API documentation for formal property-definitions.
 //

 var MyLogger =
    {
    applyLogDef       : function (MethodOwner, LogDef[, MethodOwnerName]) { ... },
    pushLogDef        : function (MethodOwner, LogDef[, MethodOwnerName]) { ... },

    applyLogDefQueue  : function ()                                       { ... },

    //-------------------------------------

    suspend           : function ()                                       { ... },
    resume            : function ()                                       { ... },

    //-------------------------------------

    LoggerNum         : Number,

    Version           : "1.0",
    AJS_Validator_Tag : "AJS_Logger_" + LoggerNumber

    };
            

Importing AJS_Logger

To use AJS_Logger in the simplest fashion, import the contents of AJS_Logger.js into your application, along with the code that defines the AJS object, as well as the AJS_DefMgr.js and ThrowException.js support-libraries. Following that, call createAJS_LoggerFactory, and retain the function-reference returned. Example 2 illustrates this process.

Note that, while Example 2 passes no arguments to createLogger, the function will accept a reference to a Stream object (see the section on Streams).

Note also that, while you may assign whatever name you like to the logger-creation function that createAJS_LoggerFactory returns, it is a good idea to call it createAJS_Logger because this is how it refers to itself in any exceptions that arise during its use.


 <!-- Example 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

 </html>
    </head>
       <script src = "ThrowException.js"></script>  <!-- Import ThrowException_Remote.js to effect            -->
                                                    <!-- exception-reporting across a network. You may employ -->
                                                    <!-- your own implementation of this function. Note that  -->
       <script src = "AJS.js"           ></script>  <!-- ThrowException_Remote.js requires                    -->
       <script src = "AJS_DefMgr.js"    ></script>  <!-- ThrowException.js                                    -->
       <script src = "AJS_Logger.js"    ></script>
       <script>

       "use strict";

       var createLogger = createAJS_LoggerFactory (AJS, createAJS_DefMgr, throwException);

       // Calls to createLogger can now be made from hereon, as in the following:

       var MyLogger = createLogger ();

       // Other code from hereon...


       </script>
    </head>

    <body> ... </body>

 </html>
            

LogDefs

AJS_Logger defines the concept of a 'Logging Definition' (referred to hereinafter as a 'LogDef'), which is an object that defines which methods of a given object should be instrumented, such that the execution of a given method is recorded in some way.

LogDefs are very similar to the ValidationDefs with which you work when using AJS_Validator, and the LoadDefs that pertain to the use of AJS_ODL. That is: you create a LogDef object, and then submit it to a logger object, passing also a reference to an object (the 'MethodOwner') to which the LogDef should be applied. The logger takes care of things automatically from there on, according to the information contained in the LogDef.

A LogDef may possess zero or one each of the following case-sensitive properties (which are also presented in Example 3):

  1. A MethodNames property that, if present, must be an array of strings of non-zero length, where each string denotes a method of the MethodOwner to which logging should be applied.
  2. A preLogger function-reference that, if present, must refer to a user-defined function. The presence of a preLogger reference overrides the default logging-behaviour provided by the logger-object concerned on entry of the execution thread into a method.
  3. A postLogger function-reference that, if present, must refer to a user-defined function. The presence of a postLogger reference overrides the default logging-behaviour provided by the logger-object concerned on return from a method call.

Note that, if a LogDef possesses no MethodNames property then the logger object to which you pass the LogDef concerned will apply logging instrumentation to every method in the MethodOwner. However, empty MethodNames properties are disallowed because this would imply that no methods in the object in question should be logged, which would render the LogDef concerned redundant.

Note also that a LogDef may contain additional user-defined properties that the logger object to which it is passed will ignore. This is of value where Pre- and Post-loggers rely on additional resources to fulfill their remit.


 // -- Example 3 (pseudo code) ------------------------------------------------

 var MyLogDef   =
    {
    MethodNames :                                                    // Optional.
       [
       String,                                                       // Mandatory.
        .
        .
        .
       String                                                        // Optional.

       ],

    preLogger   : function (MethodName, Args,   Stream, LogDef) { }, // Optional.
    postLogger  : function (MethodName, RtnVal, Stream, LogDef) { }  // Optional.

    };
            

Validating AJS_Logger Method-Calls

As the product overview page explains, none of the functions and methods that comprise AJS_Logger validate the number, class, value etc. of the arguments that they receive. You could, for example, pass a LogDef to applyLogDef that contains a preLogger property that is not a function reference.

However, as the Product Overview page also explains, you can use the AJS object in conjunction with AJS_Validator to attach affixes to AJS_Logger-object methods, and these will perform all the validation that you need when developing code that calls those methods (with, if you so choose, none of the concomitant overhead when you deploy).

To use this facet of the library, read the Dog-Food Consumption section in the Product Overview page thoroughly, and then follow the pattern in Example 4. Doing so will ensure thereafter that any malformed LogDefs you pass to a given logger, and any method mis-calls will cause an exception, where the notification that is generated will carry an informative message that describes the nature of the trouble and its location, thus saving you a considerable amount of time and effort.


 <!-- Example 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

 <html>
    <head>
       <script src = "ThrowException.js"></script> <!-- Or ThrowException_Remote.js, or your own exception   -->
       <script src = "AJS.js"           ></script> <!-- handler. Note that ThrowException_Remote.js requires -->
       <script src = "AJS_DefMgr.js"    ></script> <!-- ThrowException.js.                                   -->
       <script src = "AJS_Validator.js" ></script>
       <script src = "AJSLogger.js"     ></script>
       <script src = "AJSLogger.val.js" ></script>
       <script>

       "use strict";

       var AJS_Validator = createAJS_Validator               (AJS, createAJS_DefMgr, throwException);
       var createLogger  = createAJS_LoggerFactory_Validated (AJS, createAJS_DefMgr, throwException, AJS_Validator);


       // Application code from here on.


       </script>
    </head>

    <body> ... </body>

 </html>
            

Enabling/Disabling AJS_Logger

AJS_Logger, like AJS_Validator, is a development tool, and so you need at times to enable and disable its functionality, as it is easier to step through method calls using a debugger when those methods are not subject to the AJS interception mechanism.

You can enable and disable AJS_Logger in the same trivial fashion that you would AJS_Validator by issuing a call to its suspend method after that component has been instantiated, and before your application makes any other logging-related calls. Subsequent calls to the methods that you use to apply LogDefs will then have no effect.

Given this, it follows that you can keep the suspend call in your code, where you comment it out when you do not need call logging, and where you remove the commenting when you do.