package engine import ( "bytes" "fmt" "sync" "github.com/pulumi/pulumi/pkg/diag" "github.com/golang/glog" "github.com/pulumi/pulumi/pkg/diag/colors" "github.com/pulumi/pulumi/pkg/util/contract" ) func newEventSink(events eventEmitter) diag.Sink { return &eventSink{ events: events, counts: make(map[diag.Severity]int), } } // eventSink is a sink which writes all events to a channel type eventSink struct { events eventEmitter // the channel to emit events into. counts map[diag.Severity]int // the number of messages that have been issued per severity. mutex sync.RWMutex // a mutex for guarding updates to the counts map } func (s *eventSink) Count() int { return s.Debugs() + s.Infos() + s.Errors() + s.Warnings() } func (s *eventSink) Debugs() int { return s.getCount(diag.Debug) } func (s *eventSink) Infos() int { return s.getCount(diag.Info) } func (s *eventSink) Infoerrs() int { return s.getCount(diag.Infoerr) } func (s *eventSink) Errors() int { return s.getCount(diag.Error) } func (s *eventSink) Warnings() int { return s.getCount(diag.Warning) } func (s *eventSink) Success() bool { return s.Errors() == 0 } func (s *eventSink) Logf(sev diag.Severity, d *diag.Diag, args ...interface{}) { switch sev { case diag.Debug: s.Debugf(d, args...) case diag.Info: s.Infof(d, args...) case diag.Infoerr: s.Infoerrf(d, args...) case diag.Warning: s.Warningf(d, args...) case diag.Error: s.Errorf(d, args...) default: contract.Failf("Unrecognized severity: %v", sev) } } func (s *eventSink) Debugf(d *diag.Diag, args ...interface{}) { // For debug messages, write both to the glogger and a stream, if there is one. glog.V(3).Infof(d.Message, args...) msg := s.Stringify(diag.Debug, d, args...) if glog.V(9) { glog.V(9).Infof("eventSink::Debug(%v)", msg[:len(msg)-1]) } s.events.diagDebugEvent(d.URN, msg) s.incrementCount(diag.Debug) } func (s *eventSink) Infof(d *diag.Diag, args ...interface{}) { msg := s.Stringify(diag.Info, d, args...) if glog.V(5) { glog.V(5).Infof("eventSink::Info(%v)", msg[:len(msg)-1]) } s.events.diagInfoEvent(d.URN, msg) s.incrementCount(diag.Info) } func (s *eventSink) Infoerrf(d *diag.Diag, args ...interface{}) { msg := s.Stringify(diag.Info /* not Infoerr, just "info: "*/, d, args...) if glog.V(5) { glog.V(5).Infof("eventSink::Infoerr(%v)", msg[:len(msg)-1]) } s.events.diagInfoerrEvent(d.URN, msg) s.incrementCount(diag.Infoerr) } func (s *eventSink) Errorf(d *diag.Diag, args ...interface{}) { msg := s.Stringify(diag.Error, d, args...) if glog.V(5) { glog.V(5).Infof("eventSink::Error(%v)", msg[:len(msg)-1]) } s.events.diagErrorEvent(d.URN, msg) s.incrementCount(diag.Error) } func (s *eventSink) Warningf(d *diag.Diag, args ...interface{}) { msg := s.Stringify(diag.Warning, d, args...) if glog.V(5) { glog.V(5).Infof("eventSink::Warning(%v)", msg[:len(msg)-1]) } s.events.diagWarningEvent(d.URN, msg) s.incrementCount(diag.Warning) } func (s *eventSink) incrementCount(sev diag.Severity) { s.mutex.Lock() defer s.mutex.Unlock() s.counts[sev]++ } func (s *eventSink) getCount(sev diag.Severity) int { s.mutex.RLock() defer s.mutex.RUnlock() return s.counts[sev] } func (s *eventSink) Stringify(sev diag.Severity, d *diag.Diag, args ...interface{}) string { var buffer bytes.Buffer // Now print the message category's prefix (error/warning). switch sev { case diag.Debug: buffer.WriteString(colors.SpecDebug) case diag.Info, diag.Infoerr: buffer.WriteString(colors.SpecInfo) case diag.Error: buffer.WriteString(colors.SpecError) case diag.Warning: buffer.WriteString(colors.SpecWarning) default: contract.Failf("Unrecognized diagnostic severity: %v", sev) } buffer.WriteString(string(sev)) buffer.WriteString(": ") buffer.WriteString(colors.Reset) // Finally, actually print the message itself. buffer.WriteString(colors.SpecNote) if d.Raw { buffer.WriteString(d.Message) } else { buffer.WriteString(fmt.Sprintf(d.Message, args...)) } buffer.WriteString(colors.Reset) buffer.WriteRune('\n') // TODO[pulumi/pulumi#15]: support Clang-style expressive diagnostics. This would entail, for example, using // the buffer within the target document, to demonstrate the offending line/column range of code. return buffer.String() }