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, '&lt;').replace(/>/g, '&gt;');
    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  }