Skip to content

Instantly share code, notes, and snippets.

@philmander
Created December 31, 2017 00:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philmander/7788b680acb776bab4ae67df63db227a to your computer and use it in GitHub Desktop.
Save philmander/7788b680acb776bab4ae67df63db227a to your computer and use it in GitHub Desktop.
Send to server stream for Browser Bunyan
const userAgent = typeof window !== 'undefined' ? window.navigator.userAgent : 'no-window';
const isBot = /bot|crawler|spider|crawling/i.test(userAgent);
export class ServerLogStream {
constructor(opts = {}) {
const {
writeCondition = ServerLogStream.defaultWriteCondition,
} = opts;
this.opts = opts;
this.writeCondition = writeCondition;
this.records = {};
this.start();
}
start() {
const {
method = 'PUT',
url = '/log',
throttleInterval = 3000,
withCredentials = false,
onError,
} = this.opts;
const throttleRequests = () => {
// wait for any errors to accumulate
this.currentThrottleTimeout = setTimeout(() => {
const recs = Object.keys(this.records).map(errKey => this.records[errKey]);
if(recs.length) {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if(xhr.readyState === XMLHttpRequest.DONE) {
if(xhr.status >= 400) {
if(typeof onError === 'function') {
onError.call(this, recs, xhr);
} else {
console.warn('Browser Bunyan: A server log write failed');
}
}
this.records = {};
throttleRequests();
}
};
xhr.open(method, url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.withCredentials = withCredentials;
xhr.send(JSON.stringify(recs));
} else {
throttleRequests();
}
}, throttleInterval);
};
throttleRequests();
}
stop() {
setTimeout(() => {
if(this.currentThrottleTimeout) {
clearTimeout(this.currentThrottleTimeout);
this.currentThrottleTimeout = null;
}
}, 1);
}
write(rec) {
rec.url = typeof window !== 'undefined' && window.location.href;
rec.userAgent = userAgent;
if(this.currentThrottleTimeout && this.writeCondition(rec)) {
if(this.records[rec.msg]) {
this.records[rec.msg].count++;
} else {
rec.count = 1;
this.records[rec.msg] = rec;
}
}
}
static defaultWriteCondition() {
return window.navigator.onLine && !isBot;
}
}
@philmander
Copy link
Author

Will release this as a separate module for browser-bunyan 2.x

@peanutbother
Copy link

peanutbother commented May 2, 2019

I was using this method with slight changes.
Beware that if array gets deleted while new items are added these items will get lost.
you should use an array and change to array.splice(0, array.length) copying it to a temporary array so new values won't get lost in transaction.

kindly regards

// [...]
this.records = []; // use an array instead

    this.start();
  }

  start() {
    const { throttleInterval = 3000, client } = this.opts;

    const throttleRequests = () => {
      // wait for any errors to accumulate
      this.currentThrottleTimeout = setTimeout(() => {
        const recs = this.records.splice(0, this.records.length); // create a copy of current values, empty this.records
        if (recs.length > 0) { // check temparray isn't empty
          // [...] some client logic
           if(fail) {
              // add tmp array back to array (reschedule)
              this.records.splice(0, 0, ...recs);
           }
          }
          throttleRequests();
        } else {
          throttleRequests();
        }
      }, throttleInterval);
    };

    throttleRequests();
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment