1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 // </copyright>
4 // <summary>Sink which will take in a build event and raise it on its internal event source</summary>
5 //-----------------------------------------------------------------------
6 
7 using System;
8 using System.Collections.Generic;
9 using Microsoft.Build.Framework;
10 using Microsoft.Build.Shared;
11 
12 using InternalLoggerException = Microsoft.Build.Exceptions.InternalLoggerException;
13 
14 namespace Microsoft.Build.BackEnd.Logging
15 {
16     /// <summary>
17     /// This class raises events on behalf of the build engine to all registered loggers.
18     /// </summary>
19     internal sealed class EventSourceSink :
20 #if FEATURE_APPDOMAIN
21         MarshalByRefObject,
22 #endif
23         IEventSource2, IBuildEventSink
24     {
25         #region Events
26 
27         /// <summary>
28         /// This event is raised to log a message.
29         /// </summary>
30         public event BuildMessageEventHandler MessageRaised;
31 
32         /// <summary>
33         /// This event is raised to log an error.
34         /// </summary>
35         public event BuildErrorEventHandler ErrorRaised;
36 
37         /// <summary>
38         /// This event is raised to log a warning.
39         /// </summary>
40         public event BuildWarningEventHandler WarningRaised;
41 
42         /// <summary>
43         /// this event is raised to log the start of a build
44         /// </summary>
45         public event BuildStartedEventHandler BuildStarted;
46 
47         /// <summary>
48         /// this event is raised to log the end of a build
49         /// </summary>
50         public event BuildFinishedEventHandler BuildFinished;
51 
52         /// <summary>
53         /// this event is raised to log the start of a project build
54         /// </summary>
55         public event ProjectStartedEventHandler ProjectStarted;
56 
57         /// <summary>
58         /// this event is raised to log the end of a project build
59         /// </summary>
60         public event ProjectFinishedEventHandler ProjectFinished;
61 
62         /// <summary>
63         /// this event is raised to log the start of a target build
64         /// </summary>
65         public event TargetStartedEventHandler TargetStarted;
66 
67         /// <summary>
68         /// this event is raised to log the end of a target build
69         /// </summary>
70         public event TargetFinishedEventHandler TargetFinished;
71 
72         /// <summary>
73         /// this event is raised to log the start of task execution
74         /// </summary>
75         public event TaskStartedEventHandler TaskStarted;
76 
77         /// <summary>
78         /// this event is raised to log the end of task execution
79         /// </summary>
80         public event TaskFinishedEventHandler TaskFinished;
81 
82         /// <summary>
83         /// this event is raised to log a custom event
84         /// </summary>
85         public event CustomBuildEventHandler CustomEventRaised;
86 
87         /// <summary>
88         /// this event is raised to log build status events, such as
89         /// build/project/target/task started/stopped
90         /// </summary>
91         public event BuildStatusEventHandler StatusEventRaised;
92 
93         /// <summary>
94         /// This event is raised to log that some event has
95         /// occurred.  It is raised on every event.
96         /// </summary>
97         public event AnyEventHandler AnyEventRaised;
98 
99         /// <summary>
100         /// This event is raised to log telemetry.
101         /// </summary>
102         public event TelemetryEventHandler TelemetryLogged;
103         #endregion
104 
105         #region Properties
106         /// <summary>
107         /// Provide a friendly name for the sink to make it easier to differentiate during
108         /// debugging and display
109         /// </summary>
110         public string Name
111         {
112             get;
113             set;
114         }
115 
116         /// <summary>
117         /// Has the sink logged the BuildStartedEvent. This is important to know because we only want to log the build started event once
118         /// </summary>
119         public bool HaveLoggedBuildStartedEvent
120         {
121             get;
122             set;
123         }
124 
125         /// <summary>
126         /// Has the sink logged the BuildFinishedEvent. This is important to know because we only want to log the build finished event once
127         /// </summary>
128         public bool HaveLoggedBuildFinishedEvent
129         {
130             get;
131             set;
132         }
133 
134         /// <summary>
135         /// A list of warnings to treat as errors.  If null, nothing is treated as an error.  If an empty set, all warnings are treated as errors.
136         /// </summary>
137         public ISet<string> WarningsAsErrors
138         {
139             get;
140             set;
141         }
142 
143         /// <summary>
144         /// A list of warnings to treat as errors for an associated <see cref="BuildEventContext.ProjectInstanceId"/>.  If the set associated with a ProjectInstanceId is null, nothing is treated as an error.  If an empty set, all warnings are treated as errors.
145         /// </summary>
146         public IDictionary<int, ISet<string>> WarningsAsErrorsByProject
147         {
148             get;
149             set;
150         }
151 
152         /// <summary>
153         /// A list of warnings to treat as low importance messages.
154         /// </summary>
155         public ISet<string> WarningsAsMessages
156         {
157             get;
158             set;
159         }
160 
161         /// <summary>
162         /// A list of warnings to treat as low importance messages for an associated <see cref="BuildEventContext.ProjectInstanceId"/>.
163         /// </summary>
164         public IDictionary<int, ISet<string>> WarningsAsMessagesByProject
165         {
166             get;
167             set;
168         }
169 
170         /// <summary>
171         /// A list of build submission IDs that have logged errors.  If an error is logged outside of a submission, the submission ID is <see cref="BuildEventContext.InvalidSubmissionId"/>.
172         /// </summary>
173         public ISet<int> BuildSubmissionIdsThatHaveLoggedErrors
174         {
175             get;
176         } = new HashSet<int>();
177 
178         #endregion
179 
180         #region Methods
181 
182         #region IEventSink Methods
183 
184         /// <summary>
185         /// Raises the given event to all registered loggers. This method up-cast the events
186         /// extracted from the queue.
187         /// </summary>
188         /// <param name="buildEvent">BuildEventArgs</param>
189         /// <param name="sinkId">Note this is not used in the eventsource sink</param>
Consume(BuildEventArgs buildEvent, int sinkId)190         public void Consume(BuildEventArgs buildEvent, int sinkId)
191         {
192             Consume(buildEvent);
193         }
194 
195         /// <summary>
196         /// Raises the given event to all registered loggers. This method up-cast the events
197         /// extracted from the queue.
198         /// </summary>
Consume(BuildEventArgs buildEvent)199         public void Consume(BuildEventArgs buildEvent)
200         {
201             // FXCop may complain that there are unecessary casts here, and there are, but
202             // using "as" and allocating another variable for each event is extremely costly
203             // and is much slower then this approach even with the additional casts
204             if (buildEvent is BuildMessageEventArgs)
205             {
206                 this.RaiseMessageEvent(null, (BuildMessageEventArgs)buildEvent);
207             }
208             else if (buildEvent is TaskStartedEventArgs)
209             {
210                 this.RaiseTaskStartedEvent(null, (TaskStartedEventArgs)buildEvent);
211             }
212             else if (buildEvent is TaskFinishedEventArgs)
213             {
214                 this.RaiseTaskFinishedEvent(null, (TaskFinishedEventArgs)buildEvent);
215             }
216             else if (buildEvent is TargetStartedEventArgs)
217             {
218                 this.RaiseTargetStartedEvent(null, (TargetStartedEventArgs)buildEvent);
219             }
220             else if (buildEvent is TargetFinishedEventArgs)
221             {
222                 this.RaiseTargetFinishedEvent(null, (TargetFinishedEventArgs)buildEvent);
223             }
224             else if (buildEvent is ProjectStartedEventArgs)
225             {
226                 this.RaiseProjectStartedEvent(null, (ProjectStartedEventArgs)buildEvent);
227             }
228             else if (buildEvent is ProjectFinishedEventArgs)
229             {
230                 this.RaiseProjectFinishedEvent(null, (ProjectFinishedEventArgs)buildEvent);
231 
232                 if (buildEvent.BuildEventContext != null && buildEvent.BuildEventContext.ProjectInstanceId != BuildEventContext.InvalidProjectInstanceId)
233                 {
234                     WarningsAsErrorsByProject?.Remove(buildEvent.BuildEventContext.ProjectInstanceId);
235                     WarningsAsMessagesByProject?.Remove(buildEvent.BuildEventContext.ProjectInstanceId);
236                 }
237             }
238             else if (buildEvent is BuildStartedEventArgs)
239             {
240                 HaveLoggedBuildStartedEvent = true;
241                 this.RaiseBuildStartedEvent(null, (BuildStartedEventArgs)buildEvent);
242             }
243             else if (buildEvent is BuildFinishedEventArgs)
244             {
245                 HaveLoggedBuildFinishedEvent = true;
246                 this.RaiseBuildFinishedEvent(null, (BuildFinishedEventArgs)buildEvent);
247             }
248             else if (buildEvent is CustomBuildEventArgs)
249             {
250                 this.RaiseCustomEvent(null, (CustomBuildEventArgs)buildEvent);
251             }
252             else if (buildEvent is BuildStatusEventArgs)
253             {
254                 this.RaiseStatusEvent(null, (BuildStatusEventArgs)buildEvent);
255             }
256             else if (buildEvent is BuildWarningEventArgs)
257             {
258                 BuildWarningEventArgs warningEvent = (BuildWarningEventArgs) buildEvent;
259 
260                 if (ShouldTreatWarningAsMessage(warningEvent))
261                 {
262                     // Treat this warning as a message with low importance if its in the list
263                     BuildMessageEventArgs errorEvent = new BuildMessageEventArgs(
264                         warningEvent.Subcategory,
265                         warningEvent.Code,
266                         warningEvent.File,
267                         warningEvent.LineNumber,
268                         warningEvent.ColumnNumber,
269                         warningEvent.EndLineNumber,
270                         warningEvent.EndColumnNumber,
271                         warningEvent.Message,
272                         warningEvent.HelpKeyword,
273                         warningEvent.SenderName,
274                         MessageImportance.Low,
275                         warningEvent.Timestamp)
276                     {
277                         BuildEventContext = warningEvent.BuildEventContext,
278                         ProjectFile = warningEvent.ProjectFile,
279                     };
280 
281                     this.RaiseMessageEvent(null, errorEvent);
282 
283                 }
284                 else if (ShouldTreatWarningAsError(warningEvent))
285                 {
286                     // Treat this warning as an error if an empty set of warnings was specified or this code was specified
287                     BuildErrorEventArgs errorEvent = new BuildErrorEventArgs(
288                         warningEvent.Subcategory,
289                         warningEvent.Code,
290                         warningEvent.File,
291                         warningEvent.LineNumber,
292                         warningEvent.ColumnNumber,
293                         warningEvent.EndLineNumber,
294                         warningEvent.EndColumnNumber,
295                         warningEvent.Message,
296                         warningEvent.HelpKeyword,
297                         warningEvent.SenderName,
298                         warningEvent.Timestamp)
299                     {
300                         BuildEventContext = warningEvent.BuildEventContext,
301                         ProjectFile = warningEvent.ProjectFile,
302                     };
303 
304                     this.RaiseErrorEvent(null, errorEvent);
305                 }
306                 else
307                 {
308                     this.RaiseWarningEvent(null, warningEvent);
309                 }
310             }
311             else if (buildEvent is BuildErrorEventArgs)
312             {
313                 this.RaiseErrorEvent(null, (BuildErrorEventArgs)buildEvent);
314             }
315             else if (buildEvent is TelemetryEventArgs)
316             {
317                 this.RaiseTelemetryEvent(null, (TelemetryEventArgs) buildEvent);
318             }
319             else
320             {
321                 ErrorUtilities.VerifyThrow(false, "Unknown event args type.");
322             }
323         }
324 
325         /// <summary>
326         /// Shutdown and displose of any resource this object is holding onto.
327         /// </summary>
ShutDown()328         public void ShutDown()
329         {
330             this.UnregisterAllEventHandlers();
331         }
332         #endregion
333 
334         #region Internal Methods
335 
336         /// <summary>
337         /// Clears out all events.
338         /// </summary>
UnregisterAllEventHandlers()339         internal void UnregisterAllEventHandlers()
340         {
341             MessageRaised = null;
342             ErrorRaised = null;
343             WarningRaised = null;
344             BuildStarted = null;
345             BuildFinished = null;
346             ProjectStarted = null;
347             ProjectFinished = null;
348             TargetStarted = null;
349             TargetFinished = null;
350             TaskStarted = null;
351             TaskFinished = null;
352             CustomEventRaised = null;
353             StatusEventRaised = null;
354             AnyEventRaised = null;
355             TelemetryLogged = null;
356         }
357 
358         #endregion
359 
360         #region Private Methods
361 
362         /// <summary>
363         /// Raises a message event to all registered loggers.
364         /// </summary>
365         /// <param name="sender">sender of the event</param>
366         /// <param name="buildEvent">BuildMessageEventArgs</param>
367         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
368         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
369         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseMessageEvent(object sender, BuildMessageEventArgs buildEvent)370         private void RaiseMessageEvent(object sender, BuildMessageEventArgs buildEvent)
371         {
372             if (MessageRaised != null)
373             {
374                 try
375                 {
376                     MessageRaised(sender, buildEvent);
377                 }
378                 catch (LoggerException)
379                 {
380                     // if a logger has failed politely, abort immediately
381                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
382                     // if a fellow logger is throwing in an event handler.
383                     this.UnregisterAllEventHandlers();
384                     throw;
385                 }
386                 catch (Exception exception)
387                 {
388                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
389                     // if a fellow logger is throwing in an event handler.
390                     this.UnregisterAllEventHandlers();
391 
392                     if (ExceptionHandling.IsCriticalException(exception))
393                     {
394                         throw;
395                     }
396 
397                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
398                 }
399             }
400 
401             RaiseAnyEvent(sender, buildEvent);
402         }
403 
404         /// <summary>
405         /// Raises an error event to all registered loggers.
406         /// </summary>
407         /// <param name="sender">sender of the event</param>
408         /// <param name="buildEvent">BuildErrorEventArgs</param>
409         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
410         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
411         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseErrorEvent(object sender, BuildErrorEventArgs buildEvent)412         private void RaiseErrorEvent(object sender, BuildErrorEventArgs buildEvent)
413         {
414             // Keep track of build submissions that have logged errors.  If there is no build context, add BuildEventContext.InvalidSubmissionId.
415             BuildSubmissionIdsThatHaveLoggedErrors.Add(buildEvent?.BuildEventContext?.SubmissionId ?? BuildEventContext.InvalidSubmissionId);
416 
417             if (ErrorRaised != null)
418             {
419                 try
420                 {
421                     ErrorRaised(sender, buildEvent);
422                 }
423                 catch (LoggerException)
424                 {
425                     // if a logger has failed politely, abort immediately
426                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
427                     // if a fellow logger is throwing in an event handler.
428                     this.UnregisterAllEventHandlers();
429                     throw;
430                 }
431                 catch (Exception exception)
432                 {
433                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
434                     // if a fellow logger is throwing in an event handler.
435                     this.UnregisterAllEventHandlers();
436 
437                     if (ExceptionHandling.IsCriticalException(exception))
438                     {
439                         throw;
440                     }
441 
442                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
443                 }
444             }
445 
446             RaiseAnyEvent(sender, buildEvent);
447         }
448 
449         /// <summary>
450         /// Raises a warning event to all registered loggers.
451         /// </summary>
452         /// <param name="sender">sender of the event</param>
453         /// <param name="buildEvent">BuildWarningEventArgs</param>
454         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
455         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
456         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseWarningEvent(object sender, BuildWarningEventArgs buildEvent)457         private void RaiseWarningEvent(object sender, BuildWarningEventArgs buildEvent)
458         {
459             if (WarningRaised != null)
460             {
461                 try
462                 {
463                     WarningRaised(sender, buildEvent);
464                 }
465                 catch (LoggerException)
466                 {
467                     // if a logger has failed politely, abort immediately
468                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
469                     // if a fellow logger is throwing in an event handler.
470                     this.UnregisterAllEventHandlers();
471                     throw;
472                 }
473                 catch (Exception exception)
474                 {
475                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
476                     // if a fellow logger is throwing in an event handler.
477                     this.UnregisterAllEventHandlers();
478 
479                     if (ExceptionHandling.IsCriticalException(exception))
480                     {
481                         throw;
482                     }
483 
484                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
485                 }
486             }
487 
488             RaiseAnyEvent(sender, buildEvent);
489         }
490 
491         /// <summary>
492         /// Raises a "build started" event to all registered loggers.
493         /// </summary>
494         /// <param name="sender">sender of the event</param>
495         /// <param name="buildEvent">BuildStartedEventArgs</param>
496         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
497         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
498         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseBuildStartedEvent(object sender, BuildStartedEventArgs buildEvent)499         private void RaiseBuildStartedEvent(object sender, BuildStartedEventArgs buildEvent)
500         {
501             if (BuildStarted != null)
502             {
503                 try
504                 {
505                     BuildStarted(sender, buildEvent);
506                 }
507                 catch (LoggerException)
508                 {
509                     // if a logger has failed politely, abort immediately
510                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
511                     // if a fellow logger is throwing in an event handler.
512                     this.UnregisterAllEventHandlers();
513                     throw;
514                 }
515                 catch (Exception exception)
516                 {
517                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
518                     // if a fellow logger is throwing in an event handler.
519                     this.UnregisterAllEventHandlers();
520 
521                     if (ExceptionHandling.IsCriticalException(exception))
522                     {
523                         throw;
524                     }
525 
526                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
527                 }
528             }
529 
530             RaiseStatusEvent(sender, buildEvent);
531         }
532 
533         /// <summary>
534         /// Raises a "build finished" event to all registered loggers.
535         /// </summary>
536         /// <param name="sender">sender of the event</param>
537         /// <param name="buildEvent">BuildFinishedEventArgs</param>
538         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
539         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
540         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseBuildFinishedEvent(object sender, BuildFinishedEventArgs buildEvent)541         private void RaiseBuildFinishedEvent(object sender, BuildFinishedEventArgs buildEvent)
542         {
543             if (BuildFinished != null)
544             {
545                 try
546                 {
547                     BuildFinished(sender, buildEvent);
548                 }
549                 catch (LoggerException)
550                 {
551                     // if a logger has failed politely, abort immediately
552                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
553                     // if a fellow logger is throwing in an event handler.
554                     this.UnregisterAllEventHandlers();
555                     throw;
556                 }
557                 catch (Exception exception)
558                 {
559                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
560                     // if a fellow logger is throwing in an event handler.
561                     this.UnregisterAllEventHandlers();
562 
563                     if (ExceptionHandling.IsCriticalException(exception))
564                     {
565                         throw;
566                     }
567 
568                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
569                 }
570             }
571 
572             RaiseStatusEvent(sender, buildEvent);
573         }
574 
575         /// <summary>
576         /// Raises a "project build started" event to all registered loggers.
577         /// </summary>
578         /// <param name="sender">sender of the event</param>
579         /// <param name="buildEvent">ProjectStartedEventArgs</param>
580         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
581         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
582         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseProjectStartedEvent(object sender, ProjectStartedEventArgs buildEvent)583         private void RaiseProjectStartedEvent(object sender, ProjectStartedEventArgs buildEvent)
584         {
585             if (ProjectStarted != null)
586             {
587                 try
588                 {
589                     ProjectStarted(sender, buildEvent);
590                 }
591                 catch (LoggerException)
592                 {
593                     // if a logger has failed politely, abort immediately
594                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
595                     // if a fellow logger is throwing in an event handler.
596                     this.UnregisterAllEventHandlers();
597                     throw;
598                 }
599                 catch (Exception exception)
600                 {
601                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
602                     // if a fellow logger is throwing in an event handler.
603                     this.UnregisterAllEventHandlers();
604 
605                     if (ExceptionHandling.IsCriticalException(exception))
606                     {
607                         throw;
608                     }
609 
610                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
611                 }
612             }
613 
614             RaiseStatusEvent(sender, buildEvent);
615         }
616 
617         /// <summary>
618         /// Raises a "project build finished" event to all registered loggers.
619         /// </summary>
620         /// <param name="sender">sender of the event</param>
621         /// <param name="buildEvent">ProjectFinishedEventArgs</param>
622         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
623         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
624         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseProjectFinishedEvent(object sender, ProjectFinishedEventArgs buildEvent)625         private void RaiseProjectFinishedEvent(object sender, ProjectFinishedEventArgs buildEvent)
626         {
627             if (ProjectFinished != null)
628             {
629                 try
630                 {
631                     ProjectFinished(sender, buildEvent);
632                 }
633                 catch (LoggerException)
634                 {
635                     // if a logger has failed politely, abort immediately
636                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
637                     // if a fellow logger is throwing in an event handler.
638                     this.UnregisterAllEventHandlers();
639                     throw;
640                 }
641                 catch (Exception exception)
642                 {
643                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
644                     // if a fellow logger is throwing in an event handler.
645                     this.UnregisterAllEventHandlers();
646 
647                     if (ExceptionHandling.IsCriticalException(exception))
648                     {
649                         throw;
650                     }
651 
652                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
653                 }
654             }
655 
656             RaiseStatusEvent(sender, buildEvent);
657         }
658 
659         /// <summary>
660         /// Raises a "target build started" event to all registered loggers.
661         /// </summary>
662         /// <param name="sender">sender of the event</param>
663         /// <param name="buildEvent">TargetStartedEventArgs</param>
664         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
665         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
666         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseTargetStartedEvent(object sender, TargetStartedEventArgs buildEvent)667         private void RaiseTargetStartedEvent(object sender, TargetStartedEventArgs buildEvent)
668         {
669             if (TargetStarted != null)
670             {
671                 try
672                 {
673                     TargetStarted(sender, buildEvent);
674                 }
675                 catch (LoggerException)
676                 {
677                     // if a logger has failed politely, abort immediately
678                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
679                     // if a fellow logger is throwing in an event handler.
680                     this.UnregisterAllEventHandlers();
681                     throw;
682                 }
683                 catch (Exception exception)
684                 {
685                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
686                     // if a fellow logger is throwing in an event handler.
687                     this.UnregisterAllEventHandlers();
688 
689                     if (ExceptionHandling.IsCriticalException(exception))
690                     {
691                         throw;
692                     }
693 
694                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
695                 }
696             }
697 
698             RaiseStatusEvent(sender, buildEvent);
699         }
700 
701         /// <summary>
702         /// Raises a "target build finished" event to all registered loggers.
703         /// </summary>
704         /// <param name="sender">sender of the event</param>
705         /// <param name="buildEvent">TargetFinishedEventArgs</param>
706         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
707         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
708         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseTargetFinishedEvent(object sender, TargetFinishedEventArgs buildEvent)709         private void RaiseTargetFinishedEvent(object sender, TargetFinishedEventArgs buildEvent)
710         {
711             if (TargetFinished != null)
712             {
713                 try
714                 {
715                     TargetFinished(sender, buildEvent);
716                 }
717                 catch (LoggerException)
718                 {
719                     // if a logger has failed politely, abort immediately
720                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
721                     // if a fellow logger is throwing in an event handler.
722                     this.UnregisterAllEventHandlers();
723                     throw;
724                 }
725                 catch (Exception exception)
726                 {
727                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
728                     // if a fellow logger is throwing in an event handler.
729                     this.UnregisterAllEventHandlers();
730 
731                     if (ExceptionHandling.IsCriticalException(exception))
732                     {
733                         throw;
734                     }
735 
736                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
737                 }
738             }
739 
740             RaiseStatusEvent(sender, buildEvent);
741         }
742 
743         /// <summary>
744         /// Raises a "task execution started" event to all registered loggers.
745         /// </summary>
746         /// <param name="sender">sender of the event</param>
747         /// <param name="buildEvent">TaskStartedEventArgs</param>
748         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
749         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
750         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseTaskStartedEvent(object sender, TaskStartedEventArgs buildEvent)751         private void RaiseTaskStartedEvent(object sender, TaskStartedEventArgs buildEvent)
752         {
753             if (TaskStarted != null)
754             {
755                 try
756                 {
757                     TaskStarted(sender, buildEvent);
758                 }
759                 catch (LoggerException)
760                 {
761                     // if a logger has failed politely, abort immediately
762                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
763                     // if a fellow logger is throwing in an event handler.
764                     this.UnregisterAllEventHandlers();
765                     throw;
766                 }
767                 catch (Exception exception)
768                 {
769                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
770                     // if a fellow logger is throwing in an event handler.
771                     this.UnregisterAllEventHandlers();
772 
773                     if (ExceptionHandling.IsCriticalException(exception))
774                     {
775                         throw;
776                     }
777 
778                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
779                 }
780             }
781 
782             RaiseStatusEvent(sender, buildEvent);
783         }
784 
785         /// <summary>
786         /// Raises a "task finished executing" event to all registered loggers.
787         /// </summary>
788         /// <param name="sender">sender of the event</param>
789         /// <param name="buildEvent">TaskFinishedEventArgs</param>
790         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
791         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
792         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseTaskFinishedEvent(object sender, TaskFinishedEventArgs buildEvent)793         private void RaiseTaskFinishedEvent(object sender, TaskFinishedEventArgs buildEvent)
794         {
795             if (TaskFinished != null)
796             {
797                 try
798                 {
799                     TaskFinished(sender, buildEvent);
800                 }
801                 catch (LoggerException)
802                 {
803                     // if a logger has failed politely, abort immediately
804                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
805                     // if a fellow logger is throwing in an event handler.
806                     this.UnregisterAllEventHandlers();
807                     throw;
808                 }
809                 catch (Exception exception)
810                 {
811                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
812                     // if a fellow logger is throwing in an event handler.
813                     this.UnregisterAllEventHandlers();
814 
815                     if (ExceptionHandling.IsCriticalException(exception))
816                     {
817                         throw;
818                     }
819 
820                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
821                 }
822             }
823 
824             RaiseStatusEvent(sender, buildEvent);
825         }
826 
827         /// <summary>
828         /// Raises a custom event to all registered loggers.
829         /// </summary>
830         /// <param name="sender">sender of the event</param>
831         /// <param name="buildEvent">CustomBuildEventArgs</param>
832         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
833         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
834         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseCustomEvent(object sender, CustomBuildEventArgs buildEvent)835         private void RaiseCustomEvent(object sender, CustomBuildEventArgs buildEvent)
836         {
837             if (CustomEventRaised != null)
838             {
839                 try
840                 {
841                     CustomEventRaised(sender, buildEvent);
842                 }
843                 catch (LoggerException)
844                 {
845                     // if a logger has failed politely, abort immediately
846                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
847                     // if a fellow logger is throwing in an event handler.
848                     this.UnregisterAllEventHandlers();
849                     throw;
850                 }
851                 catch (Exception exception)
852                 {
853                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
854                     // if a fellow logger is throwing in an event handler.
855                     this.UnregisterAllEventHandlers();
856 
857                     if (ExceptionHandling.IsCriticalException(exception))
858                     {
859                         throw;
860                     }
861 
862                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
863                 }
864             }
865 
866             RaiseAnyEvent(sender, buildEvent);
867         }
868 
869         /// <summary>
870         /// Raises a catch-all build status event to all registered loggers.
871         /// </summary>
872         /// <param name="sender">sender of the event</param>
873         /// <param name="buildEvent">BuildStatusEventArgs</param>
874         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
875         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
876         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseStatusEvent(object sender, BuildStatusEventArgs buildEvent)877         private void RaiseStatusEvent(object sender, BuildStatusEventArgs buildEvent)
878         {
879             if (StatusEventRaised != null)
880             {
881                 try
882                 {
883                     StatusEventRaised(sender, buildEvent);
884                 }
885                 catch (LoggerException)
886                 {
887                     // if a logger has failed politely, abort immediately
888                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
889                     // if a fellow logger is throwing in an event handler.
890                     this.UnregisterAllEventHandlers();
891                     throw;
892                 }
893                 catch (Exception exception)
894                 {
895                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
896                     // if a fellow logger is throwing in an event handler.
897                     this.UnregisterAllEventHandlers();
898 
899                     if (ExceptionHandling.IsCriticalException(exception))
900                     {
901                         throw;
902                     }
903 
904                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
905                 }
906             }
907 
908             RaiseAnyEvent(sender, buildEvent);
909         }
910 
911         /// <summary>
912         /// Raises a catch-all build event to all registered loggers.
913         /// </summary>
914         /// <param name="sender">sender of the event</param>
915         /// <param name="buildEvent">Build EventArgs</param>
916         /// <exception cref="LoggerException">When EventHandler raises an logger exception the LoggerException is rethrown</exception>
917         /// <exception cref="InternalLoggerException">Any exceptions which are not LoggerExceptions are wrapped in an InternalLoggerException</exception>
918         /// <exception cref="Exception">ExceptionHandling.IsCriticalException exceptions will not be wrapped</exception>
RaiseAnyEvent(object sender, BuildEventArgs buildEvent)919         private void RaiseAnyEvent(object sender, BuildEventArgs buildEvent)
920         {
921             if (AnyEventRaised != null)
922             {
923                 try
924                 {
925                     AnyEventRaised(sender, buildEvent);
926                 }
927                 catch (LoggerException exception)
928                 {
929                     // if a logger has failed politely, abort immediately
930                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
931                     // if a fellow logger is throwing in an event handler.
932                     this.UnregisterAllEventHandlers();
933 
934                     // We ought to dump this farther up the stack, but if for example a task is logging an event within a
935                     // catch(Exception) block and not rethrowing it, there's the possibility that this exception could
936                     // just get silently eaten.  So better to have duplicates than to not log the problem at all. :)
937                     ExceptionHandling.DumpExceptionToFile(exception);
938 
939                     throw;
940                 }
941                 catch (Exception exception)
942                 {
943                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
944                     // if a fellow logger is throwing in an event handler.
945                     this.UnregisterAllEventHandlers();
946 
947                     // We ought to dump this farther up the stack, but if for example a task is logging an event within a
948                     // catch(Exception) block and not rethrowing it, there's the possibility that this exception could
949                     // just get silently eaten.  So better to have duplicates than to not log the problem at all. :)
950                     ExceptionHandling.DumpExceptionToFile(exception);
951 
952                     if (ExceptionHandling.IsCriticalException(exception))
953                     {
954                         throw;
955                     }
956 
957                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
958                 }
959             }
960         }
961 
962         /// <summary>
963         /// Raises the a telemetry event to all registered loggers.
964         /// </summary>
RaiseTelemetryEvent(object sender, TelemetryEventArgs buildEvent)965         private void RaiseTelemetryEvent(object sender, TelemetryEventArgs buildEvent)
966         {
967             if (TelemetryLogged != null)
968             {
969                 try
970                 {
971                     TelemetryLogged(sender, buildEvent);
972                 }
973                 catch (LoggerException)
974                 {
975                     // if a logger has failed politely, abort immediately
976                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
977                     // if a fellow logger is throwing in an event handler.
978                     this.UnregisterAllEventHandlers();
979                     throw;
980                 }
981                 catch (Exception exception)
982                 {
983                     // first unregister all loggers, since other loggers may receive remaining events in unexpected orderings
984                     // if a fellow logger is throwing in an event handler.
985                     this.UnregisterAllEventHandlers();
986 
987                     if (ExceptionHandling.IsCriticalException(exception))
988                     {
989                         throw;
990                     }
991 
992                     InternalLoggerException.Throw(exception, buildEvent, "FatalErrorWhileLogging", false);
993                 }
994             }
995         }
996 
997         /// <summary>
998         /// Determines if the specified warning should be treated as an error.
999         /// </summary>
1000         /// <param name="warningEvent">A <see cref="BuildWarningEventArgs"/> that specifies the warning.</param>
1001         /// <returns><code>true</code> if the warning should be treated as an error, otherwise <code>false</code>.</returns>
ShouldTreatWarningAsError(BuildWarningEventArgs warningEvent)1002         private bool ShouldTreatWarningAsError(BuildWarningEventArgs warningEvent)
1003         {
1004             // This only applies if the user specified /warnaserror from the command-line or added an empty set through the object model
1005             //
1006             if (WarningsAsErrors != null)
1007             {
1008                 // Global warnings as errors apply to all projects.  If the list is empty or contains the code, the warning should be treated as an error
1009                 //
1010                 if (WarningsAsErrors.Count == 0 || WarningsAsErrors.Contains(warningEvent.Code))
1011                 {
1012                     return true;
1013                 }
1014             }
1015 
1016             // This only applies if the user specified <MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors or <MSBuildWarningsAsErrors />
1017             // and there is a valid ProjectInstanceId for the warning.
1018             //
1019             if (WarningsAsErrorsByProject != null && warningEvent.BuildEventContext != null && warningEvent.BuildEventContext.ProjectInstanceId != BuildEventContext.InvalidProjectInstanceId)
1020             {
1021                 ISet<string> codesByProject;
1022 
1023                 // Attempt to get the list of warnings to treat as errors for the current project
1024                 //
1025                 if (WarningsAsErrorsByProject.TryGetValue(warningEvent.BuildEventContext.ProjectInstanceId, out codesByProject) && codesByProject != null)
1026                 {
1027                     // We create an empty set if all warnings should be treated as errors so that should be checked first.
1028                     // If the set is not empty, check the specific code.
1029                     //
1030                     return codesByProject.Count == 0 || codesByProject.Contains(warningEvent.Code);
1031                 }
1032             }
1033 
1034             return false;
1035         }
1036 
1037         /// <summary>
1038         /// Determines if the specified warning should be treated as a low importance message.
1039         /// </summary>
1040         /// <param name="warningEvent">A <see cref="BuildWarningEventArgs"/> that specifies the warning.</param>
1041         /// <returns><code>true</code> if the warning should be treated as a low importance message, otherwise <code>false</code>.</returns>
ShouldTreatWarningAsMessage(BuildWarningEventArgs warningEvent)1042         private bool ShouldTreatWarningAsMessage(BuildWarningEventArgs warningEvent)
1043         {
1044             // This only applies if the user specified /nowarn at the command-line or added the warning code through the object model
1045             //
1046             if (WarningsAsMessages != null && WarningsAsMessages.Contains(warningEvent.Code))
1047             {
1048                 return true;
1049             }
1050 
1051             // This only applies if the user specified <MSBuildWarningsAsMessages /> and there is a valid ProjectInstanceId
1052             //
1053             if (WarningsAsMessagesByProject != null && warningEvent.BuildEventContext != null && warningEvent.BuildEventContext.ProjectInstanceId != BuildEventContext.InvalidProjectInstanceId)
1054             {
1055                 ISet<string> codesByProject;
1056 
1057                 if (WarningsAsMessagesByProject.TryGetValue(warningEvent.BuildEventContext.ProjectInstanceId, out codesByProject) && codesByProject != null)
1058                 {
1059                     return codesByProject.Contains(warningEvent.Code);
1060                 }
1061             }
1062 
1063             return false;
1064         }
1065 
1066         #endregion
1067         #endregion
1068     }
1069 }
1070