Ajax Listener module in the Tealeaf Web SDK

The Ajax Listener module captures XMLHttpRequest (XHR) and Fetch API network traffic in user sessions, allowing you to view API calls in Tealeaf replay.

Key features:

  • Logs XHR and Fetch requests/responses
  • Filters for selective logging
  • Privacy controls via blocklists
  • Cooperative chaining with other monitoring tools
  • PerformanceObserver fallback for compatibility

Browser compatibility

  • Supported: Modern browsers with native XHR and Fetch APIs
  • Not recommended: Polyfilled implementations

Installation

Option 1: Inline installation

  1. Copy the module code ajaxListener.min.js into your Web SDK JavaScript file.
  2. Place the code after the SDK core and before the configuration/initialization section.

File structure:

  • Pako (gzip encoder)
  • SDK Core (library without optional modules or config section)
  • Ajax Listener
  • Configuration
  • Initialization (command to initialize the SDK and apply its configuration)

Option 2: CDN loading

<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.min.js"></script>
<script>
    (function () {
        function configureSDK() {
            const config = window.TLT.getDefaultConfig();
            config.core.modules.ajaxListener.enabled = true;
            config.modules.ajaxListener = {
                cooperativeChaining: true,
                filters: [{
                    log: {
                        requestHeaders: true,
                        responseData: true
                    }
                }]
            };
            return config;
        }

        TLT.initLibAdv({
            appKey: "APP_KEY",
            postUrl: "COLLECTOR_URL",
            newConfig: configureSDK(),
            addAjaxListener: true
        });
    }());
</script>

Configuration

Core settings

Enable/disable capture

ajaxListener: {
    xhrEnabled: true,           // Enable XHR monitoring (default: true)
    fetchEnabled: true,          // Enable Fetch monitoring (default: true)
    cooperativeChaining: false   // Work with other tools (default: false)
}

URL blocking

Block specific URLs from being logged:

urlBlocklist: [
    { regex: "analytics\\.com", flags: "i" },
    { regex: "tracking" }
]

Field privacy

Mask sensitive fields in request/response data:

fieldBlocklist: [
    "password",
    "ssn",
    { regex: ".*token.*", flags: "i" }
]

Response Filtering

blockNonJSONResponse: false  // Block non-JSON responses from logging

Filtering rules

Basic filter structure

filters: [{
    url: { regex: "/api/", flags: "i" },
    method: { regex: "POST", flags: "i" },
    status: { regex: "200" },
    log: {
        requestHeaders: true,
        requestData: true,
        responseHeaders: true,
        responseData: true
    }
}]

📘

Tip

When specifying multiple filters, place more restrictive rules before generic ones. The module uses the first matching filter.

Common patterns

Log all request headers:

{
    log: { requestHeaders: true }
}

Log GET response data only:

{
    method: { regex: "GET", flags: "i" },
    log: { responseData: true }
}

Log errors (4xx status):

{
    status: { regex: "^4\\d\\d$" },
    log: {
        requestHeaders: true,
        requestData: true
    }
}

Log specific endpoint:

{
    url: { regex: "\\/api\\/getAccountDetails" },
    log: {
        requestHeaders: true,
        requestData: true,
        responseHeaders: true,
        responseData: true
    }
}

Log with query parameter:

{
    url: { regex: "debug=on(&|$)" },
    log: {
        requestHeaders: true,
        requestData: true,
        responseHeaders: true,
        responseData: true
    }
}

Data logged by default

Every XHR/Fetch call logs these fields automatically:

  • requestURL - Request URL host and path
  • method - HTTP method (GET, POST, etc.)
  • status - HTTP response status code
  • statusText - HTTP response status text
  • async - Boolean for async request
  • ajaxResponseTime - Milliseconds from send to complete
  • locationHref - Document location when request sent

Advanced features

Version 1.4.0+ features

Cooperative chaining

Allows Ajax Listener to work alongside other monitoring tools without conflicts.

ajaxListener: {
    cooperativeChaining: true,
    monitorHookHealth: true,
    hookHealthCheckInterval: 5000,  // Check every 5 seconds
    debugLog: true
}

How it works:

  • Detects if another tool wrapped XHR/Fetch
  • Preserves and chains with existing wrappers
  • Monitors hook health at runtime
  • Auto-enables fallback if hooks fail

PerformanceObserver Fallback

Non-invasive monitoring when hooks fail or conflicts occur.

ajaxListener: {
    preferPerformanceObserver: true,  // Use as primary method
    enablePerformanceObserverFallback: true  // Use as backup
}

Limitations:

  • Cannot capture request/response headers
  • Cannot capture request/response body
  • Limited method detection
  • Status code may not be available in all browsers

Debug Logging

Enable diagnostic messages for troubleshooting:

debugLog: true  // Shows hook chaining, fallback info, errors

URL Filtering (v1.4.0+)

Filters now match full absolute URLs including protocol, host, and path.

Examples:

// Match specific domain
{ regex: "https://api\\.example\\.com/" }

// Match any domain with /api/ path
{ regex: "/api/" }

// Match localhost on any port
{ regex: "http://localhost:\\d+/" }

// Match specific protocol
{ regex: "^https://" }

Benefits:

  • More precise filtering by domain
  • Distinguish between same-path different-domain requests
  • Support for cross-domain API monitoring
  • Better control over external APIs

Troubleshooting

Issue: AJAX calls not logging

Symptoms: AJAX calls not appearing in logs

