Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package annotee | 5 package annotee |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bufio" | 8 "bufio" |
| 9 "bytes" | 9 "bytes" |
| 10 "fmt" | 10 "fmt" |
| (...skipping 269 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 280 | 280 |
| 281 h, err := p.getStepHandler(step, true) | 281 h, err := p.getStepHandler(step, true) |
| 282 if err != nil { | 282 if err != nil { |
| 283 return err | 283 return err |
| 284 } | 284 } |
| 285 | 285 |
| 286 // Determine our injected annotations. | 286 // Determine our injected annotations. |
| 287 injectedAnnotations := h.flushInjectedAnnotations() | 287 injectedAnnotations := h.flushInjectedAnnotations() |
| 288 | 288 |
| 289 // Emit the "all" link if configured (at most once). | 289 // Emit the "all" link if configured (at most once). |
| 290 » if s.EmitAllLink { | 290 » if lg := p.o.LinkGenerator; lg != nil && s.EmitAllLink { |
| 291 » » if l := buildStreamLinkAnnotation(p.o.LinkGenerator, "all", "std io", "**/stdout", "**/stderr"); l != "" { | 291 » » injectedAnnotations = append(injectedAnnotations, |
| 292 » » » injectedAnnotations = append(injectedAnnotations, l) | 292 » » » buildAliasAnnotation("all", "stdio", lg.GetLink("**/stdo ut", "**/stderr"))) |
| 293 » » } | 293 |
| 294 s.EmitAllLink = false | 294 s.EmitAllLink = false |
| 295 } | 295 } |
| 296 | 296 |
| 297 // Get our root log stream handler. As an optimization, if "step" is | 297 // Get our root log stream handler. As an optimization, if "step" is |
| 298 // the root step, then "h" is already the root handler, so we don't need | 298 // the root step, then "h" is already the root handler, so we don't need |
| 299 // to duplicate the lookup. | 299 // to duplicate the lookup. |
| 300 // | 300 // |
| 301 // We only need the handler if we're going to emit annotations to the ro ot | 301 // We only need the handler if we're going to emit annotations to the ro ot |
| 302 // stream. | 302 // stream. |
| 303 var rootHandler *stepHandler | 303 var rootHandler *stepHandler |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 349 if !s.StripAnnotations { | 349 if !s.StripAnnotations { |
| 350 if err := rootHandler.writeBaseStream(s, anno); err != n il { | 350 if err := rootHandler.writeBaseStream(s, anno); err != n il { |
| 351 log.WithError(err).Errorf(h, "Failed to send inj ected annotation line to LogDog.") | 351 log.WithError(err).Errorf(h, "Failed to send inj ected annotation line to LogDog.") |
| 352 return err | 352 return err |
| 353 } | 353 } |
| 354 } | 354 } |
| 355 } | 355 } |
| 356 | 356 |
| 357 // If we're stripping text, write a warning message noting that this str eam | 357 // If we're stripping text, write a warning message noting that this str eam |
| 358 // will not have text in it. | 358 // will not have text in it. |
| 359 » if !p.o.TeeText && s.Tee != nil && !h.textStrippedNote { | 359 » if !p.o.TeeText && s.Tee != nil { |
| 360 » » err := writeTextLine(s.Tee, "This build is configured to send lo g data exclusively to LogDog. "+ | 360 » » if !h.textStrippedNote { |
| 361 » » » "Please click the LogDog link on the build page to view this log stream.") | 361 » » » err := writeTextLine(s.Tee, "This build is configured to send log data exclusively to LogDog. "+ |
| 362 » » if err != nil { | 362 » » » » "Please use the LogDog link on the build page to view this log stream.") |
|
hinoka
2017/03/27 22:21:05
This seems like a better place to inject the viewe
dnj
2017/03/27 22:24:14
We can't inject here, since the links aren't known
| |
| 363 » » » log.WithError(err).Errorf(h, "Failed to write text strip ped notice.") | 363 » » » if err != nil { |
| 364 » » » return err | 364 » » » » log.WithError(err).Errorf(h, "Failed to write te xt stripped notice.") |
| 365 » » » » return err | |
| 366 » » » } | |
| 367 | |
| 368 » » » h.textStrippedNote = true | |
| 365 } | 369 } |
| 366 | 370 |
| 367 » » h.textStrippedNote = true | 371 » » // Add links to specific log streams as they are generated. |
| 372 » » injectTextStreamLines := h.flushInjectedTextStreamLines() | |
| 373 » » if len(injectTextStreamLines) > 0 { | |
|
hinoka
2017/03/27 22:21:05
the if statement is redundant.
dnj
2017/03/27 22:24:13
Done.
| |
| 374 » » » for _, line := range injectTextStreamLines { | |
| 375 » » » » if err := writeTextLine(s.Tee, line); err != nil { | |
| 376 » » » » » log.WithError(err).Errorf(h, "Failed to inject text stream line: %s", line) | |
| 377 » » » » » return err | |
| 378 » » » » } | |
| 379 » » » } | |
| 380 » » } | |
| 368 } | 381 } |
| 369 | 382 |
| 370 // If this is a text line, and we're teeing text, emit this line. | 383 // If this is a text line, and we're teeing text, emit this line. |
| 371 if a == "" { | 384 if a == "" { |
| 372 if s.Tee != nil && p.o.TeeText { | 385 if s.Tee != nil && p.o.TeeText { |
| 373 if err := writeTextLine(s.Tee, line); err != nil { | 386 if err := writeTextLine(s.Tee, line); err != nil { |
| 374 log.WithError(err).Errorf(h, "Failed to tee text line.") | 387 log.WithError(err).Errorf(h, "Failed to tee text line.") |
| 375 return err | 388 return err |
| 376 } | 389 } |
| 377 } | 390 } |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 569 } | 582 } |
| 570 } | 583 } |
| 571 | 584 |
| 572 // stepHandler handles the steps associated with a specified stream. | 585 // stepHandler handles the steps associated with a specified stream. |
| 573 type stepHandler struct { | 586 type stepHandler struct { |
| 574 context.Context | 587 context.Context |
| 575 | 588 |
| 576 processor *Processor | 589 processor *Processor |
| 577 step *annotation.Step | 590 step *annotation.Step |
| 578 | 591 |
| 579 » client streamclient.Client | 592 » client streamclient.Client |
| 580 » injectedAnnotations []string | 593 » injectedAnnotations []string |
| 581 » streams map[types.StreamName]streamclient.Stream | 594 » injectedTextStreamLines []string |
| 582 » finished bool | 595 » streams map[types.StreamName]streamclient.Stream |
| 583 » allEmitted bool | 596 » finished bool |
| 584 » textStrippedNote bool | 597 » allEmitted bool |
| 598 » textStrippedNote bool | |
| 585 } | 599 } |
| 586 | 600 |
| 587 func newStepHandler(p *Processor, step *annotation.Step) (*stepHandler, error) { | 601 func newStepHandler(p *Processor, step *annotation.Step) (*stepHandler, error) { |
| 588 h := stepHandler{ | 602 h := stepHandler{ |
| 589 Context: log.SetField(p.ctx, "step", step), | 603 Context: log.SetField(p.ctx, "step", step), |
| 590 processor: p, | 604 processor: p, |
| 591 step: step, | 605 step: step, |
| 592 | 606 |
| 593 client: p.o.Client, | 607 client: p.o.Client, |
| 594 streams: make(map[types.StreamName]streamclient.Stream), | 608 streams: make(map[types.StreamName]streamclient.Stream), |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 690 | 704 |
| 691 func (h *stepHandler) closeStreamImpl(name types.StreamName, s streamclient.Stre am) { | 705 func (h *stepHandler) closeStreamImpl(name types.StreamName, s streamclient.Stre am) { |
| 692 if err := s.Close(); err != nil { | 706 if err := s.Close(); err != nil { |
| 693 log.Fields{ | 707 log.Fields{ |
| 694 log.ErrorKey: err, | 708 log.ErrorKey: err, |
| 695 "stream": name, | 709 "stream": name, |
| 696 }.Errorf(h, "Failed to close step stream.") | 710 }.Errorf(h, "Failed to close step stream.") |
| 697 } | 711 } |
| 698 } | 712 } |
| 699 | 713 |
| 700 func (h *stepHandler) flushInjectedAnnotations() []string { | 714 func (h *stepHandler) flushInjectedAnnotations() []string { |
|
hinoka
2017/03/27 22:21:05
This method and the one below looks like they coul
dnj
2017/03/27 22:24:14
The method up there is huge enough. I'd like to ke
| |
| 701 » if len(h.injectedAnnotations) == 0 { | 715 » return flushStringSlice(&h.injectedAnnotations) |
| 716 } | |
| 717 | |
| 718 func (h *stepHandler) flushInjectedTextStreamLines() []string { | |
| 719 » return flushStringSlice(&h.injectedTextStreamLines) | |
| 720 } | |
| 721 | |
| 722 func (h *stepHandler) maybeInjectLink(base, text string, names ...types.StreamNa me) { | |
| 723 » if lg := h.processor.o.LinkGenerator; lg != nil { | |
| 724 » » link := lg.GetLink(names...) | |
| 725 | |
| 726 » » h.injectedAnnotations = append(h.injectedAnnotations, buildAlias Annotation(base, text, link)) | |
| 727 » » h.injectedTextStreamLines = append(h.injectedTextStreamLines, fm t.Sprintf("LogDog Link [%s]: %s", base, link)) | |
| 728 » } | |
| 729 } | |
| 730 | |
| 731 func (h *stepHandler) maybeInjectTextStreamLink(name string, stream types.Stream Name) { | |
| 732 » if lg := h.processor.o.LinkGenerator; lg != nil { | |
| 733 » } | |
| 734 } | |
| 735 | |
| 736 func buildAliasAnnotation(base, text, link string) string { | |
| 737 » return buildAnnotation("STEP_LINK", fmt.Sprintf("%s-->%s", text, base), link) | |
| 738 } | |
| 739 | |
| 740 func flushStringSlice(sp *[]string) []string { | |
| 741 » if sp == nil { | |
| 702 return nil | 742 return nil |
| 703 } | 743 } |
| 704 | 744 |
| 705 » lines := make([]string, len(h.injectedAnnotations)) | 745 » s := *sp |
| 706 » copy(lines, h.injectedAnnotations) | 746 » if len(s) == 0 { |
| 707 » h.injectedAnnotations = h.injectedAnnotations[:0] | 747 » » return nil |
| 748 » } | |
| 749 | |
| 750 » lines := make([]string, len(s)) | |
| 751 » copy(lines, s) | |
| 752 » *sp = s[:0] | |
| 708 | 753 |
| 709 return lines | 754 return lines |
| 710 } | 755 } |
| 711 | 756 |
| 712 func (h *stepHandler) maybeInjectLink(base, text string, names ...types.StreamNa me) { | |
| 713 if l := buildStreamLinkAnnotation(h.processor.o.LinkGenerator, base, tex t, names...); l != "" { | |
| 714 h.injectedAnnotations = append(h.injectedAnnotations, l) | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 func buildStreamLinkAnnotation(lg LinkGenerator, base, text string, names ...typ es.StreamName) string { | |
| 719 if lg != nil { | |
| 720 if link := lg.GetLink(names...); link != "" { | |
| 721 return buildAnnotation("STEP_LINK", fmt.Sprintf("%s-->%s ", text, base), link) | |
| 722 } | |
| 723 } | |
| 724 return "" | |
| 725 } | |
| 726 | |
| 727 // lineReader reads from an input stream and returns the data line-by-line. | 757 // lineReader reads from an input stream and returns the data line-by-line. |
| 728 // | 758 // |
| 729 // We don't use a Scanner because we want to be able to handle lines that may | 759 // We don't use a Scanner because we want to be able to handle lines that may |
| 730 // exceed the buffer length. We don't use ReadBytes here because we need to | 760 // exceed the buffer length. We don't use ReadBytes here because we need to |
| 731 // capture the last line in the stream, even if it doesn't end with a newline. | 761 // capture the last line in the stream, even if it doesn't end with a newline. |
| 732 type lineReader struct { | 762 type lineReader struct { |
| 733 r *bufio.Reader | 763 r *bufio.Reader |
| 734 buf bytes.Buffer | 764 buf bytes.Buffer |
| 735 } | 765 } |
| 736 | 766 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 774 return "@@@" + strings.Join(append(v, params...), "@") + "@@@" | 804 return "@@@" + strings.Join(append(v, params...), "@") + "@@@" |
| 775 } | 805 } |
| 776 | 806 |
| 777 func isContentAnnotation(a string) bool { | 807 func isContentAnnotation(a string) bool { |
| 778 // Strip out any annotation arguments. | 808 // Strip out any annotation arguments. |
| 779 if idx := strings.IndexRune(a, '@'); idx > 0 { | 809 if idx := strings.IndexRune(a, '@'); idx > 0 { |
| 780 a = a[:idx] | 810 a = a[:idx] |
| 781 } | 811 } |
| 782 return a == "STEP_LOG_LINE" | 812 return a == "STEP_LOG_LINE" |
| 783 } | 813 } |
| OLD | NEW |