Skip to content

Debugging

zalky edited this page Feb 9, 2023 · 3 revisions

Reflet provides a powerful visual debugger that is ideally suited to inspecting complex, data driven apps.

While tools like re-frame-10x provide an excellent global lens into your application, they are not ideal for graph data exploration. At the same time, reducing the signal to noise ratio in a global lens can sometimes be a challenge with large apps.

In contrast, the Reflet debugger immediately places you in the local context of your components, their data, and interdependencies. Understanding these connections comes naturally and intuitively from Reflet's design philosophy, where references connect everything together. References form the backbone of your API, and are the natural entry point for exploration.

Ultimately, this approach is meant to be complementary to, rather than replace other tools. For one thing, the Reflet debugger only handles graph data. But because both graph and non-graph data models can co-exists perfectly fine in a Reflet application, other tools are still indispensable.

The Reflet example client offers a working example of the debugger in action.

It should also be noted that the Reflet debugger was written entirely in Reflet, and demonstrates most of the features described in these documents in a real, complex use-case.

Starting with with-ref

Your with-ref component API is the entry point for debugging. The Reflet debugger overlays this API directly on top of your application in the browser, and you can toggle it on or off by pressing Ctrl + j:

The hotkey character j is configurable as described in the Configuration document.

Each purple marker indicates a with-ref at that point in the DOM tree. Multiple overlapping with-refs are collected together into one group marker:

Whether it is a single or group marker, mousing over it will open a list of available with-ref entry points. Clicking on an entry point will open a with-ref "props" panels:

This panel will show the props map of the with-ref. The panel header includes the component name, the component namespace, and the line number of the with-ref occurrence. Additionally, all debugger panels are draggable, and can be minimized by double clicking on the header.

The props map in the content area will include:

  • Any new refs that the with-ref created and added to props
  • Any refs that were passed in to the with-ref
  • Any additional prop values that were merged in

In this example, the props map includes 4 new refs that the with-ref generated: :player/self, :player/context, :player/source, and :player/el, and a 5th property, :app/self, which was created by another with-ref in the parent reflet.client.ui/app component.

Any refs that appear in the debugger are clickable. Most of them will appear in a condensed form, meaning that:

[:cmp/uuid #uuid "b235f3f6-f529-4dc3-8583-5289e4d93625"]

will be displayed using the namespace or name of the unique attribute and the last 8 characters of the UUID:

cmp@e4d93625

Note: obviously truncating the UUID in this way means it is possible that two unique UUIDs might be rendered to appear the same. However in practice, the productivity benefits that this condensed representation brings are substantial enough that they far outweigh the small chance of a conflict. Keep in mind that total number of UUIDs that will appear on screen during debugging at any one time is actually quite small.

If you want to see the fully expanded value of the reference, simply right-click on the ref to open a context menu:

Similarly you can right-click on any data value that is not a collection to see the full value in a context menu.

Left-clicking on any ref will open a "ref" panel, which immediately lets you choose from 4 available lenses for exploration: DB, EVENTS, QUERY, FSM:

There are also two special panel types that are available for the unique attributes :js/uuid and :el/uuid. These unique attributes are used for JS objects and DOM elements respectively. The current implementation for these panels is still somewhat limited, but for now they show whether a JS object has been initialized:

and a reactive serialization of the DOM element:

DB Lens

The DB lens shows the graph entity that is associated with that reference, as it appears in the Reflet db.

For example, the component local state of the reflet.client.ui.player/player component:

And the domain data associated with the "Orbits" track:

Every collection type can be expanded vertically by left-clicking a toggle:

When a nested collection is expanded vertically within a panel, all entity references are clickable and will open to other panels.

However, you can also right-click on the collection toggle button top open a plain-text value of the collection for copy-pasting:

Finally, note the little sandwich menu icon in the top left corner of the panel. Clicking on this will return you to the reference lens menu, where you can choose another lens to explore. This sandwich icon appears in all of the panels, except for the with-ref props panel.

Event lens

Any events that a reference participates in will show up in the event lens. A ref participates in events in two ways:

  1. An entity associated with the ref was touched by a graph data operation as a result of the event

  2. The ref was one of the positional arguments of the event (collection arguments are not checked recursively)

Each event trace in the lens has associated with it the db t at which the event occurred. This also corresponds with the t given in the query and FSM lenses. Recall from the Multi Model DB document that this t is monotonically increasing, but not necessarily contiguous. It is normal to see jumps in t between events that may have happened sequentially. Do not rely on t increasing contiguously.

Query Lens

Any graph query that a reference participates in will show up in the query lens:

A ref participates in a query if it is reached at any point during query traversal. Every graph query that touched the entity associated with the reference will show up in the Query lens.

Each trace will list:

  1. The db t at which the query was last run (not a cached result)
  2. The query vector of the invoking subscription
  3. The result of the query

Note that each trace provides a set of controls for navigating the values of the query over time. The debugger will keep a history of values determined by the Reflet configuration parameter :trace-queue-size, which defaults to 50 values.

For graph queries with a result function, the result function will show up as a separate trace, with a modified id in the query vector. For example, given a query vector:

[::query arg1 arg2]

The result query vector would be:

[::query[result-fn] arg1 arg2]

FSM Lens

Every FSM currently running that is associated with the reference via the :ref identity, will appear in the FSM lens:

Each FSM trace will list:

  1. The db t at which the FSM last transitioned; the very first trace is not technically a transition, but an FSM initialization trace
  2. The invoking FSM vector: this is either a subscription vector, or the vector that was passed to the ::fsm/start event
  3. The transition FROM -> TO states
  4. A number of additional attributes that includes:
    • The event that triggered the transition
    • Then :when conditional clause, if provided
    • The :pull expression, if provided

As with the query traces, each FSM trace provides a set of controls for navigating previous transitions of an FSM. Once again the number of retained transitions is control by the :trace-queue-size Reflet configuration parameter.

Panels and Global Controls

There is a set of global controls that appear at the bottom of the browser window whenever the debugger overlay is active:

For now this area only hosts a button that closes all the overlay panels. However, this could potentially host future features such as:

  1. Search for an explicit ref to open in a panel

  2. Time travel controls as you get in re-frame-10x

Note that activating or de-activating the debugger overlay only toggles the purple with-ref markers. In contrast, once open, the debugger panels persist until closed.


Next: Testing

Home: Home