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