DEV Community

Akram Tabka
Akram Tabka

Posted on • Updated on

Execution workflow tracing: a guide to execution-engine library (part 1)

Introduction

Welcome to an exploration of the powerful capabilities offered by the execution-engine library.
In this guide, we delve into the art of tracing the execution flow within your TypeScript/JavaScript projects.

Unleash the magic of tracing your codebase and visualize its rhythmic dance.

Why Trace Your Code Workflow 🕵️‍♂️

Tracing your code offers a visual roadmap to understand, optimize, and debug projects. Here's why tracing matters:

1. Uncover Function Flow: Tracing reveals the exact sequence of function executions, simplifying your project's flow comprehension.

2. Visualize Code Relations: Generate a visual graph from the trace to easily grasp relationships between different functions.

3. Capture Details: Traces include input, output, and error specifics, aiding in issue resolution and data analysis.

4. Monitor Timing: Understand the timing and duration of each function execution to identify performance bottlenecks.

5. Analyze Call Hierarchy: Explore how functions are called at different levels, revealing entry points and data flow patterns.

Installation 📦

Ready to experience the benefits? Install the execution-engine library and let's dive into practical examples!

We'll be using version 2.0.1 for these examples.

You can find the library on npm and its source code on GitHub.

Using npm:

npm install execution-engine@2.0.1
Enter fullscreen mode Exit fullscreen mode

Using yarn:

yarn add execution-engine@2.0.1
Enter fullscreen mode Exit fullscreen mode

Make sure to check the npm package page for any updates or additional information. If you're interested in exploring the source code or contributing, visit the GitHub repository.

Examples: Tracing Your Code in Action 🚀

1. Basic Usage

Example Code:

import { ExecutionEngine } from "execution-engine";

const engine = new ExecutionEngine();

// for sync functions:
const res1 = engine.run((param) => `result1 for ${param}`, ['param1']);

// for async functions:
const res2 = await engine.run(async (param) => `result2 for ${param}`, [res1.outputs]);

// Retrieve the trace
const trace = engine.getTrace();
console.log('Trace:', trace);
Enter fullscreen mode Exit fullscreen mode

Trace Output:
Link to Example 1 Trace Output

Trace Graph:
Link to Example 1 Trace Graph

2. Advanced Usage with Decorators

Example Code:

import { engine, run } from "execution-engine";

@engine({ id: "uniqueEngineId" })
class MyClass extends EngineTask {
  @run()
  myMethod1(param: string) {
    // Method implementation
  }

  @run()
  async myMethod2(param: string) {
    // Async method implementation
  }
}

const myInstance = new MyClass();
myInstance.myMethod2("param1");
await myInstance.myMethod2("param2");

// Retrieve the trace
const trace = myInstance.engine.getTrace();
console.log("Trace:", trace);
Enter fullscreen mode Exit fullscreen mode

Trace Output:
Link to Example 2 Trace Output

Trace Graph:
Link to Example 2 Trace Graph

In these examples, we illustrate two fundamental approaches to utilizing the ExecutionEngine library, enabling you to trace and visualize the execution flow of your code.
Take the plunge, navigate the traces, and unlock the secrets to your project's dynamic dance moves! 🕺✨

Customizing Tracing Configuration 🛠️

Customize tracing configurations for executed functions or methods in the ExecutionEngine library by using the traceOptions parameter. This flexibility is available both when using .run() and with the @run() decorator. Specify configurations through a simplified format or a comprehensive object, offering flexibility in customization.

Trace Options

The traceOptions parameter can be provided in two formats:

Simple Format:

{
  id: string;
  label: string;
  parent?: string;
}
Enter fullscreen mode Exit fullscreen mode

Full Trace Options Object:

{
  trace: {
    id: string;
    label: string;
    parent?: string;
  };
  config?: {
    traceExecution?: boolean | Array<keyof NodeExecutionTrace<I, O>> | NodeExecutionTraceExtractor<I, O>;
    parallel?: boolean | string;
    errors?: 'catch' | 'throw';
  };
}
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Simple Format: Directly specify the id, label, and optional parent properties for basic tracing information.

  • Full Trace Options Object: For more control, use the complete object format. It includes the trace property for trace-related details and the config property for additional tracing configurations.

    • traceExecution: Control what to trace exactly in the execution. Options include a boolean, an array of keys from NodeExecutionTrace, or a custom extractor function.
    • parallel: Enable or disable parallel execution. Choose a boolean or a string.
    • errors: Specify the error handling strategy. Choose between 'catch' to catch errors or 'throw' to let errors propagate.

Conclusion

This guide has provided an overview of the execution-engine library, shedding light on the intricacies of code tracing. Now, you can dive into more examples here and customize your tracing configuration effortlessly for each function or method, gaining precise insights into the execution flow of your code. Happy tracing! 🚀

Additional Resources

Top comments (1)

Collapse
 
tabkram profile image
Akram Tabka • Edited

📚 For more in-depth exploration of TraceOptions and advanced code tracing, check out the companion post:

Happy exploring! 🌐✨