Skip to content

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, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");

        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

This interactive demo is automatically embedded from examples/context-demo.html.

Released under the MIT License.