threads-list.vue 7.58 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
  <table class="threads table is-fullwidth is-hoverable">
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
    <thead>
      <tr>
        <th class="threads__thread-name">Name</th>
        <th class="threads__timeline">
          <svg class="threads__scale" height="24px"/>
        </th>
      </tr>
    </thead>
    <tbody>
      <template v-for="thread in threadTimelines">
        <tr class="is-selectable" :key="thread.threadId"
            @click="showDetails[thread.threadId] ? $delete(showDetails, thread.threadId) : $set(showDetails, thread.threadId, true)">
          <td class="threads__thread-name">
            <thread-tag :thread-state="thread.threadState"/>
            <span v-text="thread.threadName"/>
          </td>
          <td class="threads__timeline">
            <svg :id="`thread-${thread.threadId}`" height="26px"/>
          </td>
38
        </tr>
39 40 41
        <tr :key="`${thread.threadId}-detail`"
            v-if="showDetails[thread.threadId]">
          <td colspan="2">
42
            <table class="threads__thread-details table is-narrow is-fullwidth has-background-white-ter">
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
              <tr>
                <td>Thread Id</td>
                <td v-text="thread.threadId"/>
              </tr>
              <tr>
                <td>Thread name</td>
                <td v-text="thread.threadName"/>
              </tr>
              <tr>
                <td>Thread state</td>
                <td v-text="thread.threadState"/>
              </tr>
              <template v-if="thread.details !== null">
                <tr>
                  <td>Blocked count</td>
                  <td v-text="thread.details.blockedCount"/>
                </tr>
                <tr>
                  <td>Blocked time</td>
                  <td v-text="thread.details.blockedTime"/>
                </tr>
                <tr>
                  <td>Waited count</td>
                  <td v-text="thread.details.waitedCount"/>
                </tr>
                <tr>
                  <td>Waited time</td>
                  <td v-text="thread.details.waitedTime"/>
                </tr>
                <tr>
                  <td>Lock name</td>
                  <td v-text="thread.details.lockName"/>
                </tr>
                <tr>
                  <td>Lock owner id</td>
                  <td v-text="thread.details.lockOwnerId"/>
                </tr>
                <tr>
                  <td>Lock owner name</td>
                  <td v-text="thread.details.lockOwnerName"/>
                </tr>
                <tr v-if="thread.details.stackTrace.length > 0">
                  <td colspan="2">Stacktrace
86
                    <pre class="threads__thread-stacktrace"><template
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
                      v-for="(frame, idx) in thread.details.stackTrace"><span
                      :key="`frame-${thread.threadId}-${idx}`"
                      v-text="`${frame.className}.${frame.methodName}(${frame.fileName}:${frame.lineNumber})`"/> <span
                      :key="`frame-${thread.threadId}-${idx}-native`"
                      class="tag is-dark" v-if="frame.nativeMethod">native</span>
                    </template></pre>
                  </td>
                </tr>
              </template>
            </table>
          </td>
        </tr>
      </template>
    </tbody>
  </table>
Johannes Edmeier committed
102 103
</template>
<script>
104
  import d3 from '@/utils/d3';
Johannes Edmeier committed
105
  import _ from 'lodash';
106
  import moment from 'moment';
Johannes Edmeier committed
107 108
  import threadTag from './thread-tag';

109
  const maxPixelsPerSeconds = 15;
Johannes Edmeier committed
110 111

  export default {
112 113 114 115 116 117
    props: {
      threadTimelines: {
        type: Object,
        required: true
      }
    },
Johannes Edmeier committed
118 119 120 121 122 123 124 125 126
    data: () => ({
      showDetails: {},
      lastEndPosition: 0
    }),
    components: {
      threadTag
    },
    watch: {
      threadTimelines: {
127
        deep: true,
Johannes Edmeier committed
128 129
        handler: 'drawTimelines',
        immediate: true
Johannes Edmeier committed
130 131 132 133
      }
    },
    methods: {
      getTimeExtent(timelines) {
134
        return _.entries(timelines).map(([, value]) => value.timeline)
Johannes Edmeier committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
          .map(timeline => ({
            start: timeline[0].start,
            end: timeline[timeline.length - 1].end
          }))
          .reduce((current, next) => ({
            start: Math.min(current.start, next.start),
            end: Math.max(current.end, next.end)
          }), {start: Number.MAX_SAFE_INTEGER, end: Number.MIN_SAFE_INTEGER});
      },
      async drawTimelines(timelines) {
        if (timelines) {
          const wasInView = this.isInView(this.lastEndPosition);
          await this.$nextTick();

          const {start, end} = this.getTimeExtent(timelines);
150 151
          const width = this.$el.querySelector('.threads__timeline').getBoundingClientRect().width;
          const totalSeconds = Math.floor(width / maxPixelsPerSeconds);
Johannes Edmeier committed
152 153
          const x = d3.scaleTime()
            .range([0, width])
154
            .domain([start, Math.max(start + (totalSeconds + 1) * 1000, end)]);
Johannes Edmeier committed
155 156 157 158

          d3.select('.threads__scale')
            .attr('width', width)
            .call(d3.axisBottom(x)
159
              .ticks(Math.max(2, Math.floor(width / 50)))
160
              .tickFormat(d => moment(d).format('HH:mm:ss'))
Johannes Edmeier committed
161 162 163 164 165 166 167 168 169
            );

          _.entries(timelines).forEach(([threadId, value]) => {
            const svg = d3.select(`#thread-${threadId}`).attr('width', width);
            const d = svg.selectAll('rect').data(value.timeline);

            d.enter()
              .append('rect')
              .merge(d)
170
              .attr('class', d => `thread--${d.threadState.toLowerCase()}`)
Johannes Edmeier committed
171
              .attr('height', '2em')
172
              .attr('x', d => x(d.start))
Johannes Edmeier committed
173
              .transition(150)
174
              .attr('width', d => Math.max(x(d.end) - x(d.start), x(d.start + 500) - x(d.start)))
Johannes Edmeier committed
175 176 177 178 179 180 181 182 183 184 185
          });

          this.lastEndPosition = x(end);
          if (wasInView && !this.isInView(this.lastEndPosition)) {
            const scrollable = this.$el;
            scrollable.scroll(this.lastEndPosition, scrollable.scrollHeight);
          }
        }
      },
      isInView(xPos) {
        const scrollable = this.$el;
186
        return scrollable && xPos >= scrollable.scrollLeft && xPos <= (scrollable.scrollLeft + scrollable.clientWidth);
Johannes Edmeier committed
187 188 189 190 191
      }
    }
  }
</script>
<style lang="scss">
Johannes Edmeier committed
192
  @import "~@/assets/css/utilities";
Johannes Edmeier committed
193

Johannes Edmeier committed
194 195
  .threads {
    table-layout: fixed;
Johannes Edmeier committed
196

Johannes Edmeier committed
197 198 199 200 201 202
    &__thread-name {
      width: 250px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
Johannes Edmeier committed
203

204 205 206 207 208 209 210 211 212 213 214 215
    &__thread-details {
      table-layout: fixed;

      & td:first-child:not(.threads__thread-stacktrace) {
        width: 20%;
      }
    }
    &__thread-stacktrace {
      overflow: auto;
      max-height: 300px;
    }

Johannes Edmeier committed
216 217 218 219 220 221
    &__timeline {
      width: auto;
      overflow: hidden;
      text-overflow: ellipsis;
      padding-left: 0 !important;
      padding-right: 0 !important;
Johannes Edmeier committed
222

Johannes Edmeier committed
223 224 225 226
      & svg {
        display: block; //prevent margin bottom on svg
      }
    }
Johannes Edmeier committed
227

Johannes Edmeier committed
228 229 230 231
    &__scale {
      & .domain {
        display: none;
      }
Johannes Edmeier committed
232
    }
Johannes Edmeier committed
233
  }
Johannes Edmeier committed
234

Johannes Edmeier committed
235 236 237 238
  .thread {
    &--runnable {
      fill: $success;
    }
Johannes Edmeier committed
239

Johannes Edmeier committed
240 241 242 243
    &--timed_waiting,
    &--waiting {
      fill: $warning;
    }
Johannes Edmeier committed
244

Johannes Edmeier committed
245 246
    &--blocked {
      fill: $danger;
Johannes Edmeier committed
247
    }
Johannes Edmeier committed
248
  }
249
</style>