Solutions:

  1. Enable debug logging:
   debugLog: true
  1. Check if native APIs are wrapped:
   console.log(window.XMLHttpRequest.toString())
   console.log(window.fetch.toString())
   // Should contain "[native code]"
  1. Try enabling fallback:
   enablePerformanceObserverFallback: true
  1. Use PerformanceObserver as primary:
   preferPerformanceObserver: true

Issue: conflicts with other tools

Symptoms: Other monitoring tools stop working

Solutions:

  1. Ensure Ajax Listener loads early in page lifecycle.
  2. Check if other tools support cooperative chaining.
  3. Enable cooperative chaining:
   cooperativeChaining: true
  1. Consider using PerformanceObserver mode:
   preferPerformanceObserver: true

Issue: Missing headers/body data

Symptoms: Missing request/response data in logs

Possible causes:

  1. PerformanceObserver mode active (check logs)
  2. Hooks were overwritten (check console warnings)
  3. Log configuration incomplete:
   log: {
       requestHeaders: true,
       requestData: true,
       responseHeaders: true,
       responseData: true
   }

Issue: limited data in logs

Check:

  • Is PerformanceObserver mode active?
  • Were hooks overwritten?
  • Is the log configuration complete?

Example output

Logged XHR data

{
  "type": 5,
  "offset": 9182,
  "customEvent": {
    "name": "ajaxListener",
    "data": {
      "requestURL": "www.acoustic.com/api/getAccountDetails",
      "method": "GET",
      "status": 200,
      "statusText": "OK",
      "async": true,
      "ajaxResponseTime": 285,
      "locationHref": "https://www.acoustic.com/support/login",
      "requestHeaders": {
        "X-Requested-With": "XMLHttpRequest",
        "X-CustomerId": "D295024"
      },
      "responseHeaders": {
        "date": "Thu, 22 Feb 2020 01:38:07 GMT",
        "content-type": "application/json"
      },
      "response": {
        "accountDetails": {
          "id": "D295024",
          "memberSince": "15 July 2012"
        }
      }
    }
  }
}

Ajax Listener Version History

v1.4.0 (current)

New features:

  • ✨ Filter URL matching uses full absolute URLs (protocol + host + path)
  • ✨ ES6/ES2015 compatibility (removed ES2020 features)
  • ✨ Cooperative chaining pattern for XHR and Fetch
  • ✨ PerformanceObserver fallback mechanism
  • ✨ Runtime hook health monitoring
  • ✨ Query string capture in queryParams object
  • ✨ Domain inclusion in URL filters

Improvements:

  • 🔧 Better compatibility with other monitoring tools
  • 🔧 Better cross-domain API monitoring support
  • 🔧 Debug logging configuration option

Bug fixes:

  • 🐛 Handle empty responses gracefully
  • 🐛 Handle URL objects in fetch calls (CA-102138)

Migration notes:

  • URL Filters: Pathname-only patterns (e.g., /api/) still work but now match against full URLs
  • cooperativeChaining: Default is false (strict behavior). Set to true to enable
  • ES6 Requirement: Ensure target browsers support ES6/ES2015

v1.3.4 (previous)

  • Basic XHR and Fetch interception
  • Simple safety checks
  • Native code detection

Best practices

✅ DO

  1. Start with default settings and adjust as needed
  2. Enable debugLog during development for diagnostics
  3. Use selective filters - don't log everything
  4. Configure fieldBlocklist for sensitive data (passwords, tokens, SSN)
  5. Test thoroughly before production deployment
  6. Monitor hook health - keep default monitoring enabled
  7. Consider PerformanceObserver for production - non-invasive and reliable
  8. Always test your application and validate captured data before deploying to production.

❌ DON'T

  1. Don't log everything - be selective to reduce data volume
  2. Don't ignore privacy - always configure field blocklists
  3. Don't use with polyfills - not recommended
  4. Don't forget to test - validate captured data before production

Configuration examples

Example 1: standard configuration (recommended)

{
  "ajaxListener": {
    "xhrEnabled": true,
    "fetchEnabled": true,
    "cooperativeChaining": true,
    "monitorHookHealth": true,
    "debugLog": true,
    "filters": [{
      "url": { "regex": "/api/" },
      "log": {
        "requestHeaders": true,
        "requestData": true,
        "responseHeaders": true,
        "responseData": true
      }
    }]
  }
}

Example 2: high compatibility mode

{
  "ajaxListener": {
    "preferPerformanceObserver": true,
    "debugLog": true,
    "filters": [{
      "url": { "regex": ".*" },
      "log": {
        "responseData": true
      }
    }]
  }
}

Example 3: domain-specific filtering

{
  "ajaxListener": {
    "filters": [
      {
        "url": {
          "regex": "^https://api\\.example\\.com/",
          "flags": "i"
        },
        "log": {
          "requestData": true,
          "responseData": true
        }
      },
      {
        "url": {
          "regex": "^https://internal\\.company\\.com/api/"
        },
        "log": {
          "requestHeaders": true,
          "requestData": true,
          "responseHeaders": true,
          "responseData": true
        }
      }
    ]
  }
}

Example 4: error monitoring

{
  "ajaxListener": {
    "filters": [{
      "status": { "regex": "[45]\\d\\d" },
      "log": {
        "requestHeaders": true,
        "requestData": false,
        "responseHeaders": true,
        "responseData": true
      }
    }]
  }
}

Additional resources