Skip to content

Commit

Permalink
RFC: inspect api (#859)
Browse files Browse the repository at this point in the history
* Setup initial skeleton

* Add tests for basic features of inspect api

* Implement inspect api

* Implement basic inspectGraph

* Remove factory for a while

idea needs rework

* Add owners support

* Remove useless owners code for a while

* Add tests for real-world use-cases

* improve inspectGraph tests

* Unify node meta approach

* Introduce regions

* Add more tests for regions tracking behavior

* Add use case with factories

* Add factories and regions support

* Add docs on `inspect` feature

* Update errors tracking according to the doc

* Update nested regions test

* Add minimal docs on inspect graph

* Imporve docs on inspect graph

* Add docs page to beta docs
  • Loading branch information
AlexandrHoroshih committed Mar 12, 2023
1 parent d526565 commit d54403b
Show file tree
Hide file tree
Showing 15 changed files with 1,500 additions and 15 deletions.
4 changes: 4 additions & 0 deletions beta/docs/.vitepress/sidebars.ts
Expand Up @@ -312,6 +312,10 @@ const commonSidebars: LSidebar = {
text: {en: 'launch', ru: 'launch'},
link: '/api/effector/launch',
},
{
text: {en: 'inspect', ru: 'inspect'},
link: '/api/effector/inspect',
},
],
},
{
Expand Down
179 changes: 179 additions & 0 deletions beta/docs/api/effector/inspect.md
@@ -0,0 +1,179 @@
---
title: inspect
lang: en-US
---

# Inspect API

Special API methods designed to handle debugging and monitoring use cases without giving too much access to internals of your's actual app.

Useful to create developer tools and production monitoring and observability instruments.

## Inspect

Allows to track any computations, that have happened in the effector's kernel.

```ts
import {inspect, type Message} from 'effector/inspect'

import {someEvent} from './app-code'

function logInspectMessage(m: Message) {
const {name, value, kind} = m

return console.log(`[${kind}] ${name} ${value}`)
}

inspect({
fn: m => {
logInspectMessage(m)
},
})

someEvent(42)
// will log something like
// [event] someEvent 42
// [on] 42
// [store] $count 1337
// ☝️ let's say that reducer adds 1295 to provided number
//
// and so on, any triggers
```

Computations tracking is restricted by [Scope](./Scope.md).
If no scope is provided - default out-of-scope mode computations will be tracked.

```ts
import {fork, allSettled} from 'effector'
import {inspect, type Message} from 'effector/inspect'

import {someEvent} from './app-code'

function logInspectMessage(m: Message) {
const {name, value, kind} = m

return console.log(`[${kind}] ${name} ${value}`)
}

const myScope = fork()

inspect({
scope: myScope,
fn: m => {
logInspectMessage(m)
},
})

someEvent(42)
// ☝️ No logs! That's because tracking was restricted by myScope

allSettled(someEvent, {scope: myScope, params: 42})
// [event] someEvent 42
// [on] 42
// [store] $count 1337
```

### Tracing

Adding `trace: true` setting allows to look up previous computations, that led to this specific one.
It is useful to debug the specific reason of some event happening

```ts
import {fork, allSettled} from 'effector'
import {inspect, type Message} from 'effector/inspect'

import {someEvent, $count} from './app-code'

function logInspectMessage(m: Message) {
const {name, value, kind} = m

return console.log(`[${kind}] ${name} ${value}`)
}

const myScope = fork()

inspect({
scope: myScope,
trace: true, // <- explicit setting is needed
fn: m => {
if (m.kind === 'store' && m.sid === $count.sid) {
m.trace.forEach(tracedMessage => {
logInspectMessage(tracedMessage)
// ☝️ here we are loggin the trace of specific store update
})
}
},
})

allSettled(someEvent, {scope: myScope, params: 42})
// [on] 42
// [event] someEvent 42
// ☝️ traces are provided in backwards order, because we are looking back in time
```

### Errors

Effector does not allow exceptions in pure functions. In such case branch computation is stopped and exception is logged.

There is also a special message type in such case:

```ts
inspect({
fn: m => {
if (m.type === 'error') {
// do something about it
console.log(`${m.kind} ${m.name} computation has failed with ${m.error}`)
}
},
})
```

## Inspect Graph

Allows to track declarations of units, [factories](./babel-plugin.md#factories) and [regions](./withRegion.md)

```ts
import {createStore} from "effector"
import { inspectGraph, type Declaration } from "effector/inspect"

function printDeclaration(d: Declaration) {
console.log(`${d.kind} ${d.name}`)
}

inspectGraph({
fn: (d) => {
printDeclaration(d)
}
})

const $count = createStore(0)
// logs "store $count" to console
```

## withRegion

Meta-data provided via region's root node is available on declaration.

```ts
import {createNode, withRegion, createStore} from "effector"
import { inspectGraph, type Declaration } from "effector/inspect"

function createCustomSomething(config) {
const $something = createStore(0)

withRegion(createNode({meta: {hello: 'world'}}), () => {
// some code
})

return $something
}
inspectGraph({
fn: (d) => {
if (d.type === "region")
console.log(d.meta.hello)
}
})

const $some = createCustomSomething({})
// logs "world"
```
1 change: 1 addition & 0 deletions docs/api/effector/index.md
Expand Up @@ -47,3 +47,4 @@ title: API Reference

- [clearNode](./clearNode.md)
- [withRegion](./withRegion.md)
- [Inspect API](./inspectApi.md)
179 changes: 179 additions & 0 deletions docs/api/effector/inspect.md
@@ -0,0 +1,179 @@
---
id: inspect
title: inspect
---

# Inspect API

Special API methods designed to handle debugging and monitoring use cases without giving too much access to internals of your's actual app.

Useful to create developer tools and production monitoring and observability instruments.

## Inspect

Allows to track any computations, that have happened in the effector's kernel.

```ts
import {inspect, type Message} from 'effector/inspect'

import {someEvent} from './app-code'

function logInspectMessage(m: Message) {
const {name, value, kind} = m

return console.log(`[${kind}] ${name} ${value}`)
}

inspect({
fn: m => {
logInspectMessage(m)
},
})

someEvent(42)
// will log something like
// [event] someEvent 42
// [on] 42
// [store] $count 1337
// ☝️ let's say that reducer adds 1295 to provided number
//
// and so on, any triggers
```

Computations tracking is restricted by [Scope](./Scope.md).
If no scope is provided - default out-of-scope mode computations will be tracked.

```ts
import {fork, allSettled} from 'effector'
import {inspect, type Message} from 'effector/inspect'

import {someEvent} from './app-code'

function logInspectMessage(m: Message) {
const {name, value, kind} = m

return console.log(`[${kind}] ${name} ${value}`)
}

const myScope = fork()

inspect({
scope: myScope,
fn: m => {
logInspectMessage(m)
},
})

someEvent(42)
// ☝️ No logs! That's because tracking was restricted by myScope

allSettled(someEvent, {scope: myScope, params: 42})
// [event] someEvent 42
// [on] 42
// [store] $count 1337
```

### Tracing

Adding `trace: true` setting allows to look up previous computations, that led to this specific one.
It is useful to debug the specific reason of some event happening

```ts
import {fork, allSettled} from 'effector'
import {inspect, type Message} from 'effector/inspect'

import {someEvent, $count} from './app-code'

function logInspectMessage(m: Message) {
const {name, value, kind} = m

return console.log(`[${kind}] ${name} ${value}`)
}

const myScope = fork()

inspect({
scope: myScope,
trace: true, // <- explicit setting is needed
fn: m => {
if (m.kind === 'store' && m.sid === $count.sid) {
m.trace.forEach(tracedMessage => {
logInspectMessage(tracedMessage)
// ☝️ here we are loggin the trace of specific store update
})
}
},
})

allSettled(someEvent, {scope: myScope, params: 42})
// [on] 42
// [event] someEvent 42
// ☝️ traces are provided in backwards order, because we are looking back in time
```

### Errors

Effector does not allow exceptions in pure functions. In such case branch computation is stopped and exception is logged.

There is also a special message type in such case:

```ts
inspect({
fn: m => {
if (m.type === 'error') {
// do something about it
console.log(`${m.kind} ${m.name} computation has failed with ${m.error}`)
}
},
})
```

## Inspect Graph

Allows to track declarations of units, [factories](./babel-plugin.md#factories) and [regions](./withRegion.md)

```ts
import {createStore} from "effector"
import { inspectGraph, type Declaration } from "effector/inspect"

function printDeclaration(d: Declaration) {
console.log(`${d.kind} ${d.name}`)
}

inspectGraph({
fn: (d) => {
printDeclaration(d)
}
})

const $count = createStore(0)
// logs "store $count" to console
```

## withRegion

Meta-data provided via region's root node is available on declaration.

```ts
import {createNode, withRegion, createStore} from "effector"
import { inspectGraph, type Declaration } from "effector/inspect"

function createCustomSomething(config) {
const $something = createStore(0)

withRegion(createNode({meta: {hello: 'world'}}), () => {
// some code
})

return $something
}
inspectGraph({
fn: (d) => {
if (d.type === "region")
console.log(d.meta.hello)
}
})

const $some = createCustomSomething({})
// logs "world"
```

0 comments on commit d54403b

Please sign in to comment.