Context Demo
Demonstrates prefixes, context attachment, method chaining, and context inheritance with interactive flow diagrams.
📄 Source Code
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Loggical Context & Prefixes Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, sans-serif;
line-height: 1.6;
color: #333;
background: #f8f9fa;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #17a2b8 0%, #20c997 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
}
.nav {
background: #e9ecef;
padding: 15px 30px;
border-bottom: 1px solid #dee2e6;
}
.nav a {
color: #495057;
text-decoration: none;
margin-right: 20px;
font-weight: 500;
}
.nav a:hover {
color: #17a2b8;
}
.content {
padding: 30px;
}
.demo-section {
margin-bottom: 40px;
padding: 25px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #17a2b8;
}
.demo-section h2 {
color: #2c3e50;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 10px;
}
.demo-section .icon {
font-size: 1.5rem;
}
.demo-button {
background: linear-gradient(135deg, #17a2b8 0%, #20c997 100%);
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease;
margin-right: 10px;
margin-bottom: 10px;
}
.demo-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(23, 162, 184, 0.3);
}
.log-output {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas,
monospace;
font-size: 12px;
line-height: 1.4;
overflow-x: auto;
white-space: pre-wrap;
min-height: 120px;
border-radius: 8px;
margin-top: 15px;
}
.controls-panel {
background: white;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.controls-row {
display: flex;
gap: 20px;
margin-bottom: 15px;
align-items: center;
flex-wrap: wrap;
}
.control-group {
display: flex;
flex-direction: column;
gap: 5px;
}
.control-group label {
font-weight: 600;
color: #495057;
font-size: 14px;
}
.control-group input,
.control-group select {
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
}
.checkbox-group {
display: flex;
align-items: center;
gap: 8px;
}
.checkbox-group input[type="checkbox"] {
width: auto;
margin: 0;
}
.explanation {
background: #e8f4fd;
border-left: 4px solid #17a2b8;
padding: 15px;
margin: 15px 0;
border-radius: 4px;
}
.explanation h4 {
color: #0c5460;
margin-bottom: 8px;
}
.explanation p {
color: #0c5460;
margin: 0;
}
.code-example {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 15px;
margin: 15px 0;
font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
font-size: 13px;
overflow-x: auto;
}
.flow-diagram {
display: flex;
align-items: center;
gap: 15px;
margin: 20px 0;
padding: 15px;
background: white;
border-radius: 8px;
border: 1px solid #dee2e6;
overflow-x: auto;
}
.flow-step {
background: #17a2b8;
color: white;
padding: 10px 15px;
border-radius: 6px;
white-space: nowrap;
font-size: 12px;
font-weight: 500;
}
.flow-arrow {
color: #6c757d;
font-size: 18px;
}
.clear-button {
background: #6c757d;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-left: auto;
}
.clear-button:hover {
background: #5a6268;
}
/* ANSI color classes */
.ansi-black {
color: #2d3748;
}
.ansi-red {
color: #f56565;
}
.ansi-green {
color: #48bb78;
}
.ansi-yellow {
color: #ed8936;
}
.ansi-blue {
color: #4299e1;
}
.ansi-magenta {
color: #9f7aea;
}
.ansi-cyan {
color: #38b2ac;
}
.ansi-white {
color: #f7fafc;
}
.ansi-gray {
color: #a0aec0;
}
.ansi-bright-red {
color: #fc8181;
}
.ansi-bright-green {
color: #68d391;
}
.ansi-bright-yellow {
color: #f6e05e;
}
.ansi-bright-blue {
color: #63b3ed;
}
.ansi-bright-magenta {
color: #b794f6;
}
.ansi-bright-cyan {
color: #4fd1c7;
}
.ansi-bright-white {
color: #ffffff;
}
.ansi-dim {
opacity: 0.6;
}
.ansi-bold {
font-weight: bold;
}
@media (max-width: 768px) {
.controls-row {
flex-direction: column;
align-items: stretch;
}
.flow-diagram {
flex-direction: column;
align-items: stretch;
}
.flow-arrow {
transform: rotate(90deg);
align-self: center;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🏷️ Context & Prefixes Demo</h1>
<p>
Learn how to add context and prefixes for better log organization and
traceability
</p>
</div>
<div class="nav">
<a href="index.html">← Back to Demos</a>
<a href="basic-demo.html">Basic Usage</a>
<a href="security-demo.html">Security & Redaction</a>
</div>
<div class="content">
<!-- Basic Prefixes -->
<div class="demo-section">
<h2><span class="icon">🏷️</span>Basic Prefixes</h2>
<div class="explanation">
<h4>Organizing Logs with Prefixes</h4>
<p>
Prefixes help identify the source of log messages. They're
especially useful in large applications with multiple modules,
services, or components.
</p>
</div>
<div class="controls-panel">
<div class="controls-row">
<div class="control-group">
<label>Prefix Text</label>
<input
type="text"
id="prefix-text"
value="API"
placeholder="e.g., API, DB, AUTH"
/>
</div>
<div class="control-group">
<div class="checkbox-group">
<input type="checkbox" id="abbreviate-prefixes" />
<label for="abbreviate-prefixes"
>Abbreviate Long Prefixes</label
>
</div>
</div>
<div class="control-group">
<label>Max Prefix Length</label>
<input
type="number"
id="max-prefix-length"
value="10"
min="3"
max="20"
/>
</div>
</div>
</div>
<button class="demo-button" onclick="demonstrateBasicPrefix()">
🏷️ Test Basic Prefix
</button>
<button class="demo-button" onclick="demonstrateMultiplePrefixes()">
📚 Multiple Prefixes
</button>
<button class="demo-button" onclick="demonstrateLongPrefixes()">
📏 Long Prefixes
</button>
<button class="clear-button" onclick="clearOutput('prefix-output')">
Clear
</button>
<div class="code-example">
import { Logger } from 'loggical' // Basic prefix const apiLogger =
new Logger({ prefix: 'API' }) apiLogger.info('Request processed') //
With method chaining const dbLogger = logger.withPrefix('DATABASE')
dbLogger.info('Query executed')
</div>
<div id="prefix-output" class="log-output">
Click the buttons above to see prefix examples!
</div>
</div>
<!-- Context Attachment -->
<div class="demo-section">
<h2><span class="icon">🔗</span>Context Attachment</h2>
<div class="explanation">
<h4>Adding Context to Logs</h4>
<p>
Context allows you to attach key-value pairs to all log messages
from a logger instance. Perfect for tracking user sessions,
request IDs, or any persistent data.
</p>
</div>
<div class="controls-panel">
<div class="controls-row">
<div class="control-group">
<label>Context Key</label>
<input
type="text"
id="context-key"
value="userId"
placeholder="e.g., userId, requestId"
/>
</div>
<div class="control-group">
<label>Context Value</label>
<input
type="text"
id="context-value"
value="user123"
placeholder="e.g., user123, req-abc-456"
/>
</div>
<div class="control-group">
<div class="checkbox-group">
<input type="checkbox" id="compact-context" checked />
<label for="compact-context">Compact Context Format</label>
</div>
</div>
</div>
</div>
<button class="demo-button" onclick="demonstrateBasicContext()">
🔗 Basic Context
</button>
<button class="demo-button" onclick="demonstrateMultipleContext()">
📋 Multiple Context
</button>
<button class="demo-button" onclick="demonstrateContextInheritance()">
🧬 Context Inheritance
</button>
<button class="clear-button" onclick="clearOutput('context-output')">
Clear
</button>
<div class="code-example">
// Single context value const userLogger =
logger.withContext('userId', 'user123') userLogger.info('User action
performed') // Multiple context values const sessionLogger =
logger.withContext({ userId: 'user123', sessionId: 'sess-abc-456',
role: 'admin' }) sessionLogger.info('Session started')
</div>
<div id="context-output" class="log-output">
Click the buttons above to see context examples!
</div>
</div>
<!-- Method Chaining -->
<div class="demo-section">
<h2><span class="icon">🔗</span>Method Chaining & Inheritance</h2>
<div class="explanation">
<h4>Fluent API and Context Inheritance</h4>
<p>
Chain methods to build complex logger configurations. Context and
prefixes are inherited, allowing you to create specialized loggers
from base ones.
</p>
</div>
<div class="flow-diagram">
<div class="flow-step">Base Logger</div>
<div class="flow-arrow">→</div>
<div class="flow-step">+ Prefix</div>
<div class="flow-arrow">→</div>
<div class="flow-step">+ Context</div>
<div class="flow-arrow">→</div>
<div class="flow-step">+ More Context</div>
<div class="flow-arrow">→</div>
<div class="flow-step">Final Logger</div>
</div>
<button class="demo-button" onclick="demonstrateChaining()">
🔗 Method Chaining
</button>
<button class="demo-button" onclick="demonstrateInheritance()">
🧬 Context Inheritance
</button>
<button class="demo-button" onclick="demonstrateContextRemoval()">
🗑️ Context Removal
</button>
<button class="clear-button" onclick="clearOutput('chaining-output')">
Clear
</button>
<div class="code-example">
// Method chaining const complexLogger = logger
.withPrefix('PAYMENT') .withContext('orderId', 'order-123')
.withContext({ userId: 'user456', amount: 99.99 })
complexLogger.info('Payment processed') //
Context inheritance const baseLogger = logger.withContext('service',
'auth') const userLogger = baseLogger.withContext('userId',
'user123') const sessionLogger = userLogger.withContext('sessionId',
'sess-456')
</div>
<div id="chaining-output" class="log-output">
Click the buttons above to see chaining examples!
</div>
</div>
<!-- Real-world Scenarios -->
<div class="demo-section">
<h2><span class="icon">🌍</span>Real-world Scenarios</h2>
<div class="explanation">
<h4>Practical Context Usage</h4>
<p>
See how context and prefixes work in realistic application
scenarios like user sessions, API requests, database operations,
and error tracking.
</p>
</div>
<button
class="demo-button"
onclick="demonstrateScenario('user-session')"
>
👤 User Session
</button>
<button
class="demo-button"
onclick="demonstrateScenario('api-request')"
>
🌐 API Request
</button>
<button class="demo-button" onclick="demonstrateScenario('database')">
🗄️ Database Operations
</button>
<button
class="demo-button"
onclick="demonstrateScenario('error-tracking')"
>
🚨 Error Tracking
</button>
<button
class="demo-button"
onclick="demonstrateScenario('microservice')"
>
🏢 Microservice
</button>
<button class="clear-button" onclick="clearOutput('scenario-output')">
Clear
</button>
<div id="scenario-output" class="log-output">
Click the scenario buttons above to see realistic examples!
</div>
</div>
<!-- Interactive Context Builder -->
<div class="demo-section">
<h2><span class="icon">🔧</span>Interactive Context Builder</h2>
<div class="explanation">
<h4>Build Your Own Context</h4>
<p>
Experiment with different combinations of prefixes and context to
see how they work together. Build complex logger configurations
step by step.
</p>
</div>
<div class="controls-panel">
<div class="controls-row">
<div class="control-group">
<label>Step 1: Base Prefix</label>
<input
type="text"
id="builder-prefix"
value="DEMO"
placeholder="Base prefix"
/>
</div>
<div class="control-group">
<label>Step 2: Context Key 1</label>
<input
type="text"
id="builder-key1"
value="userId"
placeholder="First context key"
/>
</div>
<div class="control-group">
<label>Context Value 1</label>
<input
type="text"
id="builder-value1"
value="user123"
placeholder="First context value"
/>
</div>
</div>
<div class="controls-row">
<div class="control-group">
<label>Step 3: Context Key 2</label>
<input
type="text"
id="builder-key2"
value="requestId"
placeholder="Second context key"
/>
</div>
<div class="control-group">
<label>Context Value 2</label>
<input
type="text"
id="builder-value2"
value="req-abc-456"
placeholder="Second context value"
/>
</div>
<div class="control-group">
<label>Step 4: Additional Prefix</label>
<input
type="text"
id="builder-prefix2"
value="AUTH"
placeholder="Additional prefix"
/>
</div>
</div>
</div>
<button class="demo-button" onclick="buildCustomLogger()">
🔧 Build & Test Logger
</button>
<button class="demo-button" onclick="showBuilderSteps()">
📋 Show Build Steps
</button>
<button class="clear-button" onclick="clearOutput('builder-output')">
Clear
</button>
<div class="code-example" id="builder-code">
// Your custom logger configuration will appear here
</div>
<div id="builder-output" class="log-output">
Configure your logger above and click "Build & Test Logger"!
</div>
</div>
<!-- Performance & Best Practices -->
<div class="demo-section">
<h2><span class="icon">⚡</span>Performance & Best Practices</h2>
<div class="explanation">
<h4>Efficient Context Usage</h4>
<p>
Learn best practices for using context and prefixes efficiently.
Understand the performance implications and when to use different
approaches.
</p>
</div>
<button class="demo-button" onclick="demonstrateBestPractices()">
📚 Best Practices
</button>
<button class="demo-button" onclick="demonstratePerformance()">
⚡ Performance Tips
</button>
<button class="demo-button" onclick="demonstrateAntiPatterns()">
❌ Anti-patterns
</button>
<button
class="clear-button"
onclick="clearOutput('practices-output')"
>
Clear
</button>
<div id="practices-output" class="log-output">
Click the buttons above to see best practices and performance tips!
</div>
</div>
<!-- Namespace System -->
<div class="demo-section">
<h2><span class="icon">🏗️</span>Namespace System</h2>
<div class="explanation">
<h4>Hierarchical Logger Organization</h4>
<p>
Organize loggers by namespace for better control and filtering.
Set different log levels for different parts of your application.
</p>
<div class="code-example">
<strong>Example:</strong><br />
<code>const authLogger = getLogger('app:auth')</code><br />
<code>const dbLogger = getLogger('app:db:users')</code><br />
<code>setNamespaceLevel('app:db:*', LogLevel.WARN)</code>
</div>
</div>
<div class="demo-controls">
<button class="demo-button" onclick="demonstrateNamespaces()">
🏗️ Show Namespace Hierarchy
</button>
<button class="demo-button" onclick="demonstrateNamespaceLevels()">
🎚️ Configure Namespace Levels
</button>
</div>
<div class="output-container">
<div id="namespace-output" class="output"></div>
</div>
</div>
</div>
</div>
<script type="module">
import { Logger, LogLevel } from "loggical";
// ANSI to HTML conversion function
function ansiToHtml(text) {
const ansiMap = {
0: "reset",
1: "ansi-bold",
2: "ansi-dim",
30: "ansi-black",
31: "ansi-red",
32: "ansi-green",
33: "ansi-yellow",
34: "ansi-blue",
35: "ansi-magenta",
36: "ansi-cyan",
37: "ansi-white",
90: "ansi-gray",
91: "ansi-bright-red",
92: "ansi-bright-green",
93: "ansi-bright-yellow",
94: "ansi-bright-blue",
95: "ansi-bright-magenta",
96: "ansi-bright-cyan",
97: "ansi-bright-white",
39: "reset-fg",
};
let html = text
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
let openSpans = [];
html = html.replace(
/(?:\033\[|\x1b\[|\[)([0-9;]+)m/g,
(match, codes) => {
const codeList = codes.split(";");
let result = "";
codeList.forEach((code) => {
if (code === "0" || code === "39") {
result += "</span>".repeat(openSpans.length);
openSpans = [];
} else if (ansiMap[code]) {
const className = ansiMap[code];
if (className !== "reset" && className !== "reset-fg") {
result += `<span class="${className}">`;
openSpans.push(className);
}
}
});
return result;
}
);
html += "</span>".repeat(openSpans.length);
return html;
}
// Custom transport for capturing output
class DemoTransport {
constructor(outputElementId) {
this.name = "demo";
this.outputElementId = outputElementId;
this.messages = [];
}
write(formattedMessage, metadata) {
this.messages.push(formattedMessage);
const element = document.getElementById(this.outputElementId);
if (element) {
const htmlOutput = ansiToHtml(formattedMessage);
element.innerHTML += htmlOutput + "\n";
}
}
clear() {
this.messages = [];
const element = document.getElementById(this.outputElementId);
if (element) {
element.innerHTML = "";
}
}
}
// Global functions for demo buttons
window.demonstrateBasicPrefix = function () {
const prefixText =
document.getElementById("prefix-text").value || "API";
const abbreviate = document.getElementById(
"abbreviate-prefixes"
).checked;
const maxLength =
parseInt(document.getElementById("max-prefix-length").value) || 10;
const transport = new DemoTransport("prefix-output");
const logger = new Logger({
prefix: prefixText,
abbreviatePrefixes: abbreviate,
maxPrefixLength: maxLength,
timestamped: false,
transports: [transport],
});
logger.info("Processing user request");
logger.warn("Rate limit approaching");
logger.error("Request failed", { statusCode: 500 });
};
window.demonstrateMultiplePrefixes = function () {
const transport = new DemoTransport("prefix-output");
const apiLogger = new Logger({
prefix: "API",
timestamped: false,
transports: [transport],
});
const dbLogger = new Logger({
prefix: "DATABASE",
timestamped: false,
transports: [transport],
});
const authLogger = new Logger({
prefix: "AUTH",
timestamped: false,
transports: [transport],
});
apiLogger.info("Incoming HTTP request");
dbLogger.info("Query executed successfully");
authLogger.warn("Invalid login attempt");
apiLogger.info("Response sent to client");
};
window.demonstrateLongPrefixes = function () {
const transport = new DemoTransport("prefix-output");
// Long prefix without abbreviation
const longLogger = new Logger({
prefix: "VERY_LONG_SERVICE_NAME",
abbreviatePrefixes: false,
timestamped: false,
transports: [transport],
});
// Long prefix with abbreviation
const abbrevLogger = new Logger({
prefix: "VERY_LONG_SERVICE_NAME",
abbreviatePrefixes: true,
maxPrefixLength: 8,
timestamped: false,
transports: [transport],
});
longLogger.info("Message with full long prefix");
abbrevLogger.info("Message with abbreviated prefix");
};
window.demonstrateBasicContext = function () {
const contextKey =
document.getElementById("context-key").value || "userId";
const contextValue =
document.getElementById("context-value").value || "user123";
const compact = document.getElementById("compact-context").checked;
const transport = new DemoTransport("context-output");
const logger = new Logger({
timestamped: false,
transports: [transport],
})
.withContext(contextKey, contextValue);
logger.info("User logged in successfully");
logger.info("Profile updated");
logger.warn("Password change requested");
};
window.demonstrateMultipleContext = function () {
const transport = new DemoTransport("context-output");
const logger = new Logger({
timestamped: false,
transports: [transport],
}).withContext({
userId: "user123",
sessionId: "sess-abc-456",
role: "admin",
ip: "192.168.1.100",
});
logger.info("Admin action performed");
logger.info("User permissions checked");
logger.warn("Elevated privileges used");
};
window.demonstrateContextInheritance = function () {
const transport = new DemoTransport("context-output");
const baseLogger = new Logger({
timestamped: false,
transports: [transport],
});
const userLogger = baseLogger.withContext("userId", "user123");
const sessionLogger = userLogger.withContext(
"sessionId",
"sess-abc-456"
);
const requestLogger = sessionLogger.withContext(
"requestId",
"req-def-789"
);
baseLogger.info("Application started");
userLogger.info("User context added");
sessionLogger.info("Session context added");
requestLogger.info("Request context added - full inheritance");
};
window.demonstrateChaining = function () {
const transport = new DemoTransport("chaining-output");
const complexLogger = new Logger({
timestamped: false,
transports: [transport],
})
.withPrefix("PAYMENT")
.withContext("orderId", "order-12345")
.withContext({ userId: "user456", amount: 99.99 });
complexLogger.info("Payment processing started");
complexLogger.info("Credit card validated");
complexLogger.info("Payment completed successfully");
};
window.demonstrateInheritance = function () {
const transport = new DemoTransport("chaining-output");
const serviceLogger = new Logger({
timestamped: false,
transports: [transport],
})
.withPrefix("SERVICE")
.withContext("service", "auth-service");
const userLogger = serviceLogger.withContext("userId", "user123");
const sessionLogger = userLogger.withContext("sessionId", "sess-456");
serviceLogger.info("Service initialized");
userLogger.info("User context inherited from service");
sessionLogger.info("Session context inherited from user and service");
};
window.demonstrateContextRemoval = function () {
const transport = new DemoTransport("chaining-output");
const fullLogger = new Logger({
timestamped: false,
transports: [transport],
}).withContext({
userId: "user123",
sessionId: "sess-456",
ip: "192.168.1.100",
sensitive: "secret-data",
});
const publicLogger = fullLogger.withoutContextKey("sensitive");
const cleanLogger = fullLogger.withoutContext();
fullLogger.info("Full context logger");
publicLogger.info("Sensitive context removed");
cleanLogger.info("All context removed");
};
window.demonstrateScenario = function (scenario) {
const transport = new DemoTransport("scenario-output");
const baseLogger = new Logger({
timestamped: false,
transports: [transport],
});
switch (scenario) {
case "user-session":
const sessionLogger = baseLogger.withPrefix("SESSION").withContext({
userId: "user123",
sessionId: "sess-abc-456",
role: "admin",
});
sessionLogger.info("User session started");
sessionLogger.info("Accessing admin dashboard");
sessionLogger.warn("Elevated permissions used");
sessionLogger.info("Session activity logged");
break;
case "api-request":
const requestLogger = baseLogger.withPrefix("API").withContext({
requestId: "req-def-789",
method: "POST",
endpoint: "/api/users",
ip: "192.168.1.100",
});
requestLogger.info("Incoming request");
requestLogger.info("Request validation passed");
requestLogger.warn("Rate limiting applied");
requestLogger.info("Response sent", { statusCode: 201 });
break;
case "database":
const dbLogger = baseLogger.withPrefix("DB").withContext({
operation: "INSERT",
table: "users",
transaction: "tx-123",
});
dbLogger.info("Database transaction started");
dbLogger.info("Query prepared");
dbLogger.warn("Slow query detected", { duration: "1.2s" });
dbLogger.info("Transaction committed");
break;
case "error-tracking":
const errorLogger = baseLogger.withPrefix("ERROR").withContext({
correlationId: "corr-xyz-789",
userId: "user123",
feature: "payment",
});
try {
throw new Error("Payment gateway timeout");
} catch (error) {
errorLogger.error("Payment failed", {
error,
retryCount: 3,
gateway: "stripe",
});
}
errorLogger.warn("Fallback payment method activated");
errorLogger.info("Error recovery completed");
break;
case "microservice":
const serviceLogger = baseLogger
.withPrefix("USER-SERVICE")
.withContext({
service: "user-service",
version: "1.2.3",
instance: "us-east-1a",
});
const requestContext = serviceLogger.withContext({
requestId: "req-microservice-123",
traceId: "trace-abc-def",
});
serviceLogger.info("Service health check passed");
requestContext.info("Processing user creation request");
requestContext.info("Validating user data");
requestContext.info("User created successfully");
break;
}
};
window.buildCustomLogger = function () {
const transport = new DemoTransport("builder-output");
transport.clear();
const prefix = document.getElementById("builder-prefix").value;
const key1 = document.getElementById("builder-key1").value;
const value1 = document.getElementById("builder-value1").value;
const key2 = document.getElementById("builder-key2").value;
const value2 = document.getElementById("builder-value2").value;
const prefix2 = document.getElementById("builder-prefix2").value;
let logger = new Logger({
timestamped: false,
transports: [transport],
});
// Build step by step
if (prefix) {
logger = logger.withPrefix(prefix);
}
if (key1 && value1) {
logger = logger.withContext(key1, value1);
}
if (key2 && value2) {
logger = logger.withContext(key2, value2);
}
if (prefix2) {
logger = logger.withPrefix(prefix2);
}
// Update code example
const codeElement = document.getElementById("builder-code");
let code = "let logger = new Logger()\n";
if (prefix) code += `logger = logger.withPrefix('${prefix}')\n`;
if (key1 && value1)
code += `logger = logger.withContext('${key1}', '${value1}')\n`;
if (key2 && value2)
code += `logger = logger.withContext('${key2}', '${value2}')\n`;
if (prefix2) code += `logger = logger.withPrefix('${prefix2}')\n`;
code += '\n// Test the logger\nlogger.info("Custom logger test")';
codeElement.textContent = code;
// Test the logger
logger.info("Custom logger configuration test");
logger.info("Processing with custom context");
logger.warn("Warning with full context chain");
logger.info("Final test message");
};
window.showBuilderSteps = function () {
const transport = new DemoTransport("builder-output");
transport.clear();
const baseLogger = new Logger({
timestamped: false,
transports: [transport],
});
baseLogger.info("Step 1: Base logger created");
const step2 = baseLogger.withPrefix("DEMO");
step2.info("Step 2: Prefix added");
const step3 = step2.withContext("userId", "user123");
step3.info("Step 3: First context added");
const step4 = step3.withContext("requestId", "req-abc-456");
step4.info("Step 4: Second context added");
const final = step4.withPrefix("AUTH");
final.info("Step 5: Additional prefix added - Final logger!");
};
window.demonstrateBestPractices = function () {
const transport = new DemoTransport("practices-output");
transport.clear();
const logger = new Logger({
timestamped: false,
transports: [transport],
});
// Good practices
logger.info("✅ GOOD: Use meaningful prefixes");
const apiLogger = logger.withPrefix("API");
apiLogger.info("Request processed");
logger.info("✅ GOOD: Add context for traceability");
const trackedLogger = logger.withContext("requestId", "req-123");
trackedLogger.info("Operation completed");
logger.info("✅ GOOD: Chain contexts logically");
const chainedLogger = logger
.withContext("userId", "user123")
.withContext("sessionId", "sess-456");
chainedLogger.info("User action logged");
logger.info("✅ GOOD: Remove sensitive context when needed");
const publicLogger = chainedLogger.withoutContextKey("userId");
publicLogger.info("Public log without user ID");
};
window.demonstratePerformance = function () {
const transport = new DemoTransport("practices-output");
const logger = new Logger({
timestamped: false,
transports: [transport],
});
logger.info("⚡ PERFORMANCE: Reuse logger instances");
logger.info("⚡ PERFORMANCE: Context is lightweight - no deep copying");
logger.info("⚡ PERFORMANCE: Prefixes are cached and optimized");
logger.info("⚡ PERFORMANCE: Method chaining creates minimal overhead");
};
window.demonstrateAntiPatterns = function () {
const transport = new DemoTransport("practices-output");
const logger = new Logger({
timestamped: false,
transports: [transport],
});
logger.warn("❌ AVOID: Creating new loggers for every message");
logger.warn("❌ AVOID: Adding too much context (keep it relevant)");
logger.warn("❌ AVOID: Very long prefixes that clutter output");
logger.warn("❌ AVOID: Sensitive data in context (use redaction)");
};
window.clearOutput = function (elementId) {
const element = document.getElementById(elementId);
if (element) {
element.innerHTML = "";
}
};
window.demonstrateNamespaces = function () {
const transport = new DemoTransport("namespace-output");
transport.clear();
transport.write("=== Namespace Hierarchy Demo ===\n");
// Create loggers with different namespaces
const appLogger = getLogger("app", { transports: [transport] });
const authLogger = getLogger("app:auth", { transports: [transport] });
const dbLogger = getLogger("app:db:users", { transports: [transport] });
const apiLogger = getLogger("app:api:v1", { transports: [transport] });
const workerLogger = getLogger("worker:email-queue", {
transports: [transport],
});
transport.write("📱 Application Loggers:");
appLogger.info("Application started");
authLogger.info("User authentication initiated", { userId: 12345 });
dbLogger.info("Database query executed", {
table: "users",
duration: "45ms",
});
apiLogger.info("API request processed", {
endpoint: "/users/profile",
status: 200,
});
transport.write("\n🔧 Background Workers:");
workerLogger.info("Email queued for delivery", {
recipient: "user@example.com",
priority: "high",
});
transport.write(
"\n💡 Each namespace can have its own configuration and filtering rules!"
);
};
window.demonstrateNamespaceLevels = function () {
const transport = new DemoTransport("namespace-output");
transport.clear();
transport.write("=== Namespace Level Configuration ===\n");
// Set different log levels for different namespaces
setNamespaceLevel("app:db:*", LogLevel.WARN);
setNamespaceLevel("app:auth:*", LogLevel.DEBUG);
setNamespaceLevel("worker:*", LogLevel.ERROR);
transport.write("🎚️ Configured namespace levels:");
transport.write(" app:db:* → WARN level (only warnings and errors)");
transport.write(" app:auth:* → DEBUG level (all messages)");
transport.write(" worker:* → ERROR level (only errors)");
transport.write("\n📊 Testing with different log levels:");
// Create loggers that will respect the namespace levels
const dbLogger = getLogger("app:db:connections", {
transports: [transport],
});
const authLogger = getLogger("app:auth:jwt", {
transports: [transport],
});
const workerLogger = getLogger("worker:email-queue", {
transports: [transport],
});
transport.write("\n🗄️ Database logger (WARN level):");
dbLogger.debug("This debug message will be filtered out");
dbLogger.info("This info message will be filtered out");
dbLogger.warn("Connection pool running low", {
available: 2,
total: 10,
});
dbLogger.error("Connection failed", { host: "db.example.com" });
transport.write("\n🔐 Auth logger (DEBUG level):");
authLogger.debug("JWT token validation started");
authLogger.info("User permissions loaded", {
roles: ["user", "admin"],
});
authLogger.warn("Token expires soon", { expiresIn: "5 minutes" });
transport.write("\n⚙️ Worker logger (ERROR level):");
workerLogger.debug("This debug will be filtered");
workerLogger.info("This info will be filtered");
workerLogger.warn("This warning will be filtered");
workerLogger.error("Failed to send email", { error: "SMTP timeout" });
transport.write(
"\n💡 Use environment variables: LOGGICAL_NAMESPACES='app:db:*:warn,worker:*:error'"
);
};
// Initialize with basic examples
document.addEventListener("DOMContentLoaded", function () {
demonstrateBasicPrefix();
demonstrateBasicContext();
});
</script>
</body>
</html>✨ Features
- ✅ Prefix and context attachment demonstrations
- ✅ Method chaining examples with visual flow
- ✅ Context inheritance and nested scenarios
- ✅ Interactive context builder with live updates
- ✅ Context removal and cleanup patterns
🚀 Running This Demo
bash
# Build the package first
pnpm run build
# Start the demo server
node examples/serve.js
# Then open in your browser:
# http://localhost:3000/context-demo.html🔗 Related
- ← Back to Examples Overview
- API Reference - Technical documentation
- Getting Started Guide - Setup instructions
This interactive demo is automatically embedded from examples/context-demo.html.
