github.com/emate/nomad@v0.8.2-wo-binpacking/ui/app/utils/classes/log.js (about) 1 import Ember from 'ember'; 2 import { alias } from '@ember/object/computed'; 3 import { assert } from '@ember/debug'; 4 import Evented from '@ember/object/evented'; 5 import EmberObject, { computed } from '@ember/object'; 6 import { assign } from '@ember/polyfills'; 7 import queryString from 'npm:query-string'; 8 import { task } from 'ember-concurrency'; 9 import StreamLogger from 'nomad-ui/utils/classes/stream-logger'; 10 import PollLogger from 'nomad-ui/utils/classes/poll-logger'; 11 12 const MAX_OUTPUT_LENGTH = 50000; 13 14 export const fetchFailure = url => () => Ember.Logger.warn(`LOG FETCH: Couldn't connect to ${url}`); 15 16 const Log = EmberObject.extend(Evented, { 17 // Parameters 18 19 url: '', 20 params: computed(() => ({})), 21 logFetch() { 22 assert('Log objects need a logFetch method, which should have an interface like window.fetch'); 23 }, 24 25 // Read-only state 26 27 isStreaming: alias('logStreamer.poll.isRunning'), 28 logPointer: null, 29 logStreamer: null, 30 31 // The top of the log 32 head: '', 33 34 // The bottom of the log 35 tail: '', 36 37 // The top or bottom of the log, depending on whether 38 // the logPointer is pointed at head or tail 39 output: computed('logPointer', 'head', 'tail', function() { 40 return this.get('logPointer') === 'head' ? this.get('head') : this.get('tail'); 41 }), 42 43 init() { 44 this._super(); 45 46 const args = this.getProperties('url', 'params', 'logFetch'); 47 args.write = chunk => { 48 let newTail = this.get('tail') + chunk; 49 if (newTail.length > MAX_OUTPUT_LENGTH) { 50 newTail = newTail.substr(newTail.length - MAX_OUTPUT_LENGTH); 51 } 52 this.set('tail', newTail); 53 this.trigger('tick', chunk); 54 }; 55 56 if (StreamLogger.isSupported) { 57 this.set('logStreamer', StreamLogger.create(args)); 58 } else { 59 this.set('logStreamer', PollLogger.create(args)); 60 } 61 }, 62 63 destroy() { 64 this.stop(); 65 this._super(); 66 }, 67 68 gotoHead: task(function*() { 69 const logFetch = this.get('logFetch'); 70 const queryParams = queryString.stringify( 71 assign(this.get('params'), { 72 plain: true, 73 origin: 'start', 74 offset: 0, 75 }) 76 ); 77 const url = `${this.get('url')}?${queryParams}`; 78 79 this.stop(); 80 let text = yield logFetch(url).then(res => res.text(), fetchFailure(url)); 81 82 if (text && text.length > MAX_OUTPUT_LENGTH) { 83 text = text.substr(0, MAX_OUTPUT_LENGTH); 84 text += '\n\n---------- TRUNCATED: Click "tail" to view the bottom of the log ----------'; 85 } 86 this.set('head', text); 87 this.set('logPointer', 'head'); 88 }), 89 90 gotoTail: task(function*() { 91 const logFetch = this.get('logFetch'); 92 const queryParams = queryString.stringify( 93 assign(this.get('params'), { 94 plain: true, 95 origin: 'end', 96 offset: MAX_OUTPUT_LENGTH, 97 }) 98 ); 99 const url = `${this.get('url')}?${queryParams}`; 100 101 this.stop(); 102 let text = yield logFetch(url).then(res => res.text(), fetchFailure(url)); 103 104 this.set('tail', text); 105 this.set('logPointer', 'tail'); 106 }), 107 108 startStreaming() { 109 this.set('logPointer', 'tail'); 110 return this.get('logStreamer').start(); 111 }, 112 113 stop() { 114 this.get('logStreamer').stop(); 115 }, 116 }); 117 118 export default Log; 119 120 export function logger(urlProp, params, logFetch) { 121 return computed(urlProp, params, function() { 122 return Log.create({ 123 logFetch: logFetch.call(this), 124 params: this.get(params), 125 url: this.get(urlProp), 126 }); 127 }); 128 }