|
|
|
@@ -1,286 +1,734 @@
|
|
|
|
|
# @push.rocks/smartlog
|
|
|
|
|
# @push.rocks/smartlog 🚀
|
|
|
|
|
*The ultimate TypeScript logging solution for modern applications*
|
|
|
|
|
|
|
|
|
|
Minimalistic distributed and extensible logging tool for TypeScript and JavaScript applications.
|
|
|
|
|
[](https://www.npmjs.com/package/@push.rocks/smartlog)
|
|
|
|
|
[](https://opensource.org/licenses/MIT)
|
|
|
|
|
|
|
|
|
|
## Install
|
|
|
|
|
> **smartlog** is a powerful, distributed, and extensible logging system designed for the cloud-native era. Whether you're debugging locally, monitoring production systems, or building complex microservices, smartlog adapts to your needs with style. 🎯
|
|
|
|
|
|
|
|
|
|
Install `@push.rocks/smartlog` using pnpm (recommended), npm, or yarn:
|
|
|
|
|
## 🌟 Why smartlog?
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
- **🎨 Beautiful Console Output**: Color-coded, formatted logs that are actually readable
|
|
|
|
|
- **🔌 Extensible Architecture**: Plug in any destination - databases, files, remote servers
|
|
|
|
|
- **🌍 Distributed by Design**: Built for microservices with correlation and context tracking
|
|
|
|
|
- **⚡ Zero-Config Start**: Works out of the box, scales when you need it
|
|
|
|
|
- **🎭 Interactive CLI Tools**: Spinners and progress bars that handle non-TTY environments gracefully
|
|
|
|
|
- **📊 Structured Logging**: JSON-based for easy parsing and analysis
|
|
|
|
|
- **🔍 Smart Filtering**: Log levels and context-based filtering
|
|
|
|
|
|
|
|
|
|
## 📦 Installation
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Using pnpm (recommended)
|
|
|
|
|
pnpm add @push.rocks/smartlog
|
|
|
|
|
|
|
|
|
|
# Using npm
|
|
|
|
|
npm install @push.rocks/smartlog --save
|
|
|
|
|
npm install @push.rocks/smartlog
|
|
|
|
|
|
|
|
|
|
# Using yarn
|
|
|
|
|
yarn add @push.rocks/smartlog
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Ensure you have TypeScript and Node.js installed for TypeScript projects.
|
|
|
|
|
## 🚀 Quick Start
|
|
|
|
|
|
|
|
|
|
## Package Exports
|
|
|
|
|
|
|
|
|
|
The package provides the following exports:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// Main module
|
|
|
|
|
import { Smartlog, LogGroup, ConsoleLog } from '@push.rocks/smartlog';
|
|
|
|
|
|
|
|
|
|
// Type definitions and interfaces
|
|
|
|
|
import { ILogPackage, ILogDestination, TLogLevel } from '@push.rocks/smartlog/interfaces';
|
|
|
|
|
|
|
|
|
|
// Interactive console features (spinners, progress bars)
|
|
|
|
|
import { SmartlogSourceInteractive, SmartlogProgressBar } from '@push.rocks/smartlog/source-interactive';
|
|
|
|
|
|
|
|
|
|
// Context management
|
|
|
|
|
import { ... } from '@push.rocks/smartlog/context';
|
|
|
|
|
|
|
|
|
|
// Log destinations
|
|
|
|
|
import { SmartlogDestinationClickhouse } from '@push.rocks/smartlog/destination-clickhouse';
|
|
|
|
|
import { SmartlogDestinationDevtools } from '@push.rocks/smartlog/destination-devtools';
|
|
|
|
|
import { SmartlogDestinationFile } from '@push.rocks/smartlog/destination-file';
|
|
|
|
|
import { DestinationLocal } from '@push.rocks/smartlog/destination-local';
|
|
|
|
|
import { SmartlogDestinationReceiver } from '@push.rocks/smartlog/destination-receiver';
|
|
|
|
|
|
|
|
|
|
// Receiver functionality
|
|
|
|
|
import { SmartlogReceiver } from '@push.rocks/smartlog/receiver';
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
|
|
|
`@push.rocks/smartlog` is a flexible, extensible logging tool designed for distributed systems. It provides a consistent logging interface across different environments while being lightweight and customizable.
|
|
|
|
|
|
|
|
|
|
### Creating a Logger Instance
|
|
|
|
|
|
|
|
|
|
Start by importing `Smartlog` and create a logger instance:
|
|
|
|
|
### Your First Logger
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { Smartlog } from '@push.rocks/smartlog';
|
|
|
|
|
|
|
|
|
|
// Create a logger with context
|
|
|
|
|
const logger = new Smartlog({
|
|
|
|
|
logContext: {
|
|
|
|
|
company: 'My Company',
|
|
|
|
|
companyunit: 'Cloud Team',
|
|
|
|
|
containerName: 'api-service',
|
|
|
|
|
environment: 'production', // 'local', 'test', 'staging', 'production'
|
|
|
|
|
runtime: 'node', // 'node', 'chrome', 'rust', 'deno', 'cloudflare_workers'
|
|
|
|
|
zone: 'us-west',
|
|
|
|
|
},
|
|
|
|
|
minimumLogLevel: 'info', // Optional, defaults to 'silly'
|
|
|
|
|
company: 'MyStartup',
|
|
|
|
|
companyunit: 'Backend Team',
|
|
|
|
|
containerName: 'api-gateway',
|
|
|
|
|
environment: 'production',
|
|
|
|
|
runtime: 'node',
|
|
|
|
|
zone: 'eu-central'
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Enable console output
|
|
|
|
|
// Enable beautiful console output
|
|
|
|
|
logger.enableConsole();
|
|
|
|
|
|
|
|
|
|
// Start logging!
|
|
|
|
|
logger.log('info', '🎉 Application started successfully');
|
|
|
|
|
logger.log('error', '💥 Database connection failed', {
|
|
|
|
|
errorCode: 'DB_TIMEOUT',
|
|
|
|
|
attemptCount: 3
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The context enriches logs with valuable information for filtering and analysis across distributed systems.
|
|
|
|
|
### Using the Default Logger
|
|
|
|
|
|
|
|
|
|
### Logging Messages
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// Basic logging
|
|
|
|
|
logger.log('info', 'User authenticated successfully');
|
|
|
|
|
logger.log('error', 'Database connection failed', { errorCode: 'DB_CONN_ERR', retryCount: 3 });
|
|
|
|
|
logger.log('warn', 'Rate limit approaching', { currentRate: 95, limit: 100 });
|
|
|
|
|
|
|
|
|
|
// Log levels: 'silly', 'info', 'debug', 'note', 'ok', 'success', 'warn', 'error', 'lifecycle'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The third parameter accepts any additional data to attach to the log entry.
|
|
|
|
|
|
|
|
|
|
### Default Logger
|
|
|
|
|
|
|
|
|
|
For simple cases, use the built-in default logger:
|
|
|
|
|
For quick prototyping and simple applications:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { defaultLogger } from '@push.rocks/smartlog';
|
|
|
|
|
|
|
|
|
|
defaultLogger.log('info', 'Application started');
|
|
|
|
|
defaultLogger.log('info', 'This is so easy!');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Log Groups
|
|
|
|
|
## 📚 Core Concepts
|
|
|
|
|
|
|
|
|
|
Group related logs for better traceability:
|
|
|
|
|
### Log Levels
|
|
|
|
|
|
|
|
|
|
smartlog supports semantic log levels for different scenarios:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// Create a log group with optional transaction ID
|
|
|
|
|
const requestGroup = logger.createLogGroup('tx-123456');
|
|
|
|
|
// Lifecycle events
|
|
|
|
|
logger.log('lifecycle', '🔄 Container starting up...');
|
|
|
|
|
|
|
|
|
|
// Logs within this group will be correlated
|
|
|
|
|
requestGroup.log('info', 'Processing payment request');
|
|
|
|
|
requestGroup.log('debug', 'Validating payment details');
|
|
|
|
|
requestGroup.log('success', 'Payment processed successfully');
|
|
|
|
|
// Success states
|
|
|
|
|
logger.log('success', '✅ Payment processed');
|
|
|
|
|
logger.log('ok', '👍 Health check passed');
|
|
|
|
|
|
|
|
|
|
// Information and debugging
|
|
|
|
|
logger.log('info', '📋 User profile updated');
|
|
|
|
|
logger.log('note', '📌 Cache invalidated');
|
|
|
|
|
logger.log('debug', '🔍 Query execution plan', { sql: 'SELECT * FROM users' });
|
|
|
|
|
|
|
|
|
|
// Warnings and errors
|
|
|
|
|
logger.log('warn', '⚠️ Memory usage above 80%');
|
|
|
|
|
logger.log('error', '❌ Failed to send email');
|
|
|
|
|
|
|
|
|
|
// Verbose output
|
|
|
|
|
logger.log('silly', '🔬 Entering function processPayment()');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Custom Log Destinations
|
|
|
|
|
### Log Groups for Correlation
|
|
|
|
|
|
|
|
|
|
Extend logging capabilities by adding custom destinations:
|
|
|
|
|
Perfect for tracking request flows through your system:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { Smartlog, ILogDestination } from '@push.rocks/smartlog';
|
|
|
|
|
// Track a user request through multiple operations
|
|
|
|
|
const requestGroup = logger.createLogGroup('req-7f3a2b');
|
|
|
|
|
|
|
|
|
|
class DatabaseLogDestination implements ILogDestination {
|
|
|
|
|
async handleLog(logPackage) {
|
|
|
|
|
// Store log in database
|
|
|
|
|
await db.logs.insert({
|
|
|
|
|
timestamp: new Date(logPackage.timestamp),
|
|
|
|
|
level: logPackage.level,
|
|
|
|
|
message: logPackage.message,
|
|
|
|
|
data: logPackage.data,
|
|
|
|
|
context: logPackage.context
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
requestGroup.log('info', 'Received POST /api/users');
|
|
|
|
|
requestGroup.log('debug', 'Validating request body');
|
|
|
|
|
requestGroup.log('info', 'Creating user in database');
|
|
|
|
|
requestGroup.log('success', 'User created successfully', { userId: 'usr_123' });
|
|
|
|
|
|
|
|
|
|
// Add the custom destination to your logger
|
|
|
|
|
logger.addLogDestination(new DatabaseLogDestination());
|
|
|
|
|
// All logs in the group share the same correlation ID
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🎯 Log Destinations
|
|
|
|
|
|
|
|
|
|
### Built-in Destinations
|
|
|
|
|
|
|
|
|
|
SmartLog comes with several built-in destinations for various logging needs:
|
|
|
|
|
#### 🖥️ Local Console (Enhanced)
|
|
|
|
|
|
|
|
|
|
Beautiful, colored output for local development:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// Log to a file
|
|
|
|
|
import { SmartlogDestinationFile } from '@push.rocks/smartlog/destination-file';
|
|
|
|
|
logger.addLogDestination(new SmartlogDestinationFile('/path/to/logfile.log'));
|
|
|
|
|
|
|
|
|
|
// Colorful local console logging
|
|
|
|
|
import { DestinationLocal } from '@push.rocks/smartlog/destination-local';
|
|
|
|
|
logger.addLogDestination(new DestinationLocal());
|
|
|
|
|
|
|
|
|
|
// Browser DevTools with colored formatting
|
|
|
|
|
import { SmartlogDestinationDevtools } from '@push.rocks/smartlog/destination-devtools';
|
|
|
|
|
logger.addLogDestination(new SmartlogDestinationDevtools());
|
|
|
|
|
|
|
|
|
|
// ClickHouse database logging
|
|
|
|
|
import { SmartlogDestinationClickhouse } from '@push.rocks/smartlog/destination-clickhouse';
|
|
|
|
|
const clickhouse = await SmartlogDestinationClickhouse.createAndStart({
|
|
|
|
|
host: 'clickhouse.example.com',
|
|
|
|
|
port: 8123,
|
|
|
|
|
user: 'username',
|
|
|
|
|
password: 'password',
|
|
|
|
|
database: 'logs_db'
|
|
|
|
|
const localDestination = new DestinationLocal({
|
|
|
|
|
logLevel: 'debug' // Optional: filter by minimum log level
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
logger.addLogDestination(localDestination);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 📁 File Logging
|
|
|
|
|
|
|
|
|
|
Persist logs to files with automatic rotation support:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { SmartlogDestinationFile } from '@push.rocks/smartlog/destination-file';
|
|
|
|
|
|
|
|
|
|
const fileDestination = new SmartlogDestinationFile('./logs/app.log');
|
|
|
|
|
logger.addLogDestination(fileDestination);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 🌐 Browser DevTools
|
|
|
|
|
|
|
|
|
|
Optimized for browser environments with styled console output:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { SmartlogDestinationDevtools } from '@push.rocks/smartlog/destination-devtools';
|
|
|
|
|
|
|
|
|
|
const devtools = new SmartlogDestinationDevtools();
|
|
|
|
|
logger.addLogDestination(devtools);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 📊 ClickHouse Analytics
|
|
|
|
|
|
|
|
|
|
Store logs in ClickHouse for powerful analytics:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { SmartlogDestinationClickhouse } from '@push.rocks/smartlog/destination-clickhouse';
|
|
|
|
|
|
|
|
|
|
const clickhouse = await SmartlogDestinationClickhouse.createAndStart({
|
|
|
|
|
host: 'analytics.example.com',
|
|
|
|
|
port: 8123,
|
|
|
|
|
database: 'logs',
|
|
|
|
|
user: 'logger',
|
|
|
|
|
password: process.env.CLICKHOUSE_PASSWORD
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
logger.addLogDestination(clickhouse);
|
|
|
|
|
|
|
|
|
|
// Remote receiver logging
|
|
|
|
|
// Query your logs with SQL!
|
|
|
|
|
// SELECT * FROM logs WHERE level = 'error' AND timestamp > now() - INTERVAL 1 HOUR
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 🔗 Remote Receiver
|
|
|
|
|
|
|
|
|
|
Send logs to a centralized logging service:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { SmartlogDestinationReceiver } from '@push.rocks/smartlog/destination-receiver';
|
|
|
|
|
logger.addLogDestination(new SmartlogDestinationReceiver({
|
|
|
|
|
endpoint: 'https://logs.example.com/api/logs'
|
|
|
|
|
|
|
|
|
|
const receiver = new SmartlogDestinationReceiver({
|
|
|
|
|
endpoint: 'https://logs.mycompany.com/ingest',
|
|
|
|
|
apiKey: process.env.LOG_API_KEY,
|
|
|
|
|
batchSize: 100, // Send logs in batches
|
|
|
|
|
flushInterval: 5000 // Flush every 5 seconds
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
logger.addLogDestination(receiver);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 🛠️ Custom Destinations
|
|
|
|
|
|
|
|
|
|
Build your own destination for any logging backend:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { ILogDestination, ILogPackage } from '@push.rocks/smartlog/interfaces';
|
|
|
|
|
|
|
|
|
|
class ElasticsearchDestination implements ILogDestination {
|
|
|
|
|
private client: ElasticsearchClient;
|
|
|
|
|
|
|
|
|
|
constructor(config: ElasticsearchConfig) {
|
|
|
|
|
this.client = new ElasticsearchClient(config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async handleLog(logPackage: ILogPackage): Promise<void> {
|
|
|
|
|
await this.client.index({
|
|
|
|
|
index: `logs-${new Date().toISOString().split('T')[0]}`,
|
|
|
|
|
body: {
|
|
|
|
|
'@timestamp': logPackage.timestamp,
|
|
|
|
|
level: logPackage.level,
|
|
|
|
|
message: logPackage.message,
|
|
|
|
|
context: logPackage.context,
|
|
|
|
|
data: logPackage.data,
|
|
|
|
|
correlation: logPackage.correlation
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use your custom destination
|
|
|
|
|
logger.addLogDestination(new ElasticsearchDestination({
|
|
|
|
|
node: 'https://elasticsearch.example.com'
|
|
|
|
|
}));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Interactive Console Features
|
|
|
|
|
## 🎨 Interactive Console Features
|
|
|
|
|
|
|
|
|
|
For CLI applications, use the interactive console features:
|
|
|
|
|
### Spinners
|
|
|
|
|
|
|
|
|
|
Create beautiful loading animations that degrade gracefully in CI/CD:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { SmartlogSourceInteractive, SmartlogProgressBar } from '@push.rocks/smartlog/source-interactive';
|
|
|
|
|
```
|
|
|
|
|
import { SmartlogSourceInteractive } from '@push.rocks/smartlog/source-interactive';
|
|
|
|
|
|
|
|
|
|
#### Spinners
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
const spinner = new SmartlogSourceInteractive();
|
|
|
|
|
|
|
|
|
|
// Start a spinner with text
|
|
|
|
|
spinner.text('Loading data...');
|
|
|
|
|
// Basic usage
|
|
|
|
|
spinner.text('🔄 Fetching data from API...');
|
|
|
|
|
// ... perform async operation
|
|
|
|
|
spinner.finishSuccess('✅ Data fetched successfully!');
|
|
|
|
|
|
|
|
|
|
// Chained operations
|
|
|
|
|
spinner
|
|
|
|
|
.text('📡 Connecting to database')
|
|
|
|
|
.successAndNext('🔍 Running migrations')
|
|
|
|
|
.successAndNext('🌱 Seeding data')
|
|
|
|
|
.finishSuccess('🎉 Database ready!');
|
|
|
|
|
|
|
|
|
|
// Customize appearance
|
|
|
|
|
spinner.setSpinnerStyle('dots'); // 'dots', 'line', 'star', 'simple'
|
|
|
|
|
spinner.setColor('blue'); // 'red', 'green', 'yellow', 'blue', etc.
|
|
|
|
|
spinner.setSpeed(80); // Animation speed in milliseconds
|
|
|
|
|
spinner
|
|
|
|
|
.setSpinnerStyle('dots') // dots, line, star, simple
|
|
|
|
|
.setColor('cyan') // any terminal color
|
|
|
|
|
.setSpeed(80) // animation speed in ms
|
|
|
|
|
.text('🚀 Deploying application...');
|
|
|
|
|
|
|
|
|
|
// Update spinner status
|
|
|
|
|
spinner.text('Processing records...');
|
|
|
|
|
|
|
|
|
|
// Complete with success or failure
|
|
|
|
|
spinner.finishSuccess('Data loaded successfully!');
|
|
|
|
|
spinner.finishFail('Failed to load data!');
|
|
|
|
|
|
|
|
|
|
// Chain operations
|
|
|
|
|
spinner.text('Connecting to server')
|
|
|
|
|
.successAndNext('Fetching records')
|
|
|
|
|
.successAndNext('Processing data')
|
|
|
|
|
.finishSuccess('All done!');
|
|
|
|
|
// Handle failures
|
|
|
|
|
try {
|
|
|
|
|
await deployApp();
|
|
|
|
|
spinner.finishSuccess('✅ Deployed!');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
spinner.finishFail('❌ Deployment failed');
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Progress Bars
|
|
|
|
|
### Progress Bars
|
|
|
|
|
|
|
|
|
|
Track long-running operations with style:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
const progressBar = new SmartlogProgressBar({
|
|
|
|
|
total: 100, // Total number of items
|
|
|
|
|
width: 40, // Width of the progress bar
|
|
|
|
|
complete: '█', // Character for completed section
|
|
|
|
|
incomplete: '░', // Character for incomplete section
|
|
|
|
|
showEta: true, // Show estimated time remaining
|
|
|
|
|
showPercent: true, // Show percentage
|
|
|
|
|
showCount: true // Show count (e.g., "50/100")
|
|
|
|
|
import { SmartlogProgressBar } from '@push.rocks/smartlog/source-interactive';
|
|
|
|
|
|
|
|
|
|
// Create a progress bar
|
|
|
|
|
const progress = new SmartlogProgressBar({
|
|
|
|
|
total: 100,
|
|
|
|
|
width: 40,
|
|
|
|
|
complete: '█',
|
|
|
|
|
incomplete: '░',
|
|
|
|
|
showEta: true,
|
|
|
|
|
showPercent: true,
|
|
|
|
|
showCount: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Update progress
|
|
|
|
|
progressBar.update(25); // Set to 25% progress
|
|
|
|
|
for (let i = 0; i <= 100; i++) {
|
|
|
|
|
progress.update(i);
|
|
|
|
|
await someAsyncWork();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Increment progress
|
|
|
|
|
progressBar.increment(5); // Increase by 5 units
|
|
|
|
|
progress.complete();
|
|
|
|
|
|
|
|
|
|
// Change color
|
|
|
|
|
progressBar.setColor('blue');
|
|
|
|
|
// Real-world example: Processing files
|
|
|
|
|
const files = await getFiles();
|
|
|
|
|
const progress = new SmartlogProgressBar({
|
|
|
|
|
total: files.length,
|
|
|
|
|
width: 50
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Complete the progress bar
|
|
|
|
|
progressBar.complete();
|
|
|
|
|
for (const [index, file] of files.entries()) {
|
|
|
|
|
await processFile(file);
|
|
|
|
|
progress.increment(); // or progress.update(index + 1)
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Non-Interactive Environments
|
|
|
|
|
### Non-Interactive Fallback
|
|
|
|
|
|
|
|
|
|
Both spinners and progress bars automatically detect non-interactive environments (CI/CD, piped output) and fallback to plain text logging:
|
|
|
|
|
Both spinners and progress bars automatically detect non-interactive environments (CI/CD, Docker logs, piped output) and fallback to simple text output:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
[Loading] Connecting to server
|
|
|
|
|
[Success] Connected to server
|
|
|
|
|
[Loading] Fetching records
|
|
|
|
|
[Loading] Connecting to database
|
|
|
|
|
[Success] Connected to database
|
|
|
|
|
[Loading] Running migrations
|
|
|
|
|
Progress: 25% (25/100)
|
|
|
|
|
Progress: 50% (50/100)
|
|
|
|
|
Progress: 100% (100/100)
|
|
|
|
|
[Success] Fetching complete
|
|
|
|
|
[Success] Migrations complete
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Advanced Usage
|
|
|
|
|
## 🔧 Advanced Features
|
|
|
|
|
|
|
|
|
|
### Capturing All Console Output
|
|
|
|
|
### Context Management
|
|
|
|
|
|
|
|
|
|
Capture all `console.log` and `console.error` output through Smartlog:
|
|
|
|
|
Add context that flows through your entire application:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
logger.enableConsole({ captureAll: true });
|
|
|
|
|
// Set global context
|
|
|
|
|
logger.addLogContext({
|
|
|
|
|
requestId: 'req-123',
|
|
|
|
|
userId: 'user-456',
|
|
|
|
|
feature: 'payment-processing'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// All subsequent logs include this context
|
|
|
|
|
logger.log('info', 'Processing payment');
|
|
|
|
|
// Output includes: { ...context, message: 'Processing payment' }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Scoped Logging
|
|
|
|
|
|
|
|
|
|
Create scoped loggers for different components:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
const dbLogger = logger.createScope('database');
|
|
|
|
|
const apiLogger = logger.createScope('api');
|
|
|
|
|
|
|
|
|
|
dbLogger.log('info', 'Executing query');
|
|
|
|
|
apiLogger.log('info', 'Handling request');
|
|
|
|
|
// Logs include scope information for filtering
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Minimum Log Levels
|
|
|
|
|
|
|
|
|
|
Control log verbosity per environment:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
const logger = new Smartlog({
|
|
|
|
|
logContext: { environment: 'production' },
|
|
|
|
|
minimumLogLevel: 'warn' // Only warn and above in production
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// These won't be logged in production
|
|
|
|
|
logger.log('debug', 'Detailed debug info');
|
|
|
|
|
logger.log('info', 'Regular info');
|
|
|
|
|
|
|
|
|
|
// These will be logged
|
|
|
|
|
logger.log('warn', 'Warning message');
|
|
|
|
|
logger.log('error', 'Error message');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Increment Logging
|
|
|
|
|
|
|
|
|
|
For metrics and counters:
|
|
|
|
|
Perfect for metrics and counters:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
logger.increment('info', 'api_requests', { endpoint: '/users' });
|
|
|
|
|
// Track API calls
|
|
|
|
|
logger.increment('info', 'api.requests', { endpoint: '/users', method: 'GET' });
|
|
|
|
|
logger.increment('info', 'api.requests', { endpoint: '/users', method: 'POST' });
|
|
|
|
|
|
|
|
|
|
// Track errors by type
|
|
|
|
|
logger.increment('error', 'payment.failed', { reason: 'insufficient_funds' });
|
|
|
|
|
logger.increment('error', 'payment.failed', { reason: 'card_declined' });
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Log Correlation
|
|
|
|
|
### Capture All Console Output
|
|
|
|
|
|
|
|
|
|
Correlate logs across services with correlation IDs:
|
|
|
|
|
Redirect all console.log and console.error through smartlog:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
logger.log('info', 'Request received', { userId: 'user-123' }, {
|
|
|
|
|
id: 'req-abc-123',
|
|
|
|
|
type: 'service',
|
|
|
|
|
transaction: 'tx-payment-456'
|
|
|
|
|
logger.enableConsole({
|
|
|
|
|
captureAll: true // Capture all console.* methods
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('This goes through smartlog!');
|
|
|
|
|
console.error('This too!');
|
|
|
|
|
// All console output is now structured and routed through your destinations
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🏗️ Real-World Examples
|
|
|
|
|
|
|
|
|
|
### Microservice with Distributed Tracing
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { Smartlog } from '@push.rocks/smartlog';
|
|
|
|
|
import { SmartlogDestinationClickhouse } from '@push.rocks/smartlog/destination-clickhouse';
|
|
|
|
|
|
|
|
|
|
// Initialize logger with service context
|
|
|
|
|
const logger = new Smartlog({
|
|
|
|
|
logContext: {
|
|
|
|
|
company: 'TechCorp',
|
|
|
|
|
companyunit: 'Platform',
|
|
|
|
|
containerName: 'user-service',
|
|
|
|
|
environment: process.env.NODE_ENV,
|
|
|
|
|
runtime: 'node',
|
|
|
|
|
zone: process.env.CLOUD_ZONE
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add ClickHouse for analytics
|
|
|
|
|
const clickhouse = await SmartlogDestinationClickhouse.createAndStart({
|
|
|
|
|
host: process.env.CLICKHOUSE_HOST,
|
|
|
|
|
database: 'microservices_logs'
|
|
|
|
|
});
|
|
|
|
|
logger.addLogDestination(clickhouse);
|
|
|
|
|
|
|
|
|
|
// Enable local console for development
|
|
|
|
|
if (process.env.NODE_ENV === 'development') {
|
|
|
|
|
logger.enableConsole();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Express middleware for request tracking
|
|
|
|
|
app.use((req, res, next) => {
|
|
|
|
|
const requestId = generateRequestId();
|
|
|
|
|
const logGroup = logger.createLogGroup(requestId);
|
|
|
|
|
|
|
|
|
|
// Attach logger to request
|
|
|
|
|
req.logger = logGroup;
|
|
|
|
|
|
|
|
|
|
// Log request
|
|
|
|
|
logGroup.log('info', 'Incoming request', {
|
|
|
|
|
method: req.method,
|
|
|
|
|
path: req.path,
|
|
|
|
|
ip: req.ip
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Track response
|
|
|
|
|
res.on('finish', () => {
|
|
|
|
|
logGroup.log('info', 'Request completed', {
|
|
|
|
|
statusCode: res.statusCode,
|
|
|
|
|
duration: Date.now() - req.startTime
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
next();
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## API Documentation
|
|
|
|
|
### CLI Tool with Progress Tracking
|
|
|
|
|
|
|
|
|
|
For detailed API documentation, see the [API.md](API.md) file.
|
|
|
|
|
```typescript
|
|
|
|
|
import { Smartlog } from '@push.rocks/smartlog';
|
|
|
|
|
import { SmartlogSourceInteractive, SmartlogProgressBar } from '@push.rocks/smartlog/source-interactive';
|
|
|
|
|
|
|
|
|
|
## License and Legal Information
|
|
|
|
|
const logger = new Smartlog({
|
|
|
|
|
logContext: {
|
|
|
|
|
containerName: 'migration-tool',
|
|
|
|
|
environment: 'cli'
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
logger.enableConsole();
|
|
|
|
|
|
|
|
|
|
async function migrateDatabase() {
|
|
|
|
|
const spinner = new SmartlogSourceInteractive();
|
|
|
|
|
|
|
|
|
|
// Connect to database
|
|
|
|
|
spinner.text('🔌 Connecting to database...');
|
|
|
|
|
await connectDB();
|
|
|
|
|
spinner.finishSuccess('✅ Connected to database');
|
|
|
|
|
|
|
|
|
|
// Get migrations
|
|
|
|
|
spinner.text('📋 Loading migrations...');
|
|
|
|
|
const migrations = await getMigrations();
|
|
|
|
|
spinner.finishSuccess(`✅ Found ${migrations.length} migrations`);
|
|
|
|
|
|
|
|
|
|
// Run migrations with progress bar
|
|
|
|
|
const progress = new SmartlogProgressBar({
|
|
|
|
|
total: migrations.length,
|
|
|
|
|
width: 40,
|
|
|
|
|
showEta: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const [index, migration] of migrations.entries()) {
|
|
|
|
|
logger.log('info', `Running migration: ${migration.name}`);
|
|
|
|
|
await runMigration(migration);
|
|
|
|
|
progress.update(index + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
progress.complete();
|
|
|
|
|
logger.log('success', '🎉 All migrations completed successfully!');
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Production Logging with Multiple Destinations
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { Smartlog } from '@push.rocks/smartlog';
|
|
|
|
|
import { DestinationLocal } from '@push.rocks/smartlog/destination-local';
|
|
|
|
|
import { SmartlogDestinationFile } from '@push.rocks/smartlog/destination-file';
|
|
|
|
|
import { SmartlogDestinationReceiver } from '@push.rocks/smartlog/destination-receiver';
|
|
|
|
|
|
|
|
|
|
const logger = new Smartlog({
|
|
|
|
|
logContext: {
|
|
|
|
|
company: 'Enterprise Corp',
|
|
|
|
|
containerName: 'payment-processor',
|
|
|
|
|
environment: 'production',
|
|
|
|
|
runtime: 'node',
|
|
|
|
|
zone: 'us-east-1'
|
|
|
|
|
},
|
|
|
|
|
minimumLogLevel: 'info' // No debug logs in production
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Console for container logs (structured for log aggregators)
|
|
|
|
|
logger.addLogDestination(new DestinationLocal());
|
|
|
|
|
|
|
|
|
|
// File for audit trail
|
|
|
|
|
logger.addLogDestination(new SmartlogDestinationFile('/var/log/app/audit.log'));
|
|
|
|
|
|
|
|
|
|
// Central logging service
|
|
|
|
|
logger.addLogDestination(new SmartlogDestinationReceiver({
|
|
|
|
|
endpoint: 'https://logs.enterprise.com/ingest',
|
|
|
|
|
apiKey: process.env.LOG_API_KEY,
|
|
|
|
|
batchSize: 100,
|
|
|
|
|
flushInterval: 5000
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Critical error alerts
|
|
|
|
|
logger.addLogDestination({
|
|
|
|
|
async handleLog(logPackage) {
|
|
|
|
|
if (logPackage.level === 'error' && logPackage.data?.critical) {
|
|
|
|
|
await sendAlert({
|
|
|
|
|
channel: 'ops-team',
|
|
|
|
|
message: `🚨 Critical error: ${logPackage.message}`,
|
|
|
|
|
data: logPackage
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🔌 Integration with Other Tools
|
|
|
|
|
|
|
|
|
|
### PM2 Integration
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// ecosystem.config.js
|
|
|
|
|
module.exports = {
|
|
|
|
|
apps: [{
|
|
|
|
|
name: 'api',
|
|
|
|
|
script: './dist/index.js',
|
|
|
|
|
error_file: '/dev/null', // Disable PM2 error log
|
|
|
|
|
out_file: '/dev/null', // Disable PM2 out log
|
|
|
|
|
merge_logs: true,
|
|
|
|
|
env: {
|
|
|
|
|
NODE_ENV: 'production',
|
|
|
|
|
// smartlog handles all logging
|
|
|
|
|
}
|
|
|
|
|
}]
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Docker Integration
|
|
|
|
|
|
|
|
|
|
```dockerfile
|
|
|
|
|
FROM node:18-alpine
|
|
|
|
|
WORKDIR /app
|
|
|
|
|
COPY . .
|
|
|
|
|
RUN pnpm install --production
|
|
|
|
|
|
|
|
|
|
# smartlog handles structured logging
|
|
|
|
|
# No need for special log drivers
|
|
|
|
|
CMD ["node", "dist/index.js"]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
# docker-compose.yml
|
|
|
|
|
services:
|
|
|
|
|
app:
|
|
|
|
|
build: .
|
|
|
|
|
environment:
|
|
|
|
|
- NODE_ENV=production
|
|
|
|
|
# Logs are structured JSON, perfect for log aggregators
|
|
|
|
|
logging:
|
|
|
|
|
driver: "json-file"
|
|
|
|
|
options:
|
|
|
|
|
max-size: "10m"
|
|
|
|
|
max-file: "3"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🏆 Best Practices
|
|
|
|
|
|
|
|
|
|
### 1. Use Semantic Log Levels
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// ✅ Good - semantic and meaningful
|
|
|
|
|
logger.log('lifecycle', 'Server starting on port 3000');
|
|
|
|
|
logger.log('success', 'Database connection established');
|
|
|
|
|
logger.log('error', 'Failed to process payment', { orderId, error });
|
|
|
|
|
|
|
|
|
|
// ❌ Bad - everything is 'info'
|
|
|
|
|
logger.log('info', 'Server starting');
|
|
|
|
|
logger.log('info', 'Database connected');
|
|
|
|
|
logger.log('info', 'Error: payment failed');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. Include Structured Data
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// ✅ Good - structured data for analysis
|
|
|
|
|
logger.log('error', 'API request failed', {
|
|
|
|
|
endpoint: '/api/users',
|
|
|
|
|
statusCode: 500,
|
|
|
|
|
duration: 1234,
|
|
|
|
|
userId: 'usr_123',
|
|
|
|
|
errorCode: 'INTERNAL_ERROR'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ❌ Bad - data embedded in message
|
|
|
|
|
logger.log('error', `API request to /api/users failed with 500 in 1234ms for user usr_123`);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3. Use Log Groups for Correlation
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// ✅ Good - correlated logs
|
|
|
|
|
async function processOrder(orderId: string) {
|
|
|
|
|
const logGroup = logger.createLogGroup(orderId);
|
|
|
|
|
|
|
|
|
|
logGroup.log('info', 'Processing order');
|
|
|
|
|
logGroup.log('debug', 'Validating items');
|
|
|
|
|
logGroup.log('info', 'Charging payment');
|
|
|
|
|
logGroup.log('success', 'Order processed');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ❌ Bad - no correlation
|
|
|
|
|
async function processOrder(orderId: string) {
|
|
|
|
|
logger.log('info', `Processing order ${orderId}`);
|
|
|
|
|
logger.log('debug', `Validating items for ${orderId}`);
|
|
|
|
|
logger.log('info', `Charging payment for ${orderId}`);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4. Environment-Specific Configuration
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// ✅ Good - different configs per environment
|
|
|
|
|
const logger = new Smartlog({
|
|
|
|
|
logContext: {
|
|
|
|
|
environment: process.env.NODE_ENV,
|
|
|
|
|
// ... other context
|
|
|
|
|
},
|
|
|
|
|
minimumLogLevel: process.env.NODE_ENV === 'production' ? 'info' : 'silly'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add destinations based on environment
|
|
|
|
|
if (process.env.NODE_ENV === 'production') {
|
|
|
|
|
logger.addLogDestination(clickhouseDestination);
|
|
|
|
|
logger.addLogDestination(alertingDestination);
|
|
|
|
|
} else {
|
|
|
|
|
logger.enableConsole();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 📖 API Reference
|
|
|
|
|
|
|
|
|
|
### Smartlog Class
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
class Smartlog {
|
|
|
|
|
constructor(options?: ISmartlogOptions)
|
|
|
|
|
|
|
|
|
|
// Logging methods
|
|
|
|
|
log(level: TLogLevel, message: string, data?: any, correlation?: ILogCorrelation): void
|
|
|
|
|
increment(level: TLogLevel, key: string, data?: any): void
|
|
|
|
|
|
|
|
|
|
// Configuration
|
|
|
|
|
enableConsole(options?: IConsoleOptions): void
|
|
|
|
|
addLogDestination(destination: ILogDestination): void
|
|
|
|
|
addLogContext(context: Partial<ILogContext>): void
|
|
|
|
|
|
|
|
|
|
// Log groups
|
|
|
|
|
createLogGroup(transactionId?: string): LogGroup
|
|
|
|
|
createScope(scopeName: string): Smartlog
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Log Levels
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
type TLogLevel =
|
|
|
|
|
| 'silly' // Very verbose debugging
|
|
|
|
|
| 'debug' // Debug information
|
|
|
|
|
| 'info' // Informational messages
|
|
|
|
|
| 'note' // Notable events
|
|
|
|
|
| 'ok' // Success confirmation
|
|
|
|
|
| 'success' // Major success
|
|
|
|
|
| 'warn' // Warning messages
|
|
|
|
|
| 'error' // Error messages
|
|
|
|
|
| 'lifecycle' // Application lifecycle events
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Log Package Structure
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
interface ILogPackage {
|
|
|
|
|
timestamp: number;
|
|
|
|
|
level: TLogLevel;
|
|
|
|
|
message: string;
|
|
|
|
|
data?: any;
|
|
|
|
|
context: ILogContext;
|
|
|
|
|
correlation?: ILogCorrelation;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For complete API documentation, see [API.md](API.md).
|
|
|
|
|
|
|
|
|
|
## 🤝 Contributing
|
|
|
|
|
|
|
|
|
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
|
|
|
|
|
|
|
|
## 📄 License and Legal Information
|
|
|
|
|
|
|
|
|
|
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
|
|
|
|
|
|
|
|