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 
4 using System;
5 using System.Collections;
6 using System.Text;
7 using System.Collections.Generic;
8 
9 using Microsoft.Build.Framework;
10 using Microsoft.Build.BuildEngine.Shared;
11 
12 
13 namespace Microsoft.Build.BuildEngine
14 {
15     /// <summary>
16     /// Logger that forwards events to a central logger (e.g ConsoleLogger)
17     /// residing on the parent node.
18     /// </summary>
19     public class ConfigurableForwardingLogger: IForwardingLogger
20     {
21         #region Constructors
22         /// <summary>
23         /// Default constructor.
24         /// </summary>
ConfigurableForwardingLogger()25         public ConfigurableForwardingLogger()
26         {
27             InitializeForwardingTable();
28         }
29         #endregion
30 
31         #region Properties
32 
33         /// <summary>
34         /// Gets or sets the level of detail to show in the event log.
35         /// </summary>
36         /// <value>Verbosity level.</value>
37         public LoggerVerbosity Verbosity
38         {
39             get { return verbosity; }
40             set { verbosity = value; }
41         }
42 
43         /// <summary>
44         /// The console logger takes a single parameter to suppress the output of the errors
45         /// and warnings summary at the end of a build.
46         /// </summary>
47         /// <value>null</value>
48         public string Parameters
49         {
50             get { return loggerParameters; }
51             set { loggerParameters = value; }
52         }
53 
54         /// <summary>
55         /// This property is set by the build engine to allow a node loggers to forward messages to the
56         /// central logger
57         /// </summary>
58         public IEventRedirector BuildEventRedirector
59         {
60             get { return this.buildEventRedirector; }
61             set { this.buildEventRedirector = value; }
62         }
63 
64         public int NodeId
65         {
66             get { return nodeId; }
67             set { nodeId = value; }
68         }
69         #endregion
70 
71         #region Methods
72 
73         /// <summary>
74         /// Initialize the Forwarding Table with the default values
75         /// </summary>
InitializeForwardingTable()76         private void InitializeForwardingTable()
77         {
78             forwardingTable = new Dictionary<string, int>(15, StringComparer.OrdinalIgnoreCase);
79             forwardingTable[BuildStartedEventDescription] = 0;
80             forwardingTable[BuildFinishedEventDescription] = 0;
81             forwardingTable[ProjectStartedEventDescription] = 0;
82             forwardingTable[ProjectFinishedEventDescription] = 0;
83             forwardingTable[TargetStartedEventDescription] = 0;
84             forwardingTable[TargetFinishedEventDescription] = 0;
85             forwardingTable[TaskStartedEventDescription] = 0;
86             forwardingTable[TaskFinishedEventDescription] = 0;
87             forwardingTable[ErrorEventDescription] = 0;
88             forwardingTable[WarningEventDescription] = 0;
89             forwardingTable[HighMessageEventDescription] = 0;
90             forwardingTable[NormalMessageEventDescription] = 0;
91             forwardingTable[LowMessageEventDescription] = 0;
92             forwardingTable[CustomEventDescription] = 0;
93             forwardingTable[CommandLineDescription] = 0;
94             forwardingSetFromParameters = false;
95         }
96 
97         /// <summary>
98         /// Parses out the logger parameters from the Parameters string.
99         /// </summary>
ParseParameters()100         private void ParseParameters()
101         {
102             if (loggerParameters != null)
103             {
104                 string[] parameterComponents;
105 
106                 parameterComponents = loggerParameters.Split(parameterDelimiters);
107                 for (int param = 0; param < parameterComponents.Length; param++)
108                 {
109                     if (parameterComponents[param].Length > 0)
110                     {
111                         ApplyParameter(parameterComponents[param]);
112                     }
113                 }
114                 // Setting events to forward on the commandline will override the verbosity and other switches such as
115                 // showPerfSummand and ShowSummary
116                 if (forwardingSetFromParameters)
117                 {
118                     this.showPerfSummary = false;
119                     this.showSummary = true;
120                 }
121             }
122         }
123 
124         /// <summary>
125         /// Logger parameters can be used to enable and disable specific event types.
126         /// Otherwise, the verbosity is used to choose which events to forward.
127         /// </summary>
ApplyParameter(string parameterName)128         private void ApplyParameter(string parameterName)
129         {
130             ErrorUtilities.VerifyThrowArgumentNull(parameterName, "parameterName");
131 
132             if (forwardingTable.ContainsKey(parameterName))
133             {
134                 forwardingSetFromParameters = true;
135                 forwardingTable[parameterName] = 1;
136             }
137 
138             // If any of the following parameters are set, we will make sure we forward the events
139             // necessary for the central logger to emit the requested information
140             if (0 == String.Compare(parameterName, PerformanceSummaryDescription, StringComparison.OrdinalIgnoreCase))
141             {
142                 this.showPerfSummary = true;
143             }
144             else if (0 == String.Compare(parameterName, NoSummaryDescription, StringComparison.OrdinalIgnoreCase))
145             {
146                 this.showSummary = false;
147             }
148             else if (0 == String.Compare(parameterName, ShowCommandLineDescription, StringComparison.OrdinalIgnoreCase))
149             {
150                 showCommandLine = true;
151             }
152         }
153 
154         /// <summary>
155         /// Signs up the console logger for all build events.
156         /// </summary>
Initialize(IEventSource eventSource)157         public virtual void Initialize(IEventSource eventSource)
158         {
159             ErrorUtilities.VerifyThrowArgumentNull(eventSource, "eventSource");
160 
161             ParseParameters();
162 
163             ResetLoggerState();
164 
165             if (!forwardingSetFromParameters)
166             {
167                 SetForwardingBasedOnVerbosity();
168             }
169 
170             eventSource.BuildStarted += new BuildStartedEventHandler(BuildStartedHandler);
171             eventSource.BuildFinished += new BuildFinishedEventHandler(BuildFinishedHandler);
172             eventSource.ProjectStarted += new ProjectStartedEventHandler(ProjectStartedHandler);
173             eventSource.ProjectFinished += new ProjectFinishedEventHandler(ProjectFinishedHandler);
174             eventSource.TargetStarted += new TargetStartedEventHandler(TargetStartedHandler);
175             eventSource.TargetFinished += new TargetFinishedEventHandler(TargetFinishedHandler);
176             eventSource.TaskStarted += new TaskStartedEventHandler(TaskStartedHandler);
177             eventSource.TaskFinished += new TaskFinishedEventHandler(TaskFinishedHandler);
178             eventSource.ErrorRaised += new BuildErrorEventHandler(ErrorHandler);
179             eventSource.WarningRaised += new BuildWarningEventHandler(WarningHandler);
180             eventSource.MessageRaised += new BuildMessageEventHandler(MessageHandler);
181             eventSource.CustomEventRaised += new CustomBuildEventHandler(CustomEventHandler);
182         }
183 
184         /// <summary>
185         /// Signs up the console logger for all build events.
186         /// </summary>
Initialize(IEventSource eventSource, int nodeCount)187         public void Initialize(IEventSource eventSource, int nodeCount)
188         {
189             Initialize(eventSource);
190         }
191 
SetForwardingBasedOnVerbosity()192         private void SetForwardingBasedOnVerbosity()
193         {
194 
195             forwardingTable[BuildStartedEventDescription] = 0;
196             forwardingTable[BuildFinishedEventDescription] = 0;
197 
198             if (IsVerbosityAtLeast(LoggerVerbosity.Quiet))
199             {
200                 forwardingTable[ErrorEventDescription] = 1;
201                 forwardingTable[WarningEventDescription] = 1;
202             }
203 
204             if (IsVerbosityAtLeast(LoggerVerbosity.Minimal))
205             {
206                 forwardingTable[HighMessageEventDescription] = 1;
207             }
208 
209             if (IsVerbosityAtLeast(LoggerVerbosity.Normal))
210             {
211                 forwardingTable[NormalMessageEventDescription] = 1;
212                 forwardingTable[ProjectStartedEventDescription] = 1;
213                 forwardingTable[ProjectFinishedEventDescription] = 1;
214                 forwardingTable[TargetStartedEventDescription] = 1;
215                 forwardingTable[TargetFinishedEventDescription] = 1;
216             }
217 
218             if (IsVerbosityAtLeast(LoggerVerbosity.Detailed))
219             {
220                 forwardingTable[TargetStartedEventDescription] = 1;
221                 forwardingTable[TargetFinishedEventDescription] = 1;
222                 forwardingTable[TaskStartedEventDescription] = 1;
223                 forwardingTable[TaskFinishedEventDescription] = 1;
224                 forwardingTable[LowMessageEventDescription] = 1;
225                 forwardingTable[CommandLineDescription] = 1;
226             }
227 
228             if (IsVerbosityAtLeast(LoggerVerbosity.Diagnostic))
229             {
230                 forwardingTable[CustomEventDescription] = 1;
231             }
232 
233             if (showSummary)
234             {
235                 forwardingTable[ErrorEventDescription] = 1;
236                 forwardingTable[WarningEventDescription] = 1;
237             }
238 
239             if (this.showPerfSummary)
240             {
241                 forwardingTable[TargetStartedEventDescription] = 1;
242                 forwardingTable[TargetFinishedEventDescription] = 1;
243                 forwardingTable[TaskStartedEventDescription] = 1;
244                 forwardingTable[TaskFinishedEventDescription] = 1;
245                 forwardingTable[TargetStartedEventDescription] = 1;
246                 forwardingTable[TargetFinishedEventDescription] = 1;
247                 forwardingTable[ProjectStartedEventDescription] = 1;
248                 forwardingTable[ProjectFinishedEventDescription] = 1;
249             }
250 
251             if (this.showCommandLine)
252             {
253                 forwardingTable[CommandLineDescription] = 1;
254             }
255         }
256 
257 
258         /// <summary>
259         /// Reset the states of per-build member variables.
260         /// Used when a build is finished, but the logger might be needed for the next build.
261         /// </summary>
ResetLoggerState()262         private void ResetLoggerState()
263         {
264             // No state needs resetting
265         }
266 
267         /// <summary>
268         /// Called when Engine is done with this logger
269         /// </summary>
Shutdown()270         public virtual void Shutdown()
271         {
272             // Nothing to do
273         }
274 
275         /// <summary>
276         /// Handler for build started events
277         /// </summary>
278         /// <param name="sender">sender (should be null)</param>
279         /// <param name="e">event arguments</param>
BuildStartedHandler(object sender, BuildStartedEventArgs e)280         private void BuildStartedHandler(object sender, BuildStartedEventArgs e)
281         {
282             // This is false by default
283             if (forwardingTable[BuildStartedEventDescription] == 1)
284             {
285                 ForwardToCentralLogger(e);
286             }
287         }
288 
289         /// <summary>
290         /// Handler for build finished events
291         /// </summary>
292         /// <param name="sender">sender (should be null)</param>
293         /// <param name="e">event arguments</param>
BuildFinishedHandler(object sender, BuildFinishedEventArgs e)294         private void BuildFinishedHandler(object sender, BuildFinishedEventArgs e)
295         {
296             // This is false by default
297             if (forwardingTable[BuildFinishedEventDescription] == 1)
298             {
299                 ForwardToCentralLogger(e);
300             }
301             ResetLoggerState();
302         }
303 
304         /// <summary>
305         /// Handler for project started events
306         /// </summary>
307         /// <param name="sender">sender (should be null)</param>
308         /// <param name="e">event arguments</param>
ProjectStartedHandler(object sender, ProjectStartedEventArgs e)309         private void ProjectStartedHandler(object sender, ProjectStartedEventArgs e)
310         {
311             if (forwardingTable[ProjectStartedEventDescription] == 1)
312             {
313                 ForwardToCentralLogger(e);
314             }
315         }
316 
317         /// <summary>
318         /// Handler for project finished events
319         /// </summary>
320         /// <param name="sender">sender (should be null)</param>
321         /// <param name="e">event arguments</param>
ProjectFinishedHandler(object sender, ProjectFinishedEventArgs e)322         private void ProjectFinishedHandler(object sender, ProjectFinishedEventArgs e)
323         {
324             if (forwardingTable[ProjectFinishedEventDescription] == 1)
325             {
326                 ForwardToCentralLogger(e);
327             }
328         }
329 
330         /// <summary>
331         /// Handler for target started events
332         /// </summary>
333         /// <param name="sender">sender (should be null)</param>
334         /// <param name="e">event arguments</param>
TargetStartedHandler(object sender, TargetStartedEventArgs e)335         private void TargetStartedHandler(object sender, TargetStartedEventArgs e)
336         {
337             if (forwardingTable[TargetStartedEventDescription] == 1)
338             {
339                 ForwardToCentralLogger(e);
340             }
341         }
342 
343         /// <summary>
344         /// Handler for target finished events
345         /// </summary>
346         /// <param name="sender">sender (should be null)</param>
347         /// <param name="e">event arguments</param>
TargetFinishedHandler(object sender, TargetFinishedEventArgs e)348         private void TargetFinishedHandler(object sender, TargetFinishedEventArgs e)
349         {
350             if (forwardingTable[TargetFinishedEventDescription] == 1)
351             {
352                 ForwardToCentralLogger(e);
353             }
354         }
355 
356         /// <summary>
357         /// Handler for task started events
358         /// </summary>
359         /// <param name="sender">sender (should be null)</param>
360         /// <param name="e">event arguments</param>
TaskStartedHandler(object sender, TaskStartedEventArgs e)361         private void TaskStartedHandler(object sender, TaskStartedEventArgs e)
362         {
363             if (forwardingTable[TaskStartedEventDescription] == 1)
364             {
365                 ForwardToCentralLogger(e);
366             }
367         }
368 
369         /// <summary>
370         /// Handler for task finished events
371         /// </summary>
372         /// <param name="sender">sender (should be null)</param>
373         /// <param name="e">event arguments</param>
TaskFinishedHandler(object sender, TaskFinishedEventArgs e)374         private void TaskFinishedHandler(object sender, TaskFinishedEventArgs e)
375         {
376             if (forwardingTable[TaskFinishedEventDescription] == 1)
377             {
378                 ForwardToCentralLogger(e);
379             }
380         }
381 
382         /// <summary>
383         /// Prints an error event
384         /// </summary>
ErrorHandler(object sender, BuildErrorEventArgs e)385         private void ErrorHandler(object sender, BuildErrorEventArgs e)
386         {
387             if (forwardingTable[ErrorEventDescription] == 1)
388             {
389                 ForwardToCentralLogger(e);
390             }
391         }
392 
393         /// <summary>
394         /// Prints a warning event
395         /// </summary>
WarningHandler(object sender, BuildWarningEventArgs e)396         private void WarningHandler(object sender, BuildWarningEventArgs e)
397         {
398             if (forwardingTable[WarningEventDescription] == 1)
399             {
400                 ForwardToCentralLogger(e);
401             }
402         }
403 
404         /// <summary>
405         /// Prints a message event
406         /// </summary>
MessageHandler(object sender, BuildMessageEventArgs e)407         private void MessageHandler(object sender, BuildMessageEventArgs e)
408         {
409             bool forwardEvent = false;
410 
411             if (forwardingTable[LowMessageEventDescription] == 1 && e.Importance == MessageImportance.Low)
412             {
413                 forwardEvent = true;
414             }
415             else if (forwardingTable[NormalMessageEventDescription] == 1 && e.Importance == MessageImportance.Normal)
416             {
417                 forwardEvent = true;
418             }
419             else if (forwardingTable[HighMessageEventDescription] == 1 && e.Importance == MessageImportance.High)
420             {
421                 forwardEvent = true;
422             }
423             else if (forwardingTable[CommandLineDescription] == 1 && e is TaskCommandLineEventArgs)
424             {
425                 forwardEvent = true;
426             }
427 
428             if (forwardEvent)
429             {
430                 ForwardToCentralLogger(e);
431             }
432         }
433 
434         /// <summary>
435         /// Prints a custom event
436         /// </summary>
CustomEventHandler(object sender, CustomBuildEventArgs e)437         private void CustomEventHandler(object sender, CustomBuildEventArgs e)
438         {
439             if (forwardingTable[CustomEventDescription] == 1)
440             {
441                 ForwardToCentralLogger(e);
442             }
443         }
444 
ForwardToCentralLogger(BuildEventArgs e)445         protected virtual void ForwardToCentralLogger(BuildEventArgs e)
446         {
447             buildEventRedirector.ForwardEvent(e);
448         }
449 
450         /// <summary>
451         /// Determines whether the current verbosity setting is at least the value
452         /// passed in.
453         /// </summary>
IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)454         private bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
455         {
456             return (this.verbosity >= checkVerbosity);
457         }
458         #endregion
459 
460         #region Private member data
461 
462         /// <summary>
463         /// Controls the amount of text displayed by the logger
464         /// </summary>
465         private LoggerVerbosity verbosity = LoggerVerbosity.Normal;
466 
467         /// <summary>
468         /// Console logger parameters.
469         /// </summary>
470         private string loggerParameters = null;
471 
472         /// <summary>
473         /// Console logger parameters delimiters.
474         /// </summary>
475         private static readonly char[] parameterDelimiters = { ';' };
476 
477         /// <summary>
478         /// Strings that users of this logger can pass in to enable specific events or logger output.
479         /// Also used as keys into our dictionary.
480         /// </summary>
481         private const string BuildStartedEventDescription = "BUILDSTARTEDEVENT";
482         private const string BuildFinishedEventDescription = "BUILDFINISHEDEVENT";
483         private const string ProjectStartedEventDescription = "PROJECTSTARTEDEVENT";
484         private const string ProjectFinishedEventDescription = "PROJECTFINISHEDEVENT";
485         private const string TargetStartedEventDescription = "TARGETSTARTEDEVENT";
486         private const string TargetFinishedEventDescription = "TARGETFINISHEDEVENT";
487         private const string TaskStartedEventDescription = "TASKSTARTEDEVENT";
488         private const string TaskFinishedEventDescription = "TASKFINISHEDEVENT";
489         private const string ErrorEventDescription = "ERROREVENT";
490         private const string WarningEventDescription = "WARNINGEVENT";
491         private const string HighMessageEventDescription = "HIGHMESSAGEEVENT";
492         private const string NormalMessageEventDescription = "NORMALMESSAGEEVENT";
493         private const string LowMessageEventDescription = "LOWMESSAGEEVENT";
494         private const string CustomEventDescription = "CUSTOMEVENT";
495         private const string CommandLineDescription = "COMMANDLINE";
496         private const string PerformanceSummaryDescription = "PERFORMANCESUMMARY";
497         private const string NoSummaryDescription = "NOSUMMARY";
498         private const string ShowCommandLineDescription = "SHOWCOMMANDLINE";
499 
500         #region Per-build Members
501 
502         /// <summary>
503         /// A table indicating if a particular event type should be forwarded
504         /// The value is type int rather than bool to avoid the problem of JITting generics.
505         /// Dictionary<string, int> is already compiled into mscorlib.
506         /// </summary>
507         private Dictionary<string, int> forwardingTable;
508 
509         /// <summary>
510         /// A pointer to the central logger
511         /// </summary>
512         private IEventRedirector buildEventRedirector;
513 
514         /// <summary>
515         /// Indicates if the events to forward are being set by the parameters sent to the logger
516         /// if this is false the events to forward are based on verbosity else verbosity settings will be ignored
517         /// </summary>
518         private bool forwardingSetFromParameters;
519 
520         /// <summary>
521         /// Console logger should show error and warning summary at the end of build?
522         /// </summary>
523         private bool showSummary = true;
524 
525         /// <summary>
526         /// When true, accumulate performance numbers.
527         /// </summary>
528         private bool showPerfSummary = false;
529 
530         /// <summary>
531         /// When true the commandline message is sent
532         /// </summary>
533         private bool showCommandLine = false;
534 
535         /// <summary>
536         /// Id of the node the logger is attached to
537         /// </summary>
538         private int nodeId;
539 
540         #endregion
541         #endregion
542 
543     }
544 }
545