A structured logging library that enables rich runtime metadata tagging using tagged template strings. Strog preserves legibility while enabling machinability.
- 🏷️ Tagged Template Strings: Embed structured metadata that maintains types
- 👓 Human Readable: Logs remain legible
- 🤖 Machine Parsable: Easily extract structured data for log processing
- 🌐 Universal: Works in Node.js, Cloudflare Workers, and browsers
- 📦 Zero Dependencies: Self-contained with no external dependencies
- 🎯 TypeScript First: Full TypeScript support with proper type exports
npm install strogimport { Strog } from 'strog';
// Create your own tagged template function
const EndpointMetric = Strog('endpoint-metric', ['method', 'endpoint']);
// Use it like a regular template literal
const method = "GET";
const endpoint = "/users";
const log = EndpointMetric`${method} ${endpoint}`;
//GET /users⏎{"type":"endpoint-metric","metadata":{"method":"GET","endpoint":"/users"}
// Parse it back
const parsed = Strog.parse(log);
console.log(parsed.message);
// "GET /users"
console.log(parsed.metadata);
// {type:"endpoint-metric",metadata:{method:"GET",endpoint:"/users"}Strog appends structured metadata to your log messages using a delimiter, making logs both human-readable and machine-parsable. When you create a structured log, Strog appends metadata after your message using the default line-separator character (\u2028):
const EndpointMetric = Strog('endpoint-metric', ['method', 'endpoint']);
const log = EndpointMetric`GET /users`;
// What you see in console when logged:
// GET /users {"type":"endpoint-metric","metadata":{"method":"GET","endpoint":"/users"}}The default delimiter (\u2028) appears as a subtle space but is extremely unlikely to occur naturally in log messages, making parsing reliable. You can also supply your own delimiter as needed.
The same library can be used in log processors to extract the structured data:
// In your log processor (Cloudflare Tail Worker, log aggregator, etc.)
import { Strog } from 'strog';
// Parse a log message that came through your system
const incomingLog = "GET /users completed in 150ms\u2028{...metadata...}";
const parsed = Strog.parse(incomingLog);
console.log(parsed.message); // "GET /users completed in 150ms"
console.log(parsed.metadata); // { type: "...", metadata: {...} }This enables a powerful workflow:
- Application code uses Strog to create structured logs that look normal
- Log infrastructure uses Strog parsing functions to extract rich metadata
- Zero configuration needed - logs flow through existing systems unchanged
Creates a tagged template function for structured logging.
Parameters:
type(string): The log entry type/categorykeys(string[]): Array of metadata keys that correspond to template placeholdersdelimiter(string, optional): Custom delimiter string. Defaults to line separator (\u2028)
Returns: StrogTagFunction - A tagged template function with built-in parsing
// Basic usage
const UserAction = Strog('user-action', ['user_id', 'action']);
// With custom delimiter
const ApiCall = Strog('api', ['method', 'url'], '||LOG||');Creates metadata object from template parameters.
Parameters:
type(string): The log entry typeplaceholders(any[]): Values from template literalkeys(string[]): Metadata keys matching your template literal placeholders
Returns: Metadata object
const metadata = Strog.metadata('test', ['value1', 'value2'], ['key1', 'key2']);
// Returns: { type: 'test', metadata: { key1: 'value1', key2: 'value2' } }Parses a structured log message back into components.
Parameters:
message(string): The structured log message to parsedelimiter(string, optional): The delimiter used. Defaults to line separator (\u2028)
Returns: StructuredLog object with message and metadata? properties
const parsed = Strog.parse(logMessage);
console.log(parsed.message); // Original message without metadata
console.log(parsed.metadata); // Metadata object or undefinedimport { Strog } from 'strog';
const UserAction = Strog('user-action', ['user_id', 'action']);
const userId = 'user_123';
const action = 'login';
// Create structured log
const logMessage = UserAction`User ${userId} performed ${action}`;
console.log(logMessage);
// Output: "User user_123 performed login⏎{\"type\":\"user-action\",\"metadata\":{\"user_id\":\"user_123\",\"action\":\"login\"}}"const ErrorLog = Strog('error', ['error_code', 'message', 'stack_trace']);
try {
// Some operation that might fail
riskyOperation();
} catch (error) {
const errorCode = 'AUTH_FAILED';
const message = error.message;
const stackTrace = error.stack;
console.error(ErrorLog`${errorCode}: ${message}\n${stackTrace}`);
}const PerfMetric = Strog('performance', ['operation', 'duration_ms', 'memory_mb']);
const startTime = performance.now();
await someOperation();
const endTime = performance.now();
const memoryUsage = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(PerfMetric`Operation completed: ${operation} took ${endTime - startTime}ms, used ${memoryUsage}MB memory`);// In your Cloudflare Worker
import { Strog } from 'strog';
const RequestLog = Strog('http-request', ['method', 'url', 'status', 'duration']);
export default {
async fetch(request: Request): Promise<Response> {
const start = Date.now();
// Process request
const response = await handleRequest(request);
const duration = Date.now() - start;
// Structured log that appears normal but contains rich metadata
console.log(RequestLog`${request.method} ${request.url} ${response.status} ${duration}ms`);
return response;
}
};// In your Tail Worker for log processing
import { Strog } from 'strog';
export default {
async tail(events: TraceItem[]): Promise<void> {
for (const event of events) {
if (event.logs) {
for (const log of event.logs) {
// Parse structured logs
const parsed = Strog.parse(log.message);
if (parsed.metadata) {
// Send structured data to analytics
await analytics.track({
type: parsed.metadata.type,
message: parsed.message,
metadata: parsed.metadata,
timestamp: log.timestamp,
});
}
}
}
}
}
};import { Strog, type Metadata, type StructuredLog, type StrogTagFunction } from 'strog';
// All types are properly exported
const UserEvent: StrogTagFunction = Strog('user-event', ['user_id', 'event_type']);
// Type-safe parsing
const parsed = UserEvent.parse(logMessage); // : StructuredLog
if ( parsed.metadata ) {
const metadata = parsed.metadata; // : Metadata
console.log(metadata.type); // : string
console.log(metadata.metadata); // : Record<string, any> | undefined
}Strog is designed for minimal performance impact:
- Zero dependencies : No external library overhead
- Minimal and fast : designed for speed and ease-of-use
MIT License - see LICENSE file for details.