一键导入
measure-fn
Zero-dependency function performance measurement with hierarchical logging. Use this to structure ALL programs as measured, observable pipelines.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Zero-dependency function performance measurement with hierarchical logging. Use this to structure ALL programs as measured, observable pipelines.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
CLI tool for patching files and resolving file includes - useful when working with LLMs
How to maintain project task docs and handle "whats next" requests. Read this when the user asks whats next or at conversation start.
Git workflow conventions — commit hygiene, squashing, force-push, and interactive rebase patterns.
Lightweight Bun process manager. Use bgrun to start, stop, restart, watch, and monitor background processes — with built-in web dashboard, TOML config, Docker support, and programmatic API.
Core behavioral rule: say 'I DON'T KNOW' when you don't actually know something. Never fabricate confident answers from incomplete information.
Install, uninstall, and list agent skills. Handles NTFS hardlinks for auto-sync with source repos.
| name | measure-fn |
| description | Zero-dependency function performance measurement with hierarchical logging. Use this to structure ALL programs as measured, observable pipelines. |
Every function you write should be measured. Don't write blind code — wrap operations in measure so every success, failure, and timing is automatically visible. This is the default way to structure programs.
bun add measure-fn
If a function does I/O, computation, or anything non-trivial — wrap it in
measure.
// ✗ BAD — blind, silent, no timing, no error visibility
const users = await fetchUsers();
const config = loadConfig();
// ✓ GOOD — observable, timed, errors auto-logged
const users = await measure('Fetch users', () => fetchUsers());
const config = measureSync('Load config', () => loadConfig());
import { measure, measureSync } from 'measure-fn';
async function main() {
const config = measureSync('Load config', () => readConfig());
const db = await measure('Connect DB', () => connectDatabase(config));
const users = await measure('Fetch users', () => db.query('SELECT * FROM users'));
await measure('Send emails', () => sendEmails(users));
}
Output:
[a] ··········· 0.12ms → {"env":"prod"}
[b] ... Connect DB
[b] ·········· 45ms → [DB]
[c] ... Fetch users
[c] ··········· 23ms → [{"id":1},{"id":2}]
[d] ... Send emails
[d] ··········· 102ms
// Async children with async m:
await measure('Pipeline', async (m) => {
const raw = await m('Fetch', () => fetchData());
const parsed = m('Parse', () => parseData(raw));
await m('Save', () => saveResult(parsed));
});
// Mix async + sync children with (m, ms):
await measure('Pipeline', async (m, ms) => {
const raw = await m('Fetch', () => fetchData());
ms('Checkpoint', () => `Got ${raw.length} items`); // sync annotation, proper hierarchy
await m('Save', () => saveResult(raw));
});
Promise.allawait measure('Load all', async (m) => {
const [users, posts, settings] = await Promise.all([
m('Users', () => fetchUsers()),
m('Posts', () => fetchPosts()),
m('Settings', () => fetchSettings()),
]);
return { users, posts, settings };
});
const getUser = measure.wrap('Get user', fetchUser);
// Every call is now measured automatically
await getUser(1); // → [a] ········ 82ms → {...}
await getUser(2); // → [b] ········ 75ms → {...}
await measure.batch('Process users', userIds, async (id) => {
return await processUser(id);
}, { every: 100 });
// → [a] ... Process users (500 items)
// → [a] = 100/500 (1.2s, 83/s)
// → [a] ················· 5.3s → "500/500 ok"
const result = await measure.retry('External API', {
attempts: 3, delay: 1000, backoff: 2
}, () => callExternalService());
// Budget: warns but doesn't stop
await measure({ label: 'DB query', budget: 100 }, () => heavyQuery());
// → [a] ········ 245ms → [...] ⚠ OVER BUDGET (100ms)
// Timeout: aborts after N ms, returns null
await measure({ label: 'External API', timeout: 5000 }, () => fetchSlowApi());
// > 5s → [a] ✗ External API 5.0s (Timeout (5.0s))
// Both together: budget warns, timeout enforces
await measure({ label: 'Query', budget: 100, timeout: 5000 }, () => db.query('...'));
// Per-label result truncation (overrides global, inherits to children, 0 = unlimited)
await measure({ label: 'Full output', maxResultLength: 0 }, () => getLargeData());
// Guaranteed non-null — throws if the function returns null/undefined
const user = await measure.assert('Get user', () => findUser(id));
const api = createMeasure('api');
const db = createMeasure('db');
await api.measure('GET /users', async () => {
return await db.measure('SELECT', () => query('SELECT * FROM users'));
});
// → [api:a] ... GET /users
// → [db:a] ······ 44ms → [...]
// → [api:a] ·········· 45ms → [...]
await measure('Server ready'); // → [a] = Server ready
measureSync('Config loaded'); // → [b] = Config loaded
onError 3rd argumentmeasure never throws. Pass an onError handler as 3rd argument to handle errors:
// Default: null on error
const user = await measure('Fetch user', () => fetchUser(1));
// Recovery: fallback on error
const user = await measure('Fetch user', () => fetchUser(1),
(error) => defaultUser
);
// Error inspection: handle known errors, rethrow unknown
const user = await measure('Fetch user', () => fetchUser(1),
(error) => {
if (error instanceof NetworkError) return cachedUser;
throw error;
}
);
// Bun.serve: always return a Response
Bun.serve({
fetch: (req) => measure(
{ label: `${req.method} ${req.url}` },
() => handleRequest(req),
(error) => new Response('Internal Server Error', { status: 500 })
),
});
.assert() re-throws on error with .cause = original error:
await measure.assert('Op', () => work());
// throws: Error('measure.assert: "Op" failed', { cause: originalError })
| Pattern | On error | Use when |
|---|---|---|
measure(label, fn) | logs ✗, returns null | Default — pipeline resilience |
measure(label, fn, onError) | logs ✗, calls onError(error) | Recovery, fallbacks, error inspection |
measure.assert(label, fn) | logs ✗, throws with .cause | Must have non-null |
import { configure } from 'measure-fn';
configure({
silent: true, // suppress output (for benchmarks)
timestamps: true, // [HH:MM:SS.mmm] prefix
maxResultLength: 200, // result truncation (default: 0 = unlimited)
dotEndLabel: false, // show full label on end lines (default: true = dots)
dotChar: '.', // character for dot fill (default: '·')
logger: (event) => { // custom telemetry
myTracker.send(event);
},
});
Env vars: MEASURE_SILENT=1, MEASURE_TIMESTAMPS=1
const { result, duration } = await measure.timed('Fetch', () => fetchUsers());
if (duration > 1000) alert('Slow!');
// ✗ WORST: Using measureSync as a console.log replacement — NO TIMING, NO HIERARCHY
measureSync(`Buy started for token ${mint}`); // This is just a label with no operation!
const result = await executeBuy(wallet, amount);
measureSync(`Buy complete: ${result}`); // Useless — you learned nothing
// ✓ CORRECT: Wrap the actual operation — get timing, errors, hierarchy for free
const result = await measure('Buy', () => executeBuy(wallet, amount));
// ✗ Don't nest measure inside measure without using child `m`
await measure('Outer', async () => {
await measure('Inner', () => work()); // creates flat siblings, not hierarchy
});
// ✓ Use child measure for hierarchy
await measure('Outer', async (m) => {
await m('Inner', () => work()); // proper parent → child
});
// ✗ Don't measure trivial synchronous expressions
const x = measureSync('Add', () => 1 + 1);
The entire point of measure-fn is that functions ARE the measurements. Don't log before/after — wrap the operation itself. Use child m for async children, ms for sync annotations inside async parents.
const { measure, measureSync } = createMeasure('worker');
// ✗ BAD — self-wrapping with ROOT measure (flat siblings, no hierarchy)
async function executeSell(pos, wallet, tokens) {
return await measure(`SELL W${wallet.index}`, async (m, ms) => {
const mode = await m('Detect', () => detectMode()); // nested under SELL
const sig = await m('Send', () => sendTx(ix, wallet)); // nested under SELL
return sig;
});
}
// Problem: SELL is a ROOT entry. If the loop calls executeSell(),
// SELL is NOT nested under the loop's Tick measure.
// ✓ GOOD — FACTORY pattern: parent injects (m, ms) via closure
function createSellExecutor(parentM, parentMs) {
return async (pos, wallet, tokens) => {
return await parentM(`SELL W${wallet.index}`, async (m, ms) => {
const mode = await m('Detect', () => detectMode());
const swapState = await m('Swap state', () => fetchSwapState(pool, wallet));
const sig = await m('Send TX', () => sendWithRetry(instructions, wallet));
ms('Result', () => `tx: ${sig.slice(0, 20)}`);
return sig;
});
};
}
// Output (fully hierarchical):
// [worker:a] Tick
// [worker:a.a] SELL W2 ··················· 1.1s
// [worker:a.a.a] Detect ······· 45ms → "amm"
// [worker:a.a.b] Swap state ····· 120ms
// [worker:a.a.c] Send TX ·········· 890ms → "5xK9f..."
// [worker:a.a] = Result: tx: 5xK9f...
// ✓ GOOD — loop creates executors inside measure('Tick') scope
async function loop() {
await measure('Init', async (m) => {
await m('SOL/USD', () => fetchSolUsd());
await m('Detect mode', () => detectMode());
});
while (running) {
await measure('Tick', async (m, ms) => {
// Factory: executors inherit this tick's (m, ms)
const executeSell = createSellExecutor(m, ms);
const executeBuy = createBuyExecutor(m, ms);
const sold = await executeSell(pos, wallet, tokens);
if (!sold) {
await executeBuy(wallet, amount);
}
await Bun.sleep(1000);
});
}
}
Key principle: If you find yourself writing measureSync('some message') without a second argument containing actual work, you're doing it wrong. That's just console.log with extra steps. The only exception is truly standalone annotations (status checkpoints).
| Export | Use |
|---|---|
measure(label, fn?, onError?) | Async measurement (onError handles expected errors) |
measureSync(label, fn?) | Sync measurement |
measure.wrap(label, fn) | Decorator — wrap once, measure every call |
measure.batch(label, items, fn, opts?) | Array + progress |
measure.retry(label, opts, fn) | Retry with backoff |
measure.assert(label, fn) | Throws if null |
measure.timed(label, fn) | Returns { result, duration } |
createMeasure(prefix) | Scoped instance |
configure(opts) | Runtime config |
safeStringify(value) | Safe JSON (circular refs, truncation) |
formatDuration(ms) | Smart duration: 0.10ms → 1.2s → 2m 5s |
resetCounter() | Reset ID counter |