runtime-tracker
v2.0.0
Published
jest a timer for debug or performance tuning.
Readme
Runtime Tracker
A lightweight TypeScript library for hierarchical runtime performance tracking and debugging. Track execution times with nested steps and generate detailed timing reports.
Overview
Runtime Tracker is designed to help developers measure and analyze the performance of their applications by providing hierarchical timing capabilities. It allows you to create nested timers that can track complex execution flows with multiple levels of granularity.
Key Features
- Hierarchical Timing: Create nested timers to track multi-level execution flows
- Tree-like Output: Generate human-readable timing reports with visual tree structure
- JSON Export: Export timing data as structured JSON for further analysis
- TypeScript Support: Full TypeScript support with comprehensive type definitions
- Lightweight: Minimal dependencies and small footprint
- Easy Integration: Simple API that can be easily integrated into existing codebases
Technical Architecture
Technology Stack
- Language: TypeScript (compiled to ES5)
- Module System: CommonJS
- Dependencies:
numeral(^2.0.6) - Number formatting for readable time display
- Development Dependencies:
typescript(^4.4.2) - TypeScript compiler@types/numeral(^2.0.1) - Type definitions for numeral
Compilation Configuration
The project uses TypeScript with the following key settings:
- Target: ES5 for broad compatibility
- Module: CommonJS
- Output:
./distdirectory - Source Maps: Enabled for debugging
- Decorators: Experimental decorators enabled
Project Structure
runtime-tracker/
├── src/
│ └── index.ts # Main library source code
├── dist/ # Compiled JavaScript output (generated)
├── .github/
│ └── prompts/ # Development prompts and instructions
├── package.json # Project configuration and dependencies
├── tsconfig.json # TypeScript compiler configuration
├── LICENSE # MIT License
├── README.md # Project documentation
└── .gitignore # Git ignore rulesFile System Structure Details
src/index.ts: Contains the mainRuntimeTrackerclass andRuntimeLoginterfacedist/: Auto-generated directory containing compiled JavaScript, type definitions, and source mapspackage.json: Defines the library metadata, dependencies, and build scriptstsconfig.json: TypeScript compiler configuration with ES5 target and CommonJS modules
Development Setup
Prerequisites
- Node.js (version compatible with TypeScript 4.4.2+)
- npm or yarn package manager
Installation
npm install runtime-trackerDevelopment Installation
git clone https://github.com/EJayCheng/runtime-tracker.git
cd runtime-tracker
npm installBuilding
npm run prepublishOnlyThis command compiles TypeScript to JavaScript and generates type definitions in the dist/ directory.
Code Quality Tools
TypeScript Configuration
The project uses strict TypeScript settings:
- Source maps enabled for debugging
- Declaration files generated automatically
- Experimental decorators support
- ES2016 and DOM libraries included
Linting and Formatting
Currently, the project relies on TypeScript's built-in type checking. Future contributors should maintain consistency with the existing code style:
- Use meaningful variable and function names
- Follow TypeScript naming conventions
- Include proper type annotations
- Write clear, self-documenting code
Environment Variables
This library does not require any environment variables for basic operation. It's designed to work in any JavaScript/TypeScript environment without external configuration.
Usage
Basic Usage
import { RuntimeTracker } from 'runtime-tracker';
// Create a new tracker
const tracker = new RuntimeTracker('MyOperation');
// Add some work
await someAsyncOperation();
// Create nested steps (manual management)
const step1 = tracker.step('Step1');
await someWork();
step1.end();
// Or use asyncStep for automatic management
await tracker.asyncStep('Step2', async () => {
await moreWork();
// Step2 will be automatically ended
});
// End the main tracker
tracker.end();
// Display results
console.log(tracker.toString());Advanced Usage with Nested Steps
import { RuntimeTracker } from 'runtime-tracker';
const mainTracker = new RuntimeTracker('MainProcess');
// Create nested hierarchy
const dbOperations = mainTracker.step('DatabaseOperations');
const query1 = dbOperations.step('Query1');
await executeQuery1();
query1.end();
const query2 = dbOperations.step('Query2');
await executeQuery2();
query2.end();
dbOperations.end();
// Another main step
const processing = mainTracker.step('DataProcessing');
const validation = processing.step('Validation');
await validateData();
validation.end();
const transformation = processing.step('Transformation');
await transformData();
transformation.end();
processing.end();
mainTracker.end();
// Output tree structure
console.log(mainTracker.toString());
// [MainProcess]: 1,234 ms
// ├ [DatabaseOperations]: 800 ms
// │ ├ [Query1]: 300 ms
// │ └ [Query2]: 500 ms
// └ [DataProcessing]: 434 ms
// ├ [Validation]: 134 ms
// └ [Transformation]: 300 msUsing asyncStep for Automatic Step Management
import { RuntimeTracker } from 'runtime-tracker';
const tracker = new RuntimeTracker('AsyncProcess');
// asyncStep automatically manages the step lifecycle
const userData = await tracker.asyncStep('FetchUserData', async () => {
const response = await fetch('/api/user/123');
return response.json();
});
// You can access the step tracker for nested operations
const processedData = await tracker.asyncStep(
'ProcessData',
async (stepTracker) => {
// Use the step tracker to create nested operations
const validated = await stepTracker.asyncStep('ValidateData', async () => {
return validateUserData(userData);
});
const transformed = await stepTracker.asyncStep(
'TransformData',
async () => {
return transformData(validated);
},
);
return transformed;
},
);
// Multiple async steps with nested tracking
await tracker.asyncStep('SaveToDatabase', async (dbTracker) => {
await dbTracker.asyncStep('Connect', async () => {
return database.connect();
});
await dbTracker.asyncStep('Insert', async () => {
return database.save(processedData);
});
await dbTracker.asyncStep('Disconnect', async () => {
return database.disconnect();
});
});
await tracker.asyncStep('SendNotification', async () => {
await notificationService.send('Data processed successfully');
});
tracker.end();
console.log(tracker.toString());
// [AsyncProcess]: 2,150 ms
// ├ [FetchUserData]: 800 ms
// ├ [ProcessData]: 650 ms
// │ ├ [ValidateData]: 200 ms
// │ └ [TransformData]: 450 ms
// ├ [SaveToDatabase]: 400 ms
// │ ├ [Connect]: 50 ms
// │ ├ [Insert]: 300 ms
// │ └ [Disconnect]: 50 ms
// └ [SendNotification]: 300 msAdvanced asyncStep with Deep Nesting
import { RuntimeTracker } from 'runtime-tracker';
const tracker = new RuntimeTracker('ComplexWorkflow');
const result = await tracker.asyncStep(
'DataProcessingPipeline',
async (pipeline) => {
// Stage 1: Data Extraction
const rawData = await pipeline.asyncStep(
'DataExtraction',
async (extraction) => {
const apiData = await extraction.asyncStep('FetchFromAPI', async () => {
return fetch('/api/data').then((r) => r.json());
});
const fileData = await extraction.asyncStep(
'ReadFromFile',
async () => {
return readFileAsync('./data.json');
},
);
return { apiData, fileData };
},
);
// Stage 2: Data Validation & Cleaning
const cleanData = await pipeline.asyncStep(
'DataValidation',
async (validation) => {
const validatedApi = await validation.asyncStep(
'ValidateAPI',
async () => {
return validateData(rawData.apiData);
},
);
const validatedFile = await validation.asyncStep(
'ValidateFile',
async () => {
return validateData(rawData.fileData);
},
);
return await validation.asyncStep('MergeData', async () => {
return mergeData(validatedApi, validatedFile);
});
},
);
// Stage 3: Data Transformation
const transformedData = await pipeline.asyncStep(
'DataTransformation',
async (transform) => {
return await transform.asyncStep('ApplyBusinessLogic', async () => {
return applyBusinessRules(cleanData);
});
},
);
// Stage 4: Persistence
await pipeline.asyncStep('DataPersistence', async (persistence) => {
await persistence.asyncStep('SaveToDatabase', async () => {
return database.bulkInsert(transformedData);
});
await persistence.asyncStep('UpdateCache', async () => {
return cache.set('processed_data', transformedData);
});
await persistence.asyncStep('CreateBackup', async () => {
return backup.save(transformedData);
});
});
return transformedData;
},
);
tracker.end();
console.log(tracker.toString());
// [ComplexWorkflow]: 3,450 ms
// └ [DataProcessingPipeline]: 3,400 ms
// ├ [DataExtraction]: 1,200 ms
// │ ├ [FetchFromAPI]: 800 ms
// │ └ [ReadFromFile]: 400 ms
// ├ [DataValidation]: 900 ms
// │ ├ [ValidateAPI]: 300 ms
// │ ├ [ValidateFile]: 250 ms
// │ └ [MergeData]: 350 ms
// ├ [DataTransformation]: 600 ms
// │ └ [ApplyBusinessLogic]: 600 ms
// └ [DataPersistence]: 700 ms
// ├ [SaveToDatabase]: 400 ms
// ├ [UpdateCache]: 150 ms
// └ [CreateBackup]: 150 msExport to JSON
const tracker = new RuntimeTracker('Process');
// ... perform operations ...
tracker.end();
const jsonData = tracker.toJSON();
console.log(JSON.stringify(jsonData, null, 2));API Parameters and Configuration
RuntimeTracker Constructor
name: string- The name identifier for this trackerupper?: RuntimeTracker- Optional parent tracker for nested hierarchy
Methods
step(name: string | RuntimeTracker): RuntimeTracker- Creates a new nested step tracker
- Returns existing tracker if name already exists
- Throws error if parent tracker is already ended
asyncStep<T>(name: string | RuntimeTracker, fn: (runtimeTracker?: RuntimeTracker) => Promise<T>): Promise<T>- Creates a new nested step tracker and executes an async function within its scope
- Automatically ends the step tracker when the function completes (success or failure)
- The function receives the created step tracker as an optional parameter for nested operations
- Returns the result of the executed function
- Perfect for tracking async operations like API calls, database queries, or file operations
- Generic type
Trepresents the return type of the async function
end(endAt?: number): void- Ends the tracker and all its children
endAt- Optional custom end timestamp (defaults toDate.now())
toString(space?: string, prefix?: string, isEndRow?: boolean): string- Generates formatted tree output
space- Indentation string (default: " ")prefix- Line prefix for nested displayisEndRow- Whether this is the last item in its level
toJSON(): RuntimeLog- Exports tracker data as structured JSON
- Includes timing data and nested step information
Testing
Currently, the project doesn't include a formal testing framework. Future contributors should consider:
- Adding Jest or similar testing framework
- Writing unit tests for all public methods
- Testing edge cases like nested operations and error conditions
- Performance testing for overhead measurements
Deployment
This is a npm library package with the following deployment characteristics:
Build Process
- TypeScript compilation via
tsccommand - Generates CommonJS modules for broad compatibility
- Creates declaration files for TypeScript consumers
- Outputs to
dist/directory
Publishing
- Published to npm registry (https://registry.npmjs.org/)
- Version managed through
package.json - Includes compiled JavaScript, type definitions, and source maps
- Main entry point:
dist/index.js - TypeScript definitions:
dist/index.d.ts
Distribution Files
The published package includes:
- Compiled JavaScript (ES5-compatible)
- TypeScript declaration files
- Source maps for debugging
- Package metadata
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Ensure TypeScript compilation succeeds
- Submit a pull request
