Skip to content

Performance Optimization ​

Best practices for minimizing the performance impact of AutoTracer in your applications.

Overview ​

AutoTracer introduces some overhead for logging and instrumentation. These guidelines help you minimize that impact while maximizing debugging value.

General Principles ​

1. Development-Only by Default ​

The best performance optimization is to keep tracing out of publicly accessible builds.

typescript
// vite.config.ts
export default defineConfig(({ mode }) => ({
  plugins: [
    reactTracer.vite({
      inject: mode === "development",
    }),
    react(),
  ],
}));

2. Use Dormant Mode ​

When tracing must be available in a restricted internal test/QA deployment, use dormant mode:

typescript
// Entry file (run once, before using runtime controls)
async function bootstrap(): Promise<void> {
  await import("@autotracer/flow/runtime"); // Starts dormant

  await import("./app");
}

void bootstrap();

// In browser-based internal web apps, start tracing from the Dashboard when it is mounted.
// In non-Dashboard setups, activate only when needed:
globalThis.autoTracer.flowTracer.start();

Use @autotracer/flow instead when you want tracing available immediately on startup.

Performance Impact:

  • Dormant mode: Logging starts off; overhead is typically low until you activate tracing.
  • Active mode: Overhead varies widely and is often dominated by console output volume, DevTools rendering, and object formatting.

In large apps, treat tracing as an interactive debugging tool: enable it right before the action you care about, then turn it off immediately.

3. Selective Tracing ​

Trace only what you need:

javascript
// ✅ Good: Specific patterns
{
  "include": {
    "functions": ["handle*", "on*", "fetch*"]
  }
}

// ❌ Bad: Trace everything
{
  "include": {
    "functions": ["*"]
  }
}

ReactTracer Performance ​

Component-Level Optimization ​

1. Exclude High-Frequency Components ​

typescript
// @trace-disable
function HighFrequencyTimer() {
  const [tick, setTick] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => setTick((t) => t + 1), 100);
    return () => clearInterval(interval);
  }, []);

  return <div>{tick}</div>;
}

Why: Components that re-render on frequent timers or rapid user input create excessive console output.

2. Use Opt-In Mode for Large Apps ​

typescript
reactTracer.vite({
  mode: "opt-in", // Only trace marked components
});
typescript
// Only trace components you're debugging
// @trace
function FeatureComponent() {
  const [state] = useState(0);
  return <div>{state}</div>;
}

Note: include/exclude component filters are evaluated before pragma signals. @trace only operates within the eligible set.

Benefit: Substantially reduces output volume and DevTools load in large apps.

3. Exclude Third-Party Components ​

typescript
reactTracer.vite({
  include: {
    paths: ["src/**"],
  },
  exclude: {
    paths: ["**/node_modules/**", "**/lib/**", "**/vendor/**"],
  },
});

Hook-Level Optimization ​

Limit Traced Hooks ​

typescript
reactTracer.vite({
  labelHooksPattern: "^use(State|Effect|Reducer)$", // Only specific hooks
});

Why: Custom hooks and rarely-used hooks may not need tracing.

Reduce State Depth ​

typescript
reactTracer({
  // Prefer interactive objects in DevTools to avoid deep stringification.
  outputMode: "devtools",

  // Cap traversal in extremely deep trees.
  maxFiberDepth: 80,
});

Benefit: Reduces work in very deep trees and avoids heavy stringification.

FlowTracer Performance ​

Function-Level Optimization ​

1. Target Specific Functions ​

javascript
// ✅ Good: Only event handlers and API calls
{
  "include": {
    "functions": ["handle*", "on*", "fetch*", "api*"]
  }
}

// ❌ Bad: All functions
{
  "include": {
    "functions": ["*"]
  }
}

Impact: Can materially reduce overhead by reducing the amount of traced work and logged output.

2. Exclude Performance-Critical Code ​

javascript
{
  "exclude": {
    "functions": ["render*", "update*", "draw*", "calculate*"]
  }
}

Use cases:

  • Animation loops
  • Canvas rendering
  • Game loops
  • Real-time data processing

3. Avoid Tracing Utility Functions ​

javascript
{
  "exclude": {
    "functions": ["get*", "set*", "is*", "has*", "_*"]
  }
}

Why: Utility functions are called frequently but rarely need debugging.

Call Depth Management ​

Limit Exception Logging ​

typescript
flowTracer({
  // If you don't need stack traces for exceptions, avoid injecting catch logging.
  // This reduces log volume and avoids capturing/logging Error.stack in hot paths.
  logExceptions: false,
});

Impact: Logging exceptions typically includes Error.stack, which can be expensive under heavy error rates.

Control Output Verbosity ​

typescript
reactTracer({
  outputMode: "copy-paste", // Avoid console.group() overhead
});

Why: console.group() has higher overhead than console.log().

Build-Time Optimization ​

Vite Configuration ​

1. Minimize Plugin Processing ​

typescript
flowTracer({
  include: {
    paths: ["src/**"], // Specific paths only
  },
});

2. Exclude Test Files ​

typescript
{
  "exclude": {
    "paths": ["**/*.test.*", "**/*.spec.*", "**/__tests__/**"]
  }
}

Impact: Can reduce build-time work by avoiding unnecessary transformations.

Babel Configuration ​

1. Conditional Plugin Loading ​

javascript
// Only load plugins when needed
const plugins = process.env.YOUR_TRACING_FLAG
  ? [
      [
        "@autotracer/plugin-babel-flow",
        {
          /* config */
        },
      ],
    ]
  : [];

module.exports = {
  presets: ["next/babel"],
  plugins,
};

Runtime Optimization ​

Console Output Management ​

1. Start Dormant Outside Dev ​

typescript
async function initializeDormantFlowTracing(): Promise<void> {
  if (import.meta.env.DEV || import.meta.env.VITE_INTERNAL_QA === "true") {
    await import("@autotracer/flow/runtime");
  }
}

void initializeDormantFlowTracing();

// Tracing starts dormant; activate on demand:
// globalThis.autoTracer.flowTracer.start();

2. Exclude Hot Paths ​

typescript
// Prefer excluding performance-critical paths from instrumentation.
// Example (Vite):
//
// flowTracer({
//   exclude: { paths: ["src/hot-paths/**"] },
// });

Note: FlowTracer does not provide a generic maxDepth runtime option. If you need manual logging beside FlowTracer, use @autotracer/logger and project the specific fields you need before logging; the logger package does not export a generic depth-bounded helper.

Memory Management ​

1. Avoid Retaining Large Objects ​

typescript
// ❌ Bad: Traces retain large objects
function processData(hugeDataset) {
  // hugeDataset logged and retained in console
  return hugeDataset.map(process);
}

// ✅ Good: Exclude the heavy function at build time
{
  exclude: {
    functions: ["processData"]
  }
}

Real-World Scenarios ​

Scenario 1: Large React Application ​

Problem: 500+ components, tracing slows app significantly.

Solution:

typescript
// Use opt-in mode
reactTracer.vite({
  mode: "opt-in",
  include: {
    paths: ["src/features/problematic-feature/**"],
  },
});

// Only trace components being debugged
// @trace
function ProblematicComponent() {
  // ...
}

Result: Substantial reduction in tracing overhead.

Scenario 2: Real-Time Dashboard ​

Problem: Dashboard updates 10 times/second, console flooded.

Solution:

typescript
// Exclude high-frequency components
reactTracer.vite({
  mode: "opt-out",
  exclude: {
    paths: ["src/components/dashboard/**", "src/components/charts/**"],
  },
});

Result: Dramatic reduction in console spam; DevTools is much more likely to remain usable.

Scenario 3: Complex Function Flow ​

Problem: Deeply nested function calls create performance bottleneck.

Solution:

javascript
// Trace only top-level functions
{
  "include": {
    "functions": ["handle*", "process*"]
  },
  "exclude": {
    "functions": ["_*", "internal*", "helper*"]
  }
}

Result: Fewer traced functions and clearer output.

Performance Checklist ​

Development ​

  • [ ] Tracing enabled only in development mode
  • [ ] High-frequency components excluded
  • [ ] Third-party libraries excluded
  • [ ] Test files excluded from tracing
  • [ ] Specific function patterns used (not "*")

Staging/QA ​

  • [ ] Dormant mode enabled
  • [ ] Manual activation only
  • [ ] Narrow include/exclude filters
  • [ ] Avoid exception logging in hot paths (if you don't need it)
  • [ ] Opt-in mode for specific features

Production ​

  • [ ] Tracing completely disabled (recommended)
  • [ ] Or dormant mode (logging starts off; activate briefly)
  • [ ] No console output generated

Benchmarks ​

Exact overhead numbers are highly app-dependent and can be misleading.

Use these qualitative guidelines instead:

  • Disabled: No tracing output; no instrumentation work at runtime.
  • Dormant: Instrumentation may be present, but logging starts off; overhead is usually low until you enable tracing.
  • Active (narrow scope): Typically acceptable for short debugging sessions.
  • Active (broad scope / high frequency): Can overwhelm DevTools and significantly slow your app.

Best Practices Summary ​

✅ Do: ​

  • Keep tracing out of publicly accessible builds
  • Use dormant mode for restricted staging/QA environments
  • Target specific functions/components
  • Exclude high-frequency code
  • Limit object serialization depth
  • Use opt-in mode for large applications

❌ Don't: ​

  • Trace every component/function without reason
  • Enable tracing for animation loops
  • Keep tracing active for high-frequency updates
  • Use tracing in publicly accessible deployments

Released under the MIT License.