API
How to use RevDeBug API and interact with RevDeBug on the runtime.
Runtime RevDeBug API
RevDeBug exposes its runtime API from a global reachable object named revdebug
:
Method
revdebug.exception(error)
Signal an unhandled exception to be recorded and sent to RevDeBug server. Useful when errors are caught in the code and should still be recorded as such by RevDeBug.
snapshot(name)
Send a snapshot of the current thread execution recording, name
is optional and will be an automatically generated increasing sequence name if not provided. Recording will be available on the "Recording" tab inside the application section of top-level "Monitor" section of RevDeBug server.
setRecMode(mode)
Set the current recording mode to Continuous / Live, OnEvent / Crash or Off. The recording mode can only be set as high as the server allows, so if the server does not allow continuous recording then that will not happen and only unhandled exceptions will be sent to the server. The available recording modes are revdebug.Off
, revdebug.Continuous,
revdebug.Live
, revdebug.OnEvent
and revdebug.Crash
. Continuous and Live are aliases as are OnEvent
and Crash
.
str(obj)
Convert object to a string using standard RevDeBug stringification. Intended for use in custom user __revdrec()
stringification functions for stringifying subobjects. Enforces maximum depth and protects against circular reference cycles. This function will return an incorrect depth recursion level for objects ourside of a __revdrec()
function. For a correct depth there use strd()
.
strd(obj[, depth])
The way object recursion depth is tracked in stringification necessitates a separate function to be able to get the same recursion depth level for an object outside of __revdrec()
custom stringification handlers as inside. Calling this function without a depth
parameter anywhere will return an object recursed to the default depth level set for the project, regardless of how many nested stringification levels down the code currently is. You can also specify an explicit depth
level for this call, also regardless of current nesting level. Like str()
this function also protexts against circular reference cycles.
Custom JavaScript project layouts
The RevDeBug runtime code can be incorporated into your project in one of several ways, direct inject
into your source or index.html files, local
import or global
import or separate script load in the case of web
projects.
For inject
, the injection can be either into a Javascript source file or an HTML-ish index file. For a Javascript injection, a main
file must be specified and the RevDeBug code is inserted directly into this file and this file must be the entry point of execution. This may get tricky with import
declarations since those are always executed before the actual file that does the importing, and hence their contents are executed first. main
must be the first file that executes CHRONOLOGICALLY, not necessarily the topmost level file.
For an inject
into an HTML-ish index file an index
filename must be provided and this file will be scanned for a <head>
section. Once this is found the entire RevDeBug runtime script is inserted as the first script to run in this section.
For local
, the RevDeBug runtime is copied into the topmost level directory of the project or subproject as __revdebug.js
and the individual project files import
or require()
this file as needed. In this mode, as with global
, there is no main entry point and any of the project files may be executed first. For web
projects using this mode the individual Javascript scripts must be loaded as modules.
For global
the behavior is different between node
and web
projects. For a node
project the runtime is loaded via import
or require()
but unlike a local
path like import "../__revdebug"
the runtime is assumed to be available either globally or as an installed NPM module dependency and is imported with an absolute path like import "revdebug"
.
For a global
web
project the runtime is assumed to have been loaded as an individual script previously in an HTML file somewhere via something like <script src="__revdebug.js"></script>
where PATH is the top-level path of your web project where the runtime will be copied. This must be loaded and executed before any Javascript files which were instrumented. If you provide an index
filename then the script tag described will be automatically inserted into that file, otherwise, you are responsible for loading the RevDeBug runtime somewhere yourself.
Subprojects, RevDeBug and the global scope
RevDeBug works by injecting itself into the global scope where it is running and recording a project in that scope. There can only be one project or subproject per scope because otherwise, their instrumentation IDs would overlap. This means that for a given node
or web
project you should have only one main project. But if your overall solution comprises a node server and a web component for example then each should be its own subproject. The exception to this is when using WebWorkers or worker threads as those each gets their own global scope when they are created and can thus host a completely different project from the code which created them.
What this means basically is that all code that will run in the same global scope of your solution should be part of the same project. However, if you have multiple types of workers or threads which run in their own global scopes then each one can have its own project with different options. You could include them all in a single project if you wish but they would have to share the same options such as target
, runtime
and type
which may be inconvenient or impossible across subprojects.
Post-processing options and source maps
Post-processing options of RevDeBug allow multilevel mapping of source maps back to original pre-RevDeBug uninstrumented source code in case more processing is done after RevDeBug instrumentation of original source files (like for example with a Typescript compiler or webpack).
This is only really useful for web projects in order to be able to see the original source code in a dev console or correct line numbers in stack traces - RevDeBug will present the recordings on the original source code either way.
In order to preserve the information needed for post-processing you must turn on source maps when instrumenting with RevDeBug, as well as in whatever compilation steps you take after ReDeBug instrumentation. After you have instrumented your source files like this and after completing whatever compilation steps follow RevDeBug, when you have your final output files, you will run revd --post
a second time so that it can merge the two levels of source maps (one from the RevDeBug instrumentation pass and the second from the following compilation passes) into a single level of source maps pointing back to the original code.
In order to do this you will need to specify postPath
where the final output files live along with their source map files. if postPath
is not provided it will default to the root directory of the project. Likewise, you will need to specify postFiles
in the same way that files
are specified. The postFiles
may be a single output webpacked file or multiple files selected via wildcards. An example would be source files
of *.ts
and postFiles
as *.js
to process all the Javascript files compiled from original Typescript files.
revd --post
will only process output files once, running it again after this will have no effect on files that have already been processed, but will reprocess any individual files which may have been recompiled.
Note: As of the time of writing this RevDeBug only works with individual standalone source map files, it does not work with source maps embedded in Javascript files.
Custom object stringification
If any object has a __revdrec
method on itself or in its prototype chain then this method will be called on the object during recording when the value of the object needs to be stringified. The object is assumed to be mutable so this call happens whenever the object is accessed in code that is being recorded. The call is of the form obj.__revdrec(obj)
so the actual object can be accessed as this
or in case of arrow function as the first argument.
The method should return a string which will be shown as the value of the object at this point. Any exceptions in this method are suppressed but shown as the value of the object. Recording is turned off when this function is called and in fact by default the body of the function is not instrumented by RevDeBug for performance (or actually any function which starts with "__revd").
The size of the string returned by this function is not truncated in any way by RevDeBug so keep this in mind if your function can potentially generate huge stringified values. RevDeBug takes care of detecting and truncating recursive object cycles. RevDeBug's own stringification functions are made available to use in your own stringification function via revdebug.str(obj)
or more specifically revdebug.str.type(obj)
where type
is one of the primitive names function
, boolean
, number
, bigint
, array
or object
. Do not call a type function on an object which is not of that type, if you are not sure of the type then use revdebug.str(obj)
.
Stringification using this method is not affected by the global mutable mode which is set. Objects with these functions will always be stringified at record time even if this is turned off for all other objects with mutable: 'none'
.
Recording code block isolation
This feature allows the isolation of individual async task trees from their siblings in order to get a recording of an individual service handler or request. When you declare an async task block via revdebug.block()
you are marking this async context as well as any child async tasks it creates as isolated, meaning that if a revdebug.snapshot()
or an revdebug.exception()
in this async context or its children it will only include the task and its children in the recording, not siblings or parent. When used in the APM portion of RevDeBug, this allows the isolation of a single given HTTP handler for example which may have an error from any number of other handlers which may be running concurrently.
The particular details of how async context propagates in Node are a little wonky and possibly subject to change in future versions of the V8 Javascript engine used in Node. This means that in order to use the block()
functionality correctly you need to understand when the async task context changes in async code (it does not change for example upon initial entry into an async
function). That information is beyond the scope of this documentation but is available in the documentation for the Node async_hooks
package.
The idea is that at the start of the code you want to isolate you call block = revdebug.block()
, and when you are done with the part you want to isolate simply call revdebug.async()
. If you need to return synchronous control to some other async task without ending your isolation block yet (it will get control later), do the async()
before returning control to non-isolated code. Upon re-entry to your code which you want to isolate execute a lastBlock = block.resync()
, execute your code, and return to the previously executing context via block.async(lastBlock)
. The lastBlock
here is important because it allows recursive isolation of code blocks, otherwise the wrong previous context may be set if simply calling async()
(though this is ok after the initial revdebug.block()
because in this case we are sure we did not recurse into this block.
One last detail about this functionality is that the isolation can be total, only code in the block is shown as part of the recording. Or it can be partial where all code is included in the recording up until the initial revdebug.block()
, after which only code that is part of the block is shown. This is specified as either no parameter or a falsey parameter to revdebug.block(falsey)
for complete isolation, or a truthy parameter for partial isolation.
Environment Variables
The following environment variables can override various configuration settings at runtime (for NodeJS projects):
Note: REVDEBUG_APM, REVDEBUG_BLOCKS, REVDEBUG_CLASSPROPS, REVDEBUG_STDOBJECTS and REVDEBUG_TYPEDARRAYS should be "true" or "false".
Possible minification issues when working on the runtime
The pseudoMinify() function which is applied to the runtime when it is injected into javascript or HTML files can potentially break the code if it finds a "// " (slash, slash, space) sequence inside a string constant, so be aware of it.
Also, pseudoMinify() will not remove /* */ style comments.
If you are getting a minified production runtime that works, but a non-minified one that does not then most likely you did not terminate a "name = function(){}" or "name = class {}" with a semicolon ";". All such assignments must be terminated like this since newlines are removed in pseudo-minification and won't act as delimiters.
Last updated