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