Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(206)

Side by Side Diff: web/inc/logdog-stream-view/model.ts

Issue 2988993003: [logdog-view] Update UX, fix bugs. (Closed)
Patch Set: unfollow only on up scroll Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « web/inc/logdog-stream-view/logdog-stream-view.html ('k') | web/inc/logdog-stream-view/view.ts » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 Copyright 2016 The LUCI Authors. All rights reserved. 2 Copyright 2016 The LUCI Authors. All rights reserved.
3 Use of this source code is governed under the Apache License, Version 2.0 3 Use of this source code is governed under the Apache License, Version 2.0
4 that can be found in the LICENSE file. 4 that can be found in the LICENSE file.
5 */ 5 */
6 6
7 ///<reference path="../logdog-stream/logdog.ts" /> 7 ///<reference path="../logdog-stream/logdog.ts" />
8 ///<reference path="../luci-operation/operation.ts" /> 8 ///<reference path="../luci-operation/operation.ts" />
9 ///<reference path="../luci-sleep-promise/promise.ts" /> 9 ///<reference path="../luci-sleep-promise/promise.ts" />
10 ///<reference path="../rpc/client.ts" /> 10 ///<reference path="../rpc/client.ts" />
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 */ 53 */
54 TAIL, 54 TAIL,
55 /** 55 /**
56 * Represents an anchor point where the split occurred, obtained through a 56 * Represents an anchor point where the split occurred, obtained through a
57 * single "Tail()" RPC call. If the terminal index is known when the split 57 * single "Tail()" RPC call. If the terminal index is known when the split
58 * occurs, this should be the terminal index. 58 * occurs, this should be the terminal index.
59 */ 59 */
60 BOTTOM, 60 BOTTOM,
61 } 61 }
62 62
63 /**
64 * Interface of the specific Model functions used by the view. This is
65 * explicitly listed here as a reference for the functions that the
66 * model-viewer interface calls. See the Model class for method details.
67 *
68 * These methods are used to control the Model state and to nofity the Model
69 * of external events that occur.
70 */
71 interface ModelInterface {
72 fetch(cancel: boolean): Promise<void>;
73 split(): Promise<void>;
74
75 reset(): void;
76 setAutomatic(v: boolean): void;
77 setFetchFromTail(v: boolean): void;
78 notifyAuthenticationChanged(): void;
79 }
80
81 /** A log loading profile type. */ 63 /** A log loading profile type. */
82 type ModelProfile = { 64 type ModelProfile = {
83 /** The size of the first fetch. */ 65 /** The size of the first fetch. */
84 initialFetchSize: number; 66 initialFetchSize: number;
85 /** The size of the first fetch, if loading multiple streams. */ 67 /** The size of the first fetch, if loading multiple streams. */
86 multiInitialFetchSize: number; 68 multiInitialFetchSize: number;
87 /** The size of each subsequent fetch. */ 69 /** The size of each subsequent fetch. */
88 fetchSize: number; 70 fetchSize: number;
89 }; 71 };
90 72
91 /** 73 /**
92 * Model manages stream loading. 74 * Model manages stream loading.
93 * 75 *
94 * Model exports features to the user interface by conforming to
95 * ModelInterface. All Polymer Model functions must be documented in that
96 * interface.
97 *
98 * Model pushes state update to the user interface with the ViewBinding that 76 * Model pushes state update to the user interface with the ViewBinding that
99 * is provided to it on construction. 77 * is provided to it on construction.
100 * 78 *
101 * Model represents a view of the loading state of the configured log streams. 79 * Model represents a view of the loading state of the configured log streams.
102 * Log streams are individual named sets of consecutive records that are 80 * Log streams are individual named sets of consecutive records that are
103 * either streaming (no known terminal index, so the expectation is that new 81 * either streaming (no known terminal index, so the expectation is that new
104 * log records are being generated) or finished (known terminal index). The 82 * log records are being generated) or finished (known terminal index). The
105 * Model's job is to understand the state of these streams, load their data, 83 * Model's job is to understand the state of these streams, load their data,
106 * and cause it to be output to the user interface. 84 * and cause it to be output to the user interface.
107 * 85 *
(...skipping 20 matching lines...) Expand all
128 * 106 *
129 * If the HEAD pointer ever reaches the TAIL pointer, all logs between 0 and 107 * If the HEAD pointer ever reaches the TAIL pointer, all logs between 0 and
130 * SPLIT have been filled in and the fetching situation turns back into a 108 * SPLIT have been filled in and the fetching situation turns back into a
131 * standard sequential fetch. 109 * standard sequential fetch.
132 * 110 *
133 * This scheme is designed around the capabilities of the LogDog log API. 111 * This scheme is designed around the capabilities of the LogDog log API.
134 * 112 *
135 * The view may optionally expose itself as "mobile", indicating to the Model 113 * The view may optionally expose itself as "mobile", indicating to the Model
136 * that it should use mobile-friendly tuning parameters. 114 * that it should use mobile-friendly tuning parameters.
137 */ 115 */
138 export class Model implements ModelInterface { 116 export class Model {
139 /** Default single-stream profile. */ 117 /** Default single-stream profile. */
140 static DEFAULT_PROFILE: ModelProfile = { 118 static DEFAULT_PROFILE: ModelProfile = {
141 initialFetchSize: (1024 * 24), // 24KiB 119 initialFetchSize: (1024 * 24), // 24KiB
142 multiInitialFetchSize: (1024 * 4), // 4KiB 120 multiInitialFetchSize: (1024 * 4), // 4KiB
143 fetchSize: (4 * 1024 * 1024), // 4MiB 121 fetchSize: (4 * 1024 * 1024), // 4MiB
144 }; 122 };
145 123
146 /** Profile to use for a mobile device, regardless of stream count. */ 124 /** Profile to use for a mobile device, regardless of stream count. */
147 static MOBILE_PROFILE: ModelProfile = { 125 static MOBILE_PROFILE: ModelProfile = {
148 initialFetchSize: (1024 * 4), // 4KiB 126 initialFetchSize: (1024 * 4), // 4KiB
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 * Retained callback (Promise resolve) to invoke when authentication state 163 * Retained callback (Promise resolve) to invoke when authentication state
186 * changes. 164 * changes.
187 */ 165 */
188 private authChangedCallback: (() => void); 166 private authChangedCallback: (() => void);
189 167
190 /** The current fetch Promise. */ 168 /** The current fetch Promise. */
191 private currentOperation: luci.Operation|null = null; 169 private currentOperation: luci.Operation|null = null;
192 private currentFetchPromise: Promise<void>|null = null; 170 private currentFetchPromise: Promise<void>|null = null;
193 171
194 /** Are we in automatic mode? */ 172 /** Are we in automatic mode? */
195 private automatic = false; 173 private isAutomatic = false;
196 /** Are we tailing? */ 174 /** Are we tailing? */
197 private fetchFromTail = false; 175 private fetchFromTail = false;
198 /** Are we in the middle of rendering logs? */ 176 /** Are we in the middle of rendering logs? */
199 private rendering = true; 177 private rendering = true;
200 178
201 private cachedLogStreamUrl: string|undefined = undefined; 179 private cachedLogStreamUrl: string|undefined = undefined;
202 180
203 private loadingStateValue: LoadingState = LoadingState.NONE; 181 private loadingStateValue: LoadingState = LoadingState.NONE;
204 private streamStatusValue: StreamStatusEntry[]; 182 private streamStatusValue: StreamStatusEntry[];
205 183
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 default: 250 default:
273 provider = new AggregateLogStream(logStreams); 251 provider = new AggregateLogStream(logStreams);
274 break; 252 break;
275 } 253 }
276 provider.setStreamStatusCallback((st: LogStreamStatus[]) => { 254 provider.setStreamStatusCallback((st: LogStreamStatus[]) => {
277 if (this.provider === provider) { 255 if (this.provider === provider) {
278 this.streamStatus = this.buildStreamStatus(st); 256 this.streamStatus = this.buildStreamStatus(st);
279 } 257 }
280 }); 258 });
281 this.provider = provider; 259 this.provider = provider;
282 this.loadingState = LoadingState.NONE; 260 this.setIdleLoadingState();
261 }
262
263 private setIdleLoadingState() {
264 this.loadingState = (this.fetchedFullStream) ? (LoadingState.NONE) :
265 (LoadingState.PAUSED);
283 } 266 }
284 267
285 private resolvePathsIntoStreams(paths: string[]) { 268 private resolvePathsIntoStreams(paths: string[]) {
286 return Promise.all(paths.map(async path => { 269 return Promise.all(paths.map(async path => {
287 let stream = LogDog.StreamPath.splitProject(path); 270 let stream = LogDog.StreamPath.splitProject(path);
288 if (!LogDog.isQuery(stream.path)) { 271 if (!LogDog.isQuery(stream.path)) {
289 return [stream]; 272 return [stream];
290 } 273 }
291 274
292 // This "path" is really a query. Construct and execute. 275 // This "path" is really a query. Construct and execute.
(...skipping 26 matching lines...) Expand all
319 * Resets the state. 302 * Resets the state.
320 */ 303 */
321 reset() { 304 reset() {
322 this.view.clearLogEntries(); 305 this.view.clearLogEntries();
323 this.clearCurrentOperation(); 306 this.clearCurrentOperation();
324 this.provider = this.nullProvider(); 307 this.provider = this.nullProvider();
325 308
326 this.updateControls(); 309 this.updateControls();
327 } 310 }
328 311
312 public get automatic() {
313 return this.isAutomatic;
314 }
315
316 /**
317 * Sets whether automatic loading is enabled.
318 *
319 * When enabled, a new fetch will immediately be dispatched when a previous
320 * fetch finishes so long as there is still stream data to load.
321 *
322 * This isn't a setter because we need to be able to export it via
323 * interface.
324 */
325 public set automatic(v: boolean) {
326 this.isAutomatic = v && !this.fetchedFullStream;
327 if (v) {
328 // Passively kick off a new fetch.
329 this.fetch(false);
330 }
331 this.updateControls();
332 }
333
329 private nullProvider(): LogProvider { 334 private nullProvider(): LogProvider {
330 return new AggregateLogStream([]); 335 return new AggregateLogStream([]);
331 } 336 }
332 337
333 /** 338 /**
334 * Clears the current operation, cancelling it if set. If the operation is 339 * Clears the current operation, cancelling it if set. If the operation is
335 * cleared, the current fetch and rendering states will be reset. 340 * cleared, the current fetch and rendering states will be reset.
336 * 341 *
337 * @param op if provided, only cancel the current operation if it equals 342 * @param op if provided, only cancel the current operation if it equals
338 * the supplied "op". If "op" does not match the current operation, it 343 * the supplied "op". If "op" does not match the current operation, it
339 * will be cancelled, but the current operation will be left in-tact. 344 * will be cancelled, but the current operation will be left in-tact.
340 * If "op" is undefined, cancel the current operation regardless. 345 * If "op" is undefined, cancel the current operation regardless.
341 */ 346 */
342 private clearCurrentOperation(op?: luci.Operation) { 347 clearCurrentOperation(op?: luci.Operation) {
343 if (this.currentOperation) { 348 if (this.currentOperation) {
344 if (op && op !== this.currentOperation) { 349 if (op && op !== this.currentOperation) {
345 // Conditional clear, and we are not the current operation, so do 350 // Conditional clear, and we are not the current operation, so do
346 // nothing. 351 // nothing.
347 op.cancel(); 352 op.cancel();
348 return; 353 return;
349 } 354 }
350 this.currentOperation.cancel(); 355 this.currentOperation.cancel();
351 this.currentOperation = this.currentFetchPromise = null; 356 this.currentOperation = this.currentFetchPromise = null;
352 } 357 }
(...skipping 12 matching lines...) Expand all
365 370
366 private get streamStatus(): StreamStatusEntry[] { 371 private get streamStatus(): StreamStatusEntry[] {
367 return this.streamStatusValue; 372 return this.streamStatusValue;
368 } 373 }
369 private set streamStatus(st: StreamStatusEntry[]) { 374 private set streamStatus(st: StreamStatusEntry[]) {
370 this.streamStatusValue = st; 375 this.streamStatusValue = st;
371 this.updateControls(); 376 this.updateControls();
372 } 377 }
373 378
374 private updateControls() { 379 private updateControls() {
380 let splitState: SplitState;
381 if (this.providerCanSplit) {
382 splitState = SplitState.CAN_SPLIT;
383 } else {
384 splitState =
385 (this.isSplit) ? (SplitState.IS_SPLIT) : (SplitState.CANNOT_SPLIT);
386 }
387
375 this.view.updateControls({ 388 this.view.updateControls({
376 canSplit: this.providerCanSplit, 389 splitState: splitState,
377 split: this.isSplit,
378 bottom: !this.fetchedEndOfStream,
379 fullyLoaded: (this.fetchedFullStream && (!this.rendering)), 390 fullyLoaded: (this.fetchedFullStream && (!this.rendering)),
380 logStreamUrl: this.logStreamUrl, 391 logStreamUrl: this.logStreamUrl,
381 loadingState: this.loadingState, 392 loadingState: this.loadingState,
382 streamStatus: this.streamStatus, 393 streamStatus: this.streamStatus,
383 }); 394 });
384 } 395 }
385 396
386 /** 397 /**
387 * Note that the authentication state for the client has changed. This will 398 * Note that the authentication state for the client has changed. This will
388 * trigger an automatic fetch retry if our previous fetch failed due to 399 * trigger an automatic fetch retry if our previous fetch failed due to
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
456 /** Fetch logs from an explicit location. */ 467 /** Fetch logs from an explicit location. */
457 async fetchLocation(l: Location, cancel: boolean): Promise<void> { 468 async fetchLocation(l: Location, cancel: boolean): Promise<void> {
458 if (this.currentFetchPromise && (!cancel)) { 469 if (this.currentFetchPromise && (!cancel)) {
459 return this.currentFetchPromise; 470 return this.currentFetchPromise;
460 } 471 }
461 this.clearCurrentOperation(); 472 this.clearCurrentOperation();
462 473
463 // If our provider is finished, then do nothing. 474 // If our provider is finished, then do nothing.
464 if (this.fetchedFullStream) { 475 if (this.fetchedFullStream) {
465 // There are no more logs. 476 // There are no more logs.
477 this.updateControls();
466 return undefined; 478 return undefined;
467 } 479 }
468 480
469 // If we're asked to fetch BOTTOM, but we're not split, fetch HEAD 481 // If we're asked to fetch BOTTOM, but we're not split, fetch HEAD
470 // instead. 482 // instead.
471 if (l === Location.BOTTOM && !this.isSplit) { 483 if (l === Location.BOTTOM && !this.isSplit) {
472 l = Location.HEAD; 484 l = Location.HEAD;
473 } 485 }
474 486
475 // Rotate our fetch ID. This will effectively cancel any pending fetches. 487 // Rotate our fetch ID. This will effectively cancel any pending fetches.
(...skipping 19 matching lines...) Expand all
495 hasLogs = await this.fetchLocationRound(l, op); 507 hasLogs = await this.fetchLocationRound(l, op);
496 } catch (err) { 508 } catch (err) {
497 console.log('Fetch failed with error:', err); 509 console.log('Fetch failed with error:', err);
498 510
499 // Cancel the timer here, since we may enter other states in this 511 // Cancel the timer here, since we may enter other states in this
500 // "catch" block and we don't want to have the timer override them. 512 // "catch" block and we don't want to have the timer override them.
501 loadingWhileTimer.cancel(); 513 loadingWhileTimer.cancel();
502 514
503 // If we've been canceled, discard this result. 515 // If we've been canceled, discard this result.
504 if (err === luci.Operation.CANCELLED) { 516 if (err === luci.Operation.CANCELLED) {
517 this.setIdleLoadingState();
505 return; 518 return;
506 } 519 }
507 520
508 if (err === NOT_AUTHENTICATED) { 521 if (err === NOT_AUTHENTICATED) {
509 this.loadingState = LoadingState.NEEDS_AUTH; 522 this.loadingState = LoadingState.NEEDS_AUTH;
510 523
511 // We failed because we were not authenticated. Mark this 524 // We failed because we were not authenticated. Mark this
512 // so we can retry if that state changes. 525 // so we can retry if that state changes.
513 await this.authChangedPromise; 526 await this.authChangedPromise;
514 527
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 } 563 }
551 564
552 // Clear our loading state (updates controls automatically). 565 // Clear our loading state (updates controls automatically).
553 this.loadingState = LoadingState.RENDERING; 566 this.loadingState = LoadingState.RENDERING;
554 567
555 // Initiate the next render. This will happen in the 568 // Initiate the next render. This will happen in the
556 // background while we enqueue our next fetch. 569 // background while we enqueue our next fetch.
557 let doRender = async () => { 570 let doRender = async () => {
558 await this.renderLogs(buf, l); 571 await this.renderLogs(buf, l);
559 if (this.loadingState === LoadingState.RENDERING) { 572 if (this.loadingState === LoadingState.RENDERING) {
560 this.loadingState = LoadingState.NONE; 573 this.setIdleLoadingState();
561 } 574 }
562 }; 575 };
563 this.renderPromise = doRender(); 576 this.renderPromise = doRender();
564 577
565 if (this.fetchedFullStream) { 578 if (this.fetchedFullStream) {
566 return false; 579 return false;
567 } 580 }
568 return hasLogs; 581 return hasLogs;
569 } 582 }
570 583
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
621 /** 634 /**
622 * Sets whether the next fetch will pull from the tail (end) or the top 635 * Sets whether the next fetch will pull from the tail (end) or the top
623 * (begining) region. 636 * (begining) region.
624 * 637 *
625 * This is only relevant when there is a log split. 638 * This is only relevant when there is a log split.
626 */ 639 */
627 setFetchFromTail(v: boolean) { 640 setFetchFromTail(v: boolean) {
628 this.fetchFromTail = v; 641 this.fetchFromTail = v;
629 } 642 }
630 643
631 /**
632 * Sets whether automatic loading is enabled.
633 *
634 * When enabled, a new fetch will immediately be dispatched when a previous
635 * fetch finishes so long as there is still stream data to load.
636 */
637 setAutomatic(v: boolean) {
638 this.automatic = v;
639 if (v) {
640 // Passively kick off a new fetch.
641 this.fetch(false);
642 }
643 }
644
645 private buildStreamStatus(v: LogStreamStatus[]): StreamStatusEntry[] { 644 private buildStreamStatus(v: LogStreamStatus[]): StreamStatusEntry[] {
646 let maxStatus = LogDog.FetchStatus.IDLE; 645 let maxStatus = LogDog.FetchStatus.IDLE;
647 let maxStatusCount = 0; 646 let maxStatusCount = 0;
648 let needsAuth = false; 647 let needsAuth = false;
649 648
650 // Prune any finished entries and accumulate them for status bar change. 649 // Prune any finished entries and accumulate them for status bar change.
651 v = (v || []).filter((st) => { 650 v = (v || []).filter((st) => {
652 needsAuth = (needsAuth || st.needsAuth); 651 needsAuth = (needsAuth || st.needsAuth);
653 652
654 if (st.fetchStatus > maxStatus) { 653 if (st.fetchStatus > maxStatus) {
(...skipping 789 matching lines...) Expand 10 before | Expand all | Expand 10 after
1444 1443
1445 // Get the next log and increment our index. 1444 // Get the next log and increment our index.
1446 let log = this.logs[this.index++]; 1445 let log = this.logs[this.index++];
1447 if (this.index >= this.logs.length) { 1446 if (this.index >= this.logs.length) {
1448 this.logs = null; 1447 this.logs = null;
1449 } 1448 }
1450 return log; 1449 return log;
1451 } 1450 }
1452 } 1451 }
1453 } 1452 }
OLDNEW
« no previous file with comments | « web/inc/logdog-stream-view/logdog-stream-view.html ('k') | web/inc/logdog-stream-view/view.ts » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698