AJS_Logger is a singleton object that, like aJS_DbC is an adjunct to the core AspectJS technology. It allows applications that use AspectJS to instrument all calls to methods of the AJS object, and to any Affix and Wrapper objects that are generated thereon, and is therefore is of use in debugging and profiling an application's use of AspectJS.
Like aJS_DbC, AJS_Logger is a recursive application of the AJS object, in that it uses that object to apply affixes to its own methods. Subsequent calls to those methods invoke the relevant affixes, which inform the application with detailed information.
As with aJS_DbC, AJS_Logger is very simple to use.
To use AJS_Logger you should import the code that defines the object into your application first, remembering to import the AJS object as well.
Example 68 illustrates this.
From there, and to begin logging, you must first call the enable method of the AJS_Logger object. You must also pass a reference to the AJS object, and references to two call-back functions that your application defines, which represent the 'onMCall' and 'onMRtn' arguments respectively.
The form that these functions should take are explored in the next two sections.
<!-- Example 68 -->
</html>
</head>
<script type = "text/javascript" src = "AJS.js"></script>
<script type = "text/javascript" src = "AJS_Logger.js"></script>
</head>
<body> ... </body>
</html>
The OnMCall function ('On Method Call') is invoked by AJS_Logger whenever an AJS, Affix or Wrapper method is called. On invocation, it is passed the following optional arguments:
The function is free to do whatever it wishes with these arguments, and in the listing the function simply displays their values on screen. However, in a web application they could be piped back to the server using XMLHTTPRequest, thus yielding remote instrumentation of the application.
function onMCall (Obj, Method, IArgs, CallPoint)
{
alert ("Called: " + Obj + "." + Method + " - " + " CallPoint: " + CallPoint);
alert ("Arg[0] : " + IArgs[0]);
alert ("Arg[1] : " + IArgs[1]);
alert ("Arg[2] : " + IArgs[2]);
alert ("Arg[3] : " + IArgs[3]);
alert ("Arg[4] : " + IArgs[4]);
alert ("Arg[5] : " + IArgs[5]);
alert ("Arg[6] : " + IArgs[6]);
alert ("Arg[7] : " + IArgs[7]);
alert ("Arg[8] : " + IArgs[8]);
}
The OnMRtn function ('On Method Return') is called by AJS_Logger whenever an AJS, Affix or Wrapper method returns. It takes five optional arguments, which, in order, are as follows:
As with OnMCall, the function is free to do whatever it wishes with these arguments, and in the listing the function also displays their values on screen.
function onMRtn (Obj, Method, IArgs, IRtn, CallPoint)
{
alert ("Returned: " + Obj
+ "." + Method
+ " - Return Value: " + IRtn
+ " - CallPoint: " + CallPoint);
}
To begin logging AspectJS activity, call the enable method of the AJS_Logger object, passing a reference to the AJS object, and references to your OnMCall and OnMRtn functions.
Note that failure to pass the correct number and type of arguments to the enable method will raise an exception. In line with this, the enable method also accepts an optional CallPoint argument, which serves the same purpose as the CallPoint argument that aJS_DbC adds to the method signatures of AJS etc objects. (Although it is hard-coded into AJS_Logger, and does not arise through the use of aJS_DbC.)
The value of this argument is appended to the message contained within any Error objects thrown, meaning that the application can use it to denote the point in the code where the offending call was made.
Example 69 shows a call to the enable method (which uses the onMCall and onMRtn definitions shown above), and a subsequent call to AJS.addPrefix. That call is detected by the logging affixes attached to the addPrefix method, which generate the output shown.
Note also that should you make calls to the AJS object before calling the enable method then the methods of the Affix-objects generated by the AJS calls will not possess logging affixes, and their invocation will not show up in any logging trace. This may or may not be the effect you desire.
Moreover, should you use those Affix objects to apply other affixes (through the addBefore and addAfter methods) then the methods of the Affix objects returned will not possess logging instrumentation either, and so on.
Given these two issues, the point at which you enable logging for a given application deserves more than casual attention.
// Example 69
// onMCall and onMRtn not shown
function myFunc () { }
function prefixFunc () { }
AJS_Logger.enable (AJS, onMCall, onMRtn);
AJS.addPrefix (this, "myFunc", prefixFunc, null, Infinity, "Example 69 - Line 33");
--------------------------------------
Output:
Called: AJS.addPrefix - CallPoint: Example 69 - Line 33
Arg[0]: [object Window]
Arg[1]: myFunc
Arg[2]: function prefixFunc () { }
Arg[3]: null
Arg[4]: Infinity
Arg[5]: Example 69 - Line 33
Arg[6]: undefined
Arg[7]: undefined
Arg[8]: undefined
Returned: AJS.addPrefix - Return Value: [object Object] - CallPoint: Example 69 - Line 33
Logging can be suspended and resumed. Suspension suppresses the action of the logging affixes attached to AspectJS methods, such that calls to AspectJS methods occur normally and do not invoke onMCall and onMRtn.
Suspension, however, does not remove those logging affixes, meaning that resumption will cause them to re-commence reporting calls to AspectJS methods. Moreover, suspension does not halt application of new logging affixes whenever new interceptions are set on the application's methods.
Given this, application of an affix to a given method whilst logging is suspended will not be reported by AJS_logger, but it will continue to apply logging affixes to all relevant methods.
Example 70 illustrates these points. First, a prefix is applied to myFunc, which is reported, after which logging is suspended. A suffix is then applied to myFunc, which is not reported, after which logging is resumed. This means that a further call to add a second suffix to myFunc is reported, as is the call to the promote method of the first suffix, despite the fact that suffix was applied whilst logging was suspended.
Logging can be suspended and resumed as many times as desired, and at any point during the run of an application. But do note that, while suspension reduces the run-time overhead incurred by the logging affixes, it does not eliminate it completely.
Note also that, like the equivalent methods that Affix objects support, suspend and resume have stack-like behaviour. This means that two successive calls to suspend require two successive calls to resume in order to restore the output of logging information. Similarly, three calls to suspend require three to resume, and so on.
// Example 70
function onMCall (Obj, Method) { alert ("Called: " + Obj + "." + Method); }
function onMRtn (Obj, Method) { alert ("Returned: " + Obj + "." + Method); }
function myFunc () { }
function prefixFunc () { }
function suffixFunc () { }
AJS_Logger.enable (AJS, onMCall, onMRtn);
AJS.addPrefix (this, "myFunc", prefixFunc);
AJS_Logger.suspend ();
var SuffixObj = AJS.addSuffix (this, "myFunc", suffixFunc); // This addition will not be logged, but logging
// affixes will still be applied to methods of the
// Affix object generated
AJS_Logger.resume ();
AJS.addSuffix (this, "myFunc", suffixFunc); // This addition will be logged
SuffixObj.promote (); // Methods of SuffixObj will now report whenever
// when they are called
--------------------------------------
Output:
Called: AJS.addPrefix
Returned: AJS.addPrefix
Called: AJS.addSuffix
Returned: AJS.addSuffix
Called Suffix.promote
Returned Suffix.promote
Use of AJS_Logger can be combined freely with the use of aJS_DbC, thus allowing detection of bad calls to AJS, Affix and Wrapper methods to operate side by side with the logging of those calls.
In Example 71 (a contrived demonstration), aJS_DbC is called to put argument-checking affixes in place, and then AJS_Logger is invoked to set up the logging affixes.
One correct call is then made to the AJS object to apply a prefix to myFunc, and that is followed by a second call to addPrefix, where the quotes are missing from the argument denoting the interceptee.
The result is that the first call generates logging output, but the aJS_DbC argument-checking affix that is attached to AJS.addPrefix traps the missing quotes in the second call and throws an exception (which is the origin of the final message in the output).
It is important to understand here that the second call generates no logging output because calling aJS_DbC before AJS_Logger.enable causes argument-checking to happen before logging, and a bad argument will therefore preclude execution of the logging instrumentation.
This is significant because calling AJS_Logger.enable before aJS_DbC may cause bad calls to generate exceptions from within AJS_Logger (causing one to wonder if there is a bug in that component).
Clearly, it is far better that bad calls be detected explicitly by aJS_DbC (which, after all, is its raison d'etre), therefore if using aJS_DbC and AJS_Logger together, you should invoke them in the order shown in Example 71.
...And as a general observation: when working with a dynamically-evaluated language, one should log only well-formed code.
Note also that, because the action of aJS_DbC involves calls to AJS, Affix, Wrapper and interceptee methods, those calls generate their own logging output naturally, and do not indicate an erroneous interaction between the two components.
This can be seen in the output for Example 71, where the first two reports from AJS_Logger (which cite Point B as the call point) mark the entry to and exit from AJS.addPrefix. However, AJS.addPrefix has a DbC suffix that attaches DbC affixes to the addBefore etc methods of the Affix object that the initial call to AJS.addPrefix generated.
When this suffix executes, it makes its own calls to the AJS object in order to attach those affixes (it's Recursion City with this stuff). AJS_Logger also reports those calls faithfully, and that is the origin of the remaining messages that occur before the bad call at Point C is reported.
Note that the call point in such messages will (for fundamental reasons) always have the value of 'undefined' if aJS_DbC is invoked before AJS_Logger.enable. However, if AJS_Logger.enable is called after aJS_DbC (as per the discussion above) then the calls that the DbC affixes make to instrument Affix objects etc will possess meaningful call-point values when reported by the relevant logging affixes (and will denote points within the code that defines aJS_DbC).
// Example 71
function onMCall (Obj, Method) { alert ("Called: " + Obj + "." + Method); }
function onMRtn (Obj, Method) { alert ("Returned: " + Obj + "." + Method); }
function prefixFunc () { }
function myFunc () { }
aJS_DbC (AJS);
AJS_Logger.enable (AJS, onMCall, onMRtn, "Point A");
try
{
AJS.addPrefix (this, "myFunc", prefixFunc, null, Infinity, "Point B");
AJS.addPrefix (this, myFunc, prefixFunc, null, Infinity, "Point C");
}
catch (E) { alert (E.message); }
--------------------------------------
Output:
Called: AJS.addPrefix - CallPoint : Point B
Returned: AJS.addPrefix - CallPoint : Point B
Called: addbefore.addWrapper - CallPoint : undefined
Returned: addbefore.addWrapper - CallPoint : undefined
Called: addAfter.addWrapper - CallPoint : undefined
Returned: addAfter.addWrapper - CallPoint : undefined
Called: setExecs.addPrefix - CallPoint : undefined
Returned: setExecs.addPrefix - CallPoint : undefined
Called: setFunc.addPrefix - CallPoint : undefined
Returned: setFunc.addPrefix - CallPoint : undefined
Called: remove.addSuffix - CallPoint : undefined
Returned: remove.addSuffix - CallPoint : undefined
Error in call to AJS.addPrefix - I argument is not a string. Client-code call point: Point C
Go back to Part 8
...or get coding.