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 **evโฆ
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
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 comes with a .stats property:
const myFunction = trace(someFunction, { log: true });
// Call it a few times
myFunction();
myFunction();
myFunction();
console.log(myFunction.stats);
// {
// calls: 3,
// errors: 0,
// lastTime: 0.02,
// history: [0.02, 0.01, 0.02]
// }
You get:
- calls โ Total number of invocations
- errors โ How many times it threw an error
- lastTime โ Most recent execution time
- 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.
Just clean, efficient code.
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 <-- Found it!
// [function-trace] saveData executed in 12.34ms
๐ฆ Installation
npm install function-trace
or
yarn add function-trace
or
pnpm add function-trace
๐ฏ API Reference
trace<F>(fn: F, options?: TraceOptions): F & { stats: TraceStats }
Options
| Option | Type | Default | Description |
|---|---|---|---|
log | boolean | false | Enable console logging |
maxHistory | number | 50 | Number of execution times to keep |
color | boolean | true | Enable colored output |
Stats Object
| Property | Type | Description |
|---|---|---|
calls | number | Total number of calls |
errors | number | Total number of errors |
lastTime | number | Last execution time (ms) |
history | number[] | Array of past execution times |
โก Performance
I know what youโre thinking: "Wonโt this slow down my code?"
The overhead is approximately ~0.05ms per call. Unless youโre calling a function millions of times per second, you wonโt notice it.
For production:
- Use
log: falseto disable console output - Adjust
maxHistorybased 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:
| Feature | Chrome DevTools | function-trace |
|---|---|---|
| Works in Node.js | โ | โ |
| Programmatic access to data | โ | โ |
| Custom alerting | โ | โ |
| Production monitoring | โ | โ |
| Zero setup | โ | โ |
| Historical data | Limited | โ |
Theyโre complementary tools. Use DevTools for deep profiling, use 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
maxHistoryto save memory - Critical paths: Build alerts based on
stats.history - Debugging: Wrap suspect functions to find bottlenecks
๐ค Contributing
This is open source under the MIT license. Contributions are welcome!
- Found a bug? Open an issue
- Have an idea? Submit a PR
- Like it? Give it a โญ on GitHub
๐ Links
๐ 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! ๐