Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 1 | <!-- Copyright (C) 2019 The Android Open Source Project |
| 2 | |
| 3 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | you may not use this file except in compliance with the License. |
| 5 | You may obtain a copy of the License at |
| 6 | |
| 7 | http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | |
| 9 | Unless required by applicable law or agreed to in writing, software |
| 10 | distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | See the License for the specific language governing permissions and |
| 13 | limitations under the License. |
| 14 | --> |
| 15 | <template> |
| 16 | <md-card-content class="container"> |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 17 | <div class="navigation"> |
Nataniel Borges | f230a13 | 2021-07-28 07:13:45 +0000 | [diff] [blame] | 18 | <md-content |
| 19 | md-tag="md-toolbar" |
| 20 | md-elevation="0" |
| 21 | class="card-toolbar md-transparent md-dense" |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 22 | > |
Nataniel Borges | f230a13 | 2021-07-28 07:13:45 +0000 | [diff] [blame] | 23 | <h2 class="md-title" style="flex: 1">Log View</h2> |
| 24 | <md-button |
| 25 | class="md-dense md-primary" |
| 26 | @click.native="scrollToRow(lastOccuredVisibleIndex)" |
| 27 | > |
| 28 | Jump to latest entry |
| 29 | </md-button> |
| 30 | <md-button |
| 31 | class="md-icon-button" :class="{'md-primary': pinnedToLatest}" |
| 32 | @click.native="togglePin" |
| 33 | > |
| 34 | <md-icon>push_pin</md-icon> |
| 35 | <md-tooltip md-direction="top" v-if="pinnedToLatest"> |
| 36 | Unpin to latest message |
| 37 | </md-tooltip> |
| 38 | <md-tooltip md-direction="top" v-else> |
| 39 | Pin to latest message |
| 40 | </md-tooltip> |
| 41 | </md-button> |
| 42 | </md-content> |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 43 | </div> |
| 44 | |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 45 | <div class="filters"> |
| 46 | <md-field> |
Pablo Gamito | 42cef4b | 2020-06-26 16:55:33 +0100 | [diff] [blame] | 47 | <label>Log Levels</label> |
| 48 | <md-select v-model="selectedLogLevels" multiple> |
| 49 | <md-option v-for="level in logLevels" :value="level">{{ level }}</md-option> |
| 50 | </md-select> |
| 51 | </md-field> |
| 52 | |
| 53 | <md-field> |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 54 | <label>Tags</label> |
| 55 | <md-select v-model="selectedTags" multiple> |
| 56 | <md-option v-for="tag in tags" :value="tag">{{ tag }}</md-option> |
| 57 | </md-select> |
| 58 | </md-field> |
| 59 | |
| 60 | <md-autocomplete v-model="selectedSourceFile" :md-options="sourceFiles"> |
| 61 | <label>Source file</label> |
| 62 | |
| 63 | <template slot="md-autocomplete-item" slot-scope="{ item, term }"> |
| 64 | <md-highlight-text :md-term="term">{{ item }}</md-highlight-text> |
| 65 | </template> |
| 66 | |
| 67 | <template slot="md-autocomplete-empty" slot-scope="{ term }"> |
| 68 | No source file matching "{{ term }}" was found. |
| 69 | </template> |
| 70 | </md-autocomplete> |
| 71 | |
| 72 | <md-field class="search-message-field" md-clearable> |
| 73 | <md-input placeholder="Search messages..." v-model="searchInput"></md-input> |
| 74 | </md-field> |
| 75 | </div> |
| 76 | |
Pablo Gamito | f0031c4 | 2020-06-26 15:35:42 +0100 | [diff] [blame] | 77 | <div v-if="processedData.length > 0" style="overflow-y: auto;"> |
| 78 | <virtual-list style="height: 600px; overflow-y: auto;" |
| 79 | :data-key="'uid'" |
| 80 | :data-sources="processedData" |
| 81 | :data-component="logEntryComponent" |
| 82 | ref="loglist" |
| 83 | /> |
| 84 | </div> |
| 85 | <div class="no-logs-message" v-else> |
| 86 | <md-icon>error_outline</md-icon> |
| 87 | <span class="message">No logs founds...</span> |
| 88 | </div> |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 89 | </md-card-content> |
| 90 | </template> |
| 91 | <script> |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 92 | import { findLastMatchingSorted } from './utils/utils.js'; |
Pablo Gamito | 42cef4b | 2020-06-26 16:55:33 +0100 | [diff] [blame] | 93 | import { logLevel } from './utils/consts'; |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 94 | import LogEntryComponent from './LogEntry.vue'; |
| 95 | import VirtualList from '../libs/virtualList/VirtualList'; |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 96 | |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 97 | export default { |
| 98 | name: 'logview', |
| 99 | data() { |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 100 | const data = this.file.data; |
| 101 | |
| 102 | const tags = new Set(); |
| 103 | const sourceFiles = new Set(); |
| 104 | for (const line of data) { |
| 105 | tags.add(line.tag); |
| 106 | sourceFiles.add(line.at); |
| 107 | } |
| 108 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 109 | data.forEach((entry, index) => entry.index = index); |
| 110 | |
Pablo Gamito | 42cef4b | 2020-06-26 16:55:33 +0100 | [diff] [blame] | 111 | const logLevels = Object.values(logLevel); |
| 112 | |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 113 | return { |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 114 | data, |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 115 | isSelected: false, |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 116 | prevLastOccuredIndex: -1, |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 117 | lastOccuredIndex: 0, |
Pablo Gamito | 42cef4b | 2020-06-26 16:55:33 +0100 | [diff] [blame] | 118 | selectedTags: [], |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 119 | selectedSourceFile: null, |
| 120 | searchInput: null, |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 121 | sourceFiles: Object.freeze(Array.from(sourceFiles)), |
| 122 | tags: Object.freeze(Array.from(tags)), |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 123 | pinnedToLatest: true, |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 124 | logEntryComponent: LogEntryComponent, |
Pablo Gamito | 42cef4b | 2020-06-26 16:55:33 +0100 | [diff] [blame] | 125 | logLevels, |
| 126 | selectedLogLevels: [], |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 127 | } |
| 128 | }, |
| 129 | methods: { |
| 130 | arrowUp() { |
| 131 | this.isSelected = !this.isSelected; |
| 132 | return !this.isSelected; |
| 133 | }, |
| 134 | arrowDown() { |
| 135 | this.isSelected = !this.isSelected; |
| 136 | return !this.isSelected; |
Pablo Gamito | f2f36d3 | 2020-05-29 14:19:23 +0100 | [diff] [blame] | 137 | }, |
| 138 | getRowEl(idx) { |
| 139 | return this.$refs.tableBody.querySelectorAll('tr')[idx]; |
| 140 | }, |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 141 | togglePin() { |
| 142 | this.pinnedToLatest = !this.pinnedToLatest; |
| 143 | }, |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 144 | scrollToRow(index) { |
| 145 | if (!this.$refs.loglist) { |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 146 | return; |
| 147 | } |
| 148 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 149 | const itemOffset = this.$refs.loglist.virtual.getOffset(index); |
| 150 | const itemSize = 35; |
| 151 | const loglistSize = this.$refs.loglist.getClientSize(); |
Pablo Gamito | f2f36d3 | 2020-05-29 14:19:23 +0100 | [diff] [blame] | 152 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 153 | this.$refs.loglist.scrollToOffset(itemOffset - loglistSize + itemSize); |
Pablo Gamito | 396ab56 | 2020-06-01 10:25:12 +0100 | [diff] [blame] | 154 | }, |
Pablo Gamito | f0031c4 | 2020-06-26 15:35:42 +0100 | [diff] [blame] | 155 | getLastOccuredIndex(data, timestamp) { |
| 156 | if (this.data.length === 0) { |
| 157 | return 0; |
| 158 | } |
| 159 | return findLastMatchingSorted(data, |
| 160 | (array, idx) => array[idx].timestamp <= timestamp); |
| 161 | }, |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 162 | }, |
| 163 | watch: { |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 164 | pinnedToLatest(isPinned) { |
| 165 | if (isPinned) { |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 166 | this.scrollToRow(this.lastOccuredVisibleIndex); |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 167 | } |
| 168 | }, |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 169 | currentTimestamp: { |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 170 | immediate: true, |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 171 | handler(newTimestamp) { |
Pablo Gamito | 6c71e1f | 2020-06-05 19:01:08 +0100 | [diff] [blame] | 172 | this.prevLastOccuredIndex = this.lastOccuredIndex; |
Pablo Gamito | f0031c4 | 2020-06-26 15:35:42 +0100 | [diff] [blame] | 173 | this.lastOccuredIndex = this.getLastOccuredIndex(this.data, newTimestamp); |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 174 | |
| 175 | if (this.pinnedToLatest) { |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 176 | this.scrollToRow(this.lastOccuredVisibleIndex); |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 177 | } |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 178 | }, |
| 179 | } |
| 180 | }, |
| 181 | props: ['file'], |
| 182 | computed: { |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 183 | lastOccuredVisibleIndex() { |
Pablo Gamito | f0031c4 | 2020-06-26 15:35:42 +0100 | [diff] [blame] | 184 | return this.getLastOccuredIndex(this.processedData, this.currentTimestamp); |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 185 | }, |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 186 | currentTimestamp() { |
| 187 | return this.$store.state.currentTimestamp; |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 188 | }, |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 189 | processedData() { |
| 190 | const filteredData = this.data.filter(line => { |
Pablo Gamito | 42cef4b | 2020-06-26 16:55:33 +0100 | [diff] [blame] | 191 | if (this.selectedLogLevels.length > 0 && |
| 192 | !this.selectedLogLevels.includes(line.level.toLowerCase())) { |
| 193 | return false; |
| 194 | } |
| 195 | |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 196 | if (this.sourceFiles.includes(this.selectedSourceFile)) { |
| 197 | // Only filter once source file is fully inputed |
| 198 | if (line.at != this.selectedSourceFile) { |
| 199 | return false; |
| 200 | } |
| 201 | } |
| 202 | |
Pablo Gamito | 42cef4b | 2020-06-26 16:55:33 +0100 | [diff] [blame] | 203 | if (this.selectedTags.length > 0 && !this.selectedTags.includes(line.tag)) { |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 204 | return false; |
| 205 | } |
| 206 | |
| 207 | if (this.searchInput && !line.text.includes(this.searchInput)) { |
| 208 | return false; |
| 209 | } |
| 210 | |
| 211 | return true; |
| 212 | }); |
| 213 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 214 | for (const entry of filteredData) { |
| 215 | entry.new = this.prevLastOccuredIndex < entry.index && |
| 216 | entry.index <= this.lastOccuredIndex; |
| 217 | entry.occured = entry.index <= this.lastOccuredIndex; |
Pablo Gamito | 0c14b12 | 2020-07-24 11:38:19 +0100 | [diff] [blame] | 218 | entry.justInactivated = this.lastOccuredIndex < entry.index && |
| 219 | entry.index <= this.prevLastOccuredIndex; |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 220 | |
| 221 | // Force refresh if any of these changes |
Pablo Gamito | 0c14b12 | 2020-07-24 11:38:19 +0100 | [diff] [blame] | 222 | entry.uid = `${entry.index}${entry.new ? '-new' : ''}${entry.index}${entry.justInactivated ? '-just-inactivated' : ''}${entry.occured ? '-occured' : ''}` |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 223 | } |
| 224 | |
| 225 | return filteredData; |
| 226 | } |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 227 | }, |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 228 | components: { |
| 229 | 'virtual-list': VirtualList, |
| 230 | 'logentry': LogEntryComponent, |
| 231 | } |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | </script> |
| 235 | <style> |
Pablo Gamito | 050e20b | 2020-07-01 16:41:56 +0100 | [diff] [blame] | 236 | .container { |
| 237 | display: flex; |
| 238 | flex-wrap: wrap; |
| 239 | } |
| 240 | |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 241 | .filters, .navigation { |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 242 | width: 100%; |
| 243 | display: flex; |
Pablo Gamito | 677dbec | 2020-06-05 16:51:19 +0100 | [diff] [blame] | 244 | flex-direction: row; |
| 245 | align-items: center; |
| 246 | } |
| 247 | |
| 248 | .navigation { |
| 249 | justify-content: flex-end; |
| 250 | } |
| 251 | |
| 252 | .navigation > button { |
| 253 | margin: 0; |
Pablo Gamito | 84bd8ad | 2020-06-01 13:46:41 +0100 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | .filters > div { |
| 257 | margin: 10px; |
| 258 | } |
| 259 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 260 | .log-header { |
| 261 | display: inline-flex; |
| 262 | color: var(--md-theme-default-text-accent-on-background, rgba(0,0,0,0.54)); |
| 263 | font-weight: bold; |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 264 | } |
| 265 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 266 | .log-header > div { |
| 267 | padding: 6px 10px; |
| 268 | border-bottom: 1px solid #f1f1f1; |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 269 | } |
| 270 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 271 | .log-header .time-column { |
| 272 | width: 13em; |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 273 | } |
| 274 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 275 | .log-header .tag-column { |
| 276 | width: 10em; |
Adam Pardyl | 494b54f | 2019-09-25 11:59:23 +0200 | [diff] [blame] | 277 | } |
| 278 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 279 | .log-header .at-column { |
| 280 | width: 30em; |
Adam Pardyl | 494b54f | 2019-09-25 11:59:23 +0200 | [diff] [blame] | 281 | } |
| 282 | |
Pablo Gamito | 7099c95 | 2020-06-10 17:31:28 +0100 | [diff] [blame] | 283 | .column-title { |
| 284 | font-size: 12px; |
Pablo Gamito | 6c71e1f | 2020-06-05 19:01:08 +0100 | [diff] [blame] | 285 | } |
Pablo Gamito | f0031c4 | 2020-06-26 15:35:42 +0100 | [diff] [blame] | 286 | |
| 287 | .no-logs-message { |
| 288 | margin: 15px; |
| 289 | display: flex; |
| 290 | align-content: center; |
| 291 | align-items: center; |
| 292 | } |
| 293 | |
| 294 | .no-logs-message .message { |
| 295 | margin-left: 10px; |
| 296 | font-size: 15px; |
| 297 | } |
Adam Pardyl | f1af74f | 2019-09-16 12:18:15 +0200 | [diff] [blame] | 298 | </style> |