index.vue 4.67 KB
Newer Older
Johannes Edmeier committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<!--
  - Copyright 2014-2018 the original author or authors.
  -
  - Licensed under the Apache License, Version 2.0 (the "License");
  - you may not use this file except in compliance with the License.
  - You may obtain a copy of the License at
  -
  -     http://www.apache.org/licenses/LICENSE-2.0
  -
  - Unless required by applicable law or agreed to in writing, software
  - distributed under the License is distributed on an "AS IS" BASIS,
  - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  - See the License for the specific language governing permissions and
  - limitations under the License.
  -->

<template>
18 19 20 21 22 23 24 25 26
  <div class="section logfile-view" :class="{ 'is-loading' : !hasLoaded }">
    <div v-if="error" class="message is-danger">
      <div class="message-body">
        <strong>
          <font-awesome-icon class="has-text-danger" icon="exclamation-triangle"/>
          Fetching logfile failed.
        </strong>
        <p v-text="error.message"/>
      </div>
Johannes Edmeier committed
27
    </div>
28 29 30 31 32 33 34 35 36 37 38 39 40 41
    <div class="logfile-view-actions" v-if="hasLoaded">
      <div class="logfile-view-actions__navigation">
        <sba-icon-button :disabled="atTop" @click.native="scrollToTop" icon="step-backward" size="lg"
                         icon-class="rotated"/>
        <sba-icon-button :disabled="atBottom" @click.native="scrollToBottom" icon="step-forward" size="lg"
                         icon-class="rotated"/>
      </div>
      <a class="button" v-if="instance" :href="`instances/${instance.id}/logfile`" target="_blank">
        <font-awesome-icon icon="download"/>&nbsp;Download
      </a>
    </div>
    <p v-if="skippedBytes" v-text="`skipped ${prettyBytes(skippedBytes)}`"/>
    <!-- log will be appended here -->
  </div>
Johannes Edmeier committed
42 43 44
</template>

<script>
45
  import subscribing from '@/mixins/subscribing';
46
  import Instance from '@/services/instance';
Johannes Edmeier committed
47 48 49 50 51
  import {animationFrame, Observable} from '@/utils/rxjs';
  import _ from 'lodash';
  import prettyBytes from 'pretty-bytes';

  export default {
52 53 54 55 56 57
    props: {
      instance: {
        type: Instance,
        required: true
      }
    },
58
    mixins: [subscribing],
Johannes Edmeier committed
59
    data: () => ({
60 61
      hasLoaded: false,
      error: null,
Johannes Edmeier committed
62 63
      atBottom: true,
      atTop: false,
64
      skippedBytes: null
Johannes Edmeier committed
65 66 67 68 69 70 71 72 73 74 75 76
    }),
    created() {
      this.scrollParent = null;
    },
    mounted() {
      this.scrollParent = document.documentElement;
      window.addEventListener('scroll', this.onScroll);
    },
    beforeDestroy() {
      window.removeEventListener('scroll', this.onScroll);
    },
    methods: {
77
      prettyBytes,
78
      createSubscription() {
Johannes Edmeier committed
79 80
        const vm = this;
        if (this.instance) {
81
          vm.error = null;
82
          return this.instance.streamLogfile(1000)
Johannes Edmeier committed
83 84 85 86 87 88
            .do(chunk => vm.skippedBytes = vm.skippedBytes || chunk.skipped)
            .concatMap(chunk => _.chunk(chunk.addendum.split(/\r?\n/), 250))
            .map(lines => Observable.of(lines, animationFrame))
            .concatAll()
            .subscribe({
              next: lines => {
89
                vm.hasLoaded = true;
Johannes Edmeier committed
90 91 92 93 94 95 96 97 98 99
                lines.forEach(line => {
                  const child = document.createElement('pre');
                  child.textContent = line;
                  vm.$el.appendChild(child);
                });

                if (vm.atBottom) {
                  vm.scrollToBottom();
                }
              },
100 101 102 103
              error: error => {
                vm.hasLoaded = true;
                console.warn('Fetching logfile failed:', error);
                vm.error = error;
Johannes Edmeier committed
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
              }
            });
        }
      },
      onScroll() {
        this.atBottom = this.scrollParent.scrollTop >= this.scrollParent.scrollHeight - this.scrollParent.clientHeight;
        this.atTop = this.scrollParent.scrollTop === 0;
      },
      scrollToTop() {
        this.scrollParent.scrollTo(this.scrollParent.scrollLeft, 0);
      },
      scrollToBottom() {
        this.scrollParent.scrollTo(this.scrollParent.scrollLeft, (this.scrollParent.scrollHeight - this.scrollParent.clientHeight))
      }
    }
  }
</script>

<style lang="scss">
123
  @import "~@/assets/css/utilities";
Johannes Edmeier committed
124

125 126 127 128 129 130
  .logfile-view {
    pre {
      word-break: break-all;
      padding: 0;
      white-space: pre-wrap;
      width: 100%;
Johannes Edmeier committed
131

132 133 134 135
      &:hover {
        background: $grey-lighter;
      }
    }
Johannes Edmeier committed
136

137 138 139 140 141 142 143
    &-actions {
      top: (($gap / 2) + $navbar-height-px + $tabs-height-px);
      right: ($gap /2);
      display: flex;
      align-items: center;
      position: sticky;
      float: right;
Johannes Edmeier committed
144

145 146 147 148 149 150
      &__navigation {
        display: inline-flex;
        flex-direction: column;
        justify-content: space-between;
        margin-right: 0.5rem;
      }
Johannes Edmeier committed
151
    }
152
  }
Johannes Edmeier committed
153

154 155 156 157
  .rotated {
    transform: rotate(90deg);
  }
</style>