I Built a Tiny Function Profiler That Changed How I Debug JavaScript

Published: (December 27, 2025 at 12:55 PM EST)
5 min read
Source: Dev.to

Source: Dev.to

Have you ever wondered:

  • “Why is this function so slow?”
  • “How many times is this being called?”
  • “Where exactly is my code failing?”

I was asking myself these questions every single day. So I built a tool to answer them.

Introducing function‑trace

A tiny, zero‑dependency function profiler for JavaScript and TypeScript.

🤯 The Problem

Last month I was debugging a production issue. An API endpoint was randomly slow, but I couldn’t figure out which function was the culprit.

My debugging process looked like this:

const start = performance.now();
const result = await someFunction();
const end = performance.now();
console.log(`someFunction took ${end - start}ms`);

I was copy‑pasting this everywhere. It was:

  • ❌ Ugly
  • ❌ Time‑consuming
  • ❌ Easy to forget to remove
  • ❌ No historical data

There had to be a better way.

💡 The Solution

I built function‑trace — a wrapper that does all of this automatically:

import { trace } from 'function-trace';

const fetchUser = trace(
  async (id) => {
    const res = await fetch(`/api/users/${id}`);
    return res.json();
  },
  { log: true }
);

await fetchUser(1);
// [function-trace] fetchUser executed in 142.35ms

One line. That’s it. No setup. No configuration. Just wrap and go.

✨ Features That Make It Special

1. Works with Everything

TypeSupported
Sync functions
Async functions
Arrow functions
Class methods
// Sync
const add = trace((a, b) => a + b, { log: true });

// Async
const fetchData = trace(async () => {
  return await fetch('/api/data');
}, { log: true });

2. Built‑in Statistics

Every traced function gets a .stats property:

const myFunction = trace(someFunction, { log: true });

myFunction();
myFunction();
myFunction();

console.log(myFunction.stats);
// {
//   calls: 3,
//   errors: 0,
//   lastTime: 0.02,
//   history: [0.02, 0.01, 0.02]
// }
  • calls – total number of invocations
  • errors – how many times it threw an error
  • lastTime – most recent execution time (ms)
  • history – array of past execution times

3. Performance Alerts

Build your own monitoring with the stats:

const criticalOperation = trace(async () => {
  // Some critical operation
}, { log: true, maxHistory: 100 });

async function executeWithMonitoring() {
  await criticalOperation();

  const avgTime =
    criticalOperation.stats.history.reduce((a, b) => a + b, 0) /
    criticalOperation.stats.history.length;

  if (avgTime > 1000) {
    console.error('⚠️ SLA threshold exceeded!');
    // Send alert to your monitoring service
  }
}

4. Zero Dependencies

The entire package is pure TypeScript—no lodash, no moment, no bloat.

5. Type‑Safe

Full TypeScript support with proper type inference:

const multiply = trace(
  (a: number, b: number): number => a * b,
  { log: true }
);

const result = multiply(3, 4); // result is typed as number

🚀 Real‑World Use Cases

Database Query Monitoring

import { trace } from 'function-trace';

const getUserById = trace(
  async (userId) => {
    return await db.users.findById(userId);
  },
  { log: true, maxHistory: 100 }
);

// Later, check if queries are getting slow
const avgQueryTime =
  getUserById.stats.history.reduce((a, b) => a + b, 0) /
  getUserById.stats.history.length;

if (avgQueryTime > 100) {
  console.warn('⚠️ Database queries are slow. Consider adding an index.');
}

API Endpoint Profiling

const apiCall = trace(
  async (endpoint) => {
    const response = await fetch(endpoint);
    return response.json();
  },
  { log: true }
);

// After some usage
console.log(`Total API calls: ${apiCall.stats.calls}`);
console.log(`Failed calls: ${apiCall.stats.errors}`);
console.log(`Last response time: ${apiCall.stats.lastTime}ms`);

Finding Performance Bottlenecks

const step1 = trace(processData, { log: true });
const step2 = trace(transformData, { log: true });
const step3 = trace(saveData, { log: true });

await step1(data);
await step2(data);
await step3(data);

// Console output shows exactly where time is spent:
// [function-trace] processData executed in 5.23ms
// [function-trace] transformData executed in 234.56ms

Options

OptionTypeDefaultDescription
logbooleanfalseEnable console logging
maxHistorynumber50Number of execution times to keep
colorbooleantrueEnable colored output

Stats Object

PropertyTypeDescription
callsnumberTotal number of calls
errorsnumberTotal number of errors
lastTimenumberLast execution time (ms)
historynumber[]Array of recent execution times

history

Type: number[]
Description: Array of past execution times

⚡ Performance

I know what you’re thinking: “Won’t this slow down my code?”

The overhead is approximately ~0.05 ms per call. Unless you’re calling a function millions of times per second, you won’t notice it.

For production:

  • Use log: false to disable console output.
  • Adjust maxHistory based on your memory constraints.
  • The history array is automatically bounded — no memory leaks.

🆚 Why Not Just Use Chrome DevTools?

Great question! Chrome DevTools is amazing, but function‑trace offers features that DevTools doesn’t provide out‑of‑the‑box.

FeatureChrome DevToolsfunction‑trace
Works in Node.js
Programmatic access to data
Custom alerting
Production monitoring
Zero setup
Historical dataLimited

They’re complementary tools: use DevTools for deep profiling, and function‑trace for quick debugging and production monitoring.

🛠️ Best Practices

  • Development: Enable logging with { log: true }.
  • Production: Disable logging with { log: false } but keep stats.
  • High‑frequency functions: Lower maxHistory to save memory.
  • Critical paths: Build alerts based on stats.history.
  • Debugging: Wrap suspect functions to find bottlenecks.

🤝 Contributing

function‑trace is open source under the MIT license. Contributions are welcome!

🙏 Final Thoughts

I built function‑trace to solve my own problems, but I hope it helps you too.

If you try it out, let me know in the comments! I’d love to hear:

  • How are you using it?
  • What features would you like to see?
  • Did it help you find a performance issue?

Happy debugging! 🐛🔍

If this helped you, consider following me for more JavaScript/TypeScript content. I write about web development, open source, and developer tools.

What’s your go‑to debugging technique? Drop it in the comments! 👇

Back to Blog

Related posts

Read more »

JavaScript

Key Trends - Innovation in runtimes: Deno’s latest release introduces a new tool dx for running NPM & JSR binaries, improving compatibility and developer workf...