Extending Lens

On this page

Extending Lens

Every BoxLang module has its own class loader, so a module cannot implement Java interfaces from inside bx-lens. Lens therefore uses a data-first contract. You declare a panel and fill it with data. Lens renders it with a built-in renderer, so you ship no JavaScript.

This is the only extension tier in v1. Java collectors and custom UI are planned.

A custom Harness panel built from application code

Interception points

Lens registers these points. Your code only listens.

PointWhenPayload
onLensRegisterAt Lens activation, and again after all modules activatedata.registry.panel( id, label, icon, order, renderer )
onLensRequestStartPer request, after the request object existscontext, requestId
onLensCollectPer request, before renderdata.lens.panel( id ) handle to add data, and data.requestId
onLensRequestFinishPer request, after it is storedrequestId, summary

From a module

Declare the panel once, then fill it for each request. The harness ships a working example in harness/home/modules/demoLens.

Register an interceptor in your module:

function configure() {
	settings = {};
	interceptors = [
		{ class : "interceptors.DemoLens", name : "DemoLens@demoLens", properties : {} }
	];
}

Then write the interceptor:

class {

	function onLensRegister( data ) {
		data.registry.panel( "demo", "Demo module", "bolt", 110, "kv" );
	}

	function onLensCollect( data ) {
		data.lens.panel( "demo" )
			.kv( {
				module    : "demoLens",
				requestId : data.requestId,
				note      : "This panel comes from another module"
			} )
			.badge( 1, "none" );
	}

}

A panel contributed by the demoLens module

From app code

You can build a panel without a module. Call lensPanel anywhere in a request.

lensPanel( "harness", "Harness", "table" )
	.columns( [ "Feature", "Source", "Count" ] )
	.rows( [
		[ "Custom panel", "application code", 1 ],
		[ "Panel from a module", "demoLens module", 1 ]
	] )
	.badge( 2, "none" )
	.order( 95 );

See harness/app/extend.bxm for the full page.

Renderers

RendererBuilder methodUse it for
tablecolumns( [...] ), rows( [...] )Rows and columns. A row is an array, or a struct keyed by column title.
kvkv( struct )Key and value pairs.
treetree( nodes )Nested data. Each node has label, optional ms and optional children.
spansspans( [...] )Timed items. These feed the Timeline.
messagesmessages( [...] )Log style lines. Each has level, text and optional ms.
jsonjson( value )Raw structured data.
texttext( value )Plain text.

Spans

A spans panel supplies a label, a start, a duration and a type for each item. Lens draws them in the Timeline with the custom type.

lensPanel( "phases", "Phases" )
	.spans( [
		{ label : "warm up", start : 0.5, dur : 1.5, type : "custom" },
		{ label : "render", start : 2.5, dur : 2, type : "custom" }
	] );

Issues

A panel can add issues. They show among the issues in the console request detail. The bar does not show issues.

lensPanel( "orm", "ORM" )
	.issue( "warn", "Slow flush", "Flushing Order took 31 ms", "/app/models/Order.bx", 88 );

The arguments are severity, title, detail, file, line.

Safety

Module and app data goes through the same redaction, caps and escaping as built-in collectors. The UI always shows text as text. Panels cannot ship HTML or script.