open-nomad/ui/app/utils/classes/log.js

129 lines
3.4 KiB
JavaScript

import Ember from 'ember';
import { alias } from '@ember/object/computed';
import { assert } from '@ember/debug';
import Evented from '@ember/object/evented';
import EmberObject, { computed } from '@ember/object';
import { assign } from '@ember/polyfills';
import queryString from 'npm:query-string';
import { task } from 'ember-concurrency';
import StreamLogger from 'nomad-ui/utils/classes/stream-logger';
import PollLogger from 'nomad-ui/utils/classes/poll-logger';
const MAX_OUTPUT_LENGTH = 50000;
export const fetchFailure = url => () => Ember.Logger.warn(`LOG FETCH: Couldn't connect to ${url}`);
const Log = EmberObject.extend(Evented, {
// Parameters
url: '',
params: computed(() => ({})),
logFetch() {
assert('Log objects need a logFetch method, which should have an interface like window.fetch');
},
// Read-only state
isStreaming: alias('logStreamer.poll.isRunning'),
logPointer: null,
logStreamer: null,
// The top of the log
head: '',
// The bottom of the log
tail: '',
// The top or bottom of the log, depending on whether
// the logPointer is pointed at head or tail
output: computed('logPointer', 'head', 'tail', function() {
return this.get('logPointer') === 'head' ? this.get('head') : this.get('tail');
}),
init() {
this._super();
const args = this.getProperties('url', 'params', 'logFetch');
args.write = chunk => {
let newTail = this.get('tail') + chunk;
if (newTail.length > MAX_OUTPUT_LENGTH) {
newTail = newTail.substr(newTail.length - MAX_OUTPUT_LENGTH);
}
this.set('tail', newTail);
this.trigger('tick', chunk);
};
if (StreamLogger.isSupported) {
this.set('logStreamer', StreamLogger.create(args));
} else {
this.set('logStreamer', PollLogger.create(args));
}
},
destroy() {
this.stop();
this._super();
},
gotoHead: task(function*() {
const logFetch = this.get('logFetch');
const queryParams = queryString.stringify(
assign(this.get('params'), {
plain: true,
origin: 'start',
offset: 0,
})
);
const url = `${this.get('url')}?${queryParams}`;
this.stop();
let text = yield logFetch(url).then(res => res.text(), fetchFailure(url));
if (text && text.length > MAX_OUTPUT_LENGTH) {
text = text.substr(0, MAX_OUTPUT_LENGTH);
text += '\n\n---------- TRUNCATED: Click "tail" to view the bottom of the log ----------';
}
this.set('head', text);
this.set('logPointer', 'head');
}),
gotoTail: task(function*() {
const logFetch = this.get('logFetch');
const queryParams = queryString.stringify(
assign(this.get('params'), {
plain: true,
origin: 'end',
offset: MAX_OUTPUT_LENGTH,
})
);
const url = `${this.get('url')}?${queryParams}`;
this.stop();
let text = yield logFetch(url).then(res => res.text(), fetchFailure(url));
this.set('tail', text);
this.set('logPointer', 'tail');
}),
startStreaming() {
this.set('logPointer', 'tail');
return this.get('logStreamer').start();
},
stop() {
this.get('logStreamer').stop();
},
});
export default Log;
export function logger(urlProp, params, logFetch) {
return computed(urlProp, params, function() {
return Log.create({
logFetch: logFetch.call(this),
params: this.get(params),
url: this.get(urlProp),
});
});
}