MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Concurrent;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Globalization;8using System.Management.Automation.Runspaces;9using System.Management.Automation.Tracing;10using System.Security;11using System.Threading;12 13namespace System.Management.Automation14{15 /// <summary>16 /// Monad Logging in general is a two layer architecture. At the upper layer are the17 /// Msh Log Engine and Logging Api. At the lower layer is the Provider Interface18 /// and Log Providers. This architecture is adopted to achieve independency between19 /// Monad logging and logging details of different logging technology.20 ///21 /// This file implements the upper layer of the Monad Logging architecture.22 /// Lower layer of Msh Log architecture is implemented in LogProvider.cs file.23 ///24 /// Logging Api is made up of following five sets25 /// 1. Engine Health Event26 /// 2. Engine Lifecycle Event27 /// 3. Command Lifecycle Event28 /// 4. Provider Lifecycle Event29 /// 5. Settings Event30 ///31 /// Msh Log Engine provides features in following areas,32 /// 1. Loading and managing logging providers. Based on some "Provider Catalog", engine will try to33 /// load providers. First provider that is successfully loaded will be used for low level logging.34 /// If no providers can be loaded, a dummy provider will be used, which will essentially do nothing.35 /// 2. Implementation of logging api functions. These api functions is implemented by calling corresponding36 /// functions in provider interface.37 /// 3. Sequence Id Generation. Unique id are generated in this class. These id's will be attached to events.38 /// 4. Monad engine state management. Engine state is stored in ExecutionContext class but managed here.39 /// Later on, this feature may be moved to engine itself (where it should belongs to) when sophisticated40 /// engine state model is established.41 /// 5. Logging policy support. Events are logged or not logged based on logging policy settings (which is stored42 /// in session state of the engine.43 ///44 /// MshLog class is defined as a static class. This essentially make the logging api to be a static api.45 ///46 /// We want to provide sufficient synchronization for static functions calls.47 /// This is not needed for now because of following two reasons,48 /// a. Currently, only one monad engine can be running in one process. So logically only one49 /// event will be log at a time.50 /// b. Even in the case of multiple events are logged, underlining logging media should51 /// provide synchronization.52 /// </summary>53 internal static class MshLog54 {55 #region Initialization56 57 /// <summary>58 /// A static dictionary to keep track of log providers for different shellId's.59 ///60 /// The value of this dictionary is never empty. A value of type DummyProvider means61 /// no logging.62 /// </summary>63 private static readonly ConcurrentDictionary<string, Collection<LogProvider>> s_logProviders =64 new ConcurrentDictionary<string, Collection<LogProvider>>();65 66 private const string _crimsonLogProviderAssemblyName = "MshCrimsonLog";67 private const string _crimsonLogProviderTypeName = "System.Management.Automation.Logging.CrimsonLogProvider";68 69 private static readonly Collection<string> s_ignoredCommands = new Collection<string>();70 71 /// <summary>72 /// Static constructor.73 /// </summary>74 static MshLog()75 {76 s_ignoredCommands.Add("Out-Lineoutput");77 s_ignoredCommands.Add("Format-Default");78 }79 80 /// <summary>81 /// Currently initialization is done in following sequence82 /// a. Try to load CrimsonLogProvider (in the case of Longhorn)83 /// b. If a fails, use the DummyLogProvider instead. (in low-level OS)84 ///85 /// In the longer turn, we may need to use a "Provider Catalog" for86 /// log provider loading.87 /// </summary>88 /// <param name="shellId"></param>89 /// <returns></returns>90 private static IEnumerable<LogProvider> GetLogProvider(string shellId)91 {92 return s_logProviders.GetOrAdd(shellId, CreateLogProvider);93 }94 95 /// <summary>96 /// Get Log Provider based on Execution Context.97 /// </summary>98 /// <param name="executionContext"></param>99 /// <returns></returns>100 private static IEnumerable<LogProvider> GetLogProvider(ExecutionContext executionContext)101 {102 if (executionContext == null)103 {104 throw PSTraceSource.NewArgumentNullException(nameof(executionContext));105 }106 107 string shellId = executionContext.ShellID;108 109 return GetLogProvider(shellId);110 }111 112 /// <summary>113 /// Get Log Provider based on Log Context.114 /// </summary>115 /// <param name="logContext"></param>116 /// <returns></returns>117 private static IEnumerable<LogProvider> GetLogProvider(LogContext logContext)118 {119 System.Diagnostics.Debug.Assert(logContext != null);120 System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(logContext.ShellId));121 122 return GetLogProvider(logContext.ShellId);123 }124 125 /// <summary>126 /// Create a log provider based on a shell Id.127 /// </summary>128 /// <param name="shellId"></param>129 /// <returns></returns>130 private static Collection<LogProvider> CreateLogProvider(string shellId)131 {132 Collection<LogProvider> providers = new Collection<LogProvider>();133 // Porting note: Linux does not support ETW134 135 try136 {137#if UNIX138 LogProvider sysLogProvider = new PSSysLogProvider();139 providers.Add(sysLogProvider);140#else141 LogProvider etwLogProvider = new PSEtwLogProvider();142 providers.Add(etwLogProvider);143#endif144 145 return providers;146 }147 catch (ArgumentException)148 {149 }150 catch (InvalidOperationException)151 {152 }153 catch (SecurityException)154 {155 // This exception will happen if we try to create an event source156 // (corresponding to the current running minishell)157 // when running as non-admin user. In that case, we will default158 // to dummy log.159 }160 161 providers.Add(new DummyLogProvider());162 return providers;163 }164 165 /// <summary>166 /// This will set the current log provider to be dummy log.167 /// </summary>168 /// <param name="shellId"></param>169 internal static void SetDummyLog(string shellId)170 {171 Collection<LogProvider> providers = new Collection<LogProvider> { new DummyLogProvider() };172 s_logProviders.AddOrUpdate(shellId, providers, (key, value) => providers);173 }174 175 #endregion176 177 #region Engine Health Event Logging Api178 179 /// <summary>180 /// LogEngineHealthEvent: Log an engine health event. If engine state is changed, a engine181 /// lifecycle event will be logged also.182 ///183 /// This is the basic form of EngineHealthEvent logging api, in which all parameters are provided.184 ///185 /// Variant form of this function is defined below, which will make parameters additionalInfo186 /// and newEngineState optional.187 /// </summary>188 /// <param name="executionContext">Execution context for the engine that is running.</param>189 /// <param name="eventId">EventId for the event to be logged.</param>190 /// <param name="exception">Exception associated with this event.</param>191 /// <param name="severity">Severity of this event.</param>192 /// <param name="additionalInfo">Additional information for this event.</param>193 /// <param name="newEngineState">New engine state.</param>194 internal static void LogEngineHealthEvent(ExecutionContext executionContext,195 int eventId,196 Exception exception,197 Severity severity,198 Dictionary<string, string> additionalInfo,199 EngineState newEngineState)200 {201 if (executionContext == null)202 {203 PSTraceSource.NewArgumentNullException(nameof(executionContext));204 return;205 }206 207 if (exception == null)208 {209 PSTraceSource.NewArgumentNullException(nameof(exception));210 return;211 }212 213 InvocationInfo invocationInfo = null;214 if (exception is IContainsErrorRecord icer && icer.ErrorRecord != null)215 {216 invocationInfo = icer.ErrorRecord.InvocationInfo;217 }218 219 foreach (LogProvider provider in GetLogProvider(executionContext))220 {221 if (NeedToLogEngineHealthEvent(provider, executionContext))222 {223 provider.LogEngineHealthEvent(GetLogContext(executionContext, invocationInfo, severity), eventId, exception, additionalInfo);224 }225 }226 227 if (newEngineState != EngineState.None)228 {229 LogEngineLifecycleEvent(executionContext, newEngineState, invocationInfo);230 }231 }232 233 /// <summary>234 /// This is a variation of LogEngineHealthEvent api to make additionalInfo and newEngineState235 /// optional.236 /// </summary>237 /// <param name="executionContext"></param>238 /// <param name="eventId"></param>239 /// <param name="exception"></param>240 /// <param name="severity"></param>241 internal static void LogEngineHealthEvent(ExecutionContext executionContext,242 int eventId,243 Exception exception,244 Severity severity)245 {246 LogEngineHealthEvent(executionContext, eventId, exception, severity, null);247 }248 249 /// <summary>250 /// This is a variation of LogEngineHealthEvent api to make eventid, additionalInfo and newEngineState251 /// optional.252 ///253 /// A default event id for engine health event will be used.254 /// </summary>255 /// <param name="executionContext"></param>256 /// <param name="exception"></param>257 /// <param name="severity"></param>258 internal static void LogEngineHealthEvent(ExecutionContext executionContext,259 Exception exception,260 Severity severity)261 {262 LogEngineHealthEvent(executionContext, 100, exception, severity, null);263 }264 265 /// <summary>266 /// This is a variation of LogEngineHealthEvent api to make newEngineState267 /// optional.268 /// </summary>269 /// <param name="executionContext"></param>270 /// <param name="eventId"></param>271 /// <param name="exception"></param>272 /// <param name="severity"></param>273 /// <param name="additionalInfo"></param>274 internal static void LogEngineHealthEvent(ExecutionContext executionContext,275 int eventId,276 Exception exception,277 Severity severity,278 Dictionary<string, string> additionalInfo)279 {280 LogEngineHealthEvent(executionContext, eventId, exception, severity, additionalInfo, EngineState.None);281 }282 283 /// <summary>284 /// This is a variation of LogEngineHealthEvent api to make additionalInfo285 /// optional.286 /// </summary>287 /// <param name="executionContext"></param>288 /// <param name="eventId"></param>289 /// <param name="exception"></param>290 /// <param name="severity"></param>291 /// <param name="newEngineState"></param>292 internal static void LogEngineHealthEvent(ExecutionContext executionContext,293 int eventId,294 Exception exception,295 Severity severity,296 EngineState newEngineState)297 {298 LogEngineHealthEvent(executionContext, eventId, exception, severity, null, newEngineState);299 }300 301 /// <summary>302 /// LogEngineHealthEvent: This is an API for logging engine health event while execution context303 /// is not available. In this case, caller of this API will directly construct LogContext304 /// instance.305 ///306 /// This API is currently used only by runspace before engine start.307 /// </summary>308 /// <param name="logContext">LogContext to be.</param>309 /// <param name="eventId">EventId for the event to be logged.</param>310 /// <param name="exception">Exception associated with this event.</param>311 /// <param name="additionalInfo">Additional information for this event.</param>312 internal static void LogEngineHealthEvent(LogContext logContext,313 int eventId,314 Exception exception,315 Dictionary<string, string> additionalInfo316 )317 {318 if (logContext == null)319 {320 PSTraceSource.NewArgumentNullException(nameof(logContext));321 return;322 }323 324 if (exception == null)325 {326 PSTraceSource.NewArgumentNullException(nameof(exception));327 return;328 }329 330 // Here execution context doesn't exist, we will have to log this event regardless.331 // Don't check NeedToLogEngineHealthEvent here.332 foreach (LogProvider provider in GetLogProvider(logContext))333 {334 provider.LogEngineHealthEvent(logContext, eventId, exception, additionalInfo);335 }336 }337 338 #endregion339 340 #region Engine Lifecycle Event Logging Api341 342 /// <summary>343 /// LogEngineLifecycleEvent: Log an engine lifecycle event.344 ///345 /// This is the basic form of EngineLifecycleEvent logging api, in which all parameters are provided.346 ///347 /// Variant form of this function is defined below, which will make parameter additionalInfo348 /// optional.349 /// </summary>350 /// <param name="executionContext">Execution context for current engine instance.</param>351 /// <param name="engineState">New engine state.</param>352 /// <param name="invocationInfo">InvocationInfo for current command that is running.</param>353 internal static void LogEngineLifecycleEvent(ExecutionContext executionContext,354 EngineState engineState,355 InvocationInfo invocationInfo)356 {357 if (executionContext == null)358 {359 PSTraceSource.NewArgumentNullException(nameof(executionContext));360 return;361 }362 363 EngineState previousState = GetEngineState(executionContext);364 if (engineState == previousState)365 return;366 367 foreach (LogProvider provider in GetLogProvider(executionContext))368 {369 if (NeedToLogEngineLifecycleEvent(provider, executionContext))370 {371 provider.LogEngineLifecycleEvent(GetLogContext(executionContext, invocationInfo), engineState, previousState);372 }373 }374 375 SetEngineState(executionContext, engineState);376 }377 378 /// <summary>379 /// This is a variation of basic LogEngineLifeCycleEvent api which makes invocationInfo380 /// optional.381 /// </summary>382 /// <param name="executionContext"></param>383 /// <param name="engineState"></param>384 internal static void LogEngineLifecycleEvent(ExecutionContext executionContext,385 EngineState engineState)386 {387 LogEngineLifecycleEvent(executionContext, engineState, null);388 }389 390 #endregion391 392 #region Command Health Event Logging Api393 394 /// <summary>395 /// LogProviderHealthEvent: Log a command health event.396 /// </summary>397 /// <param name="executionContext">Execution context for the engine that is running.</param>398 /// <param name="exception">Exception associated with this event.</param>399 /// <param name="severity">Severity of this event.</param>400 internal static void LogCommandHealthEvent(ExecutionContext executionContext,401 Exception exception,402 Severity severity403 )404 {405 if (executionContext == null)406 {407 PSTraceSource.NewArgumentNullException(nameof(executionContext));408 return;409 }410 411 if (exception == null)412 {413 PSTraceSource.NewArgumentNullException(nameof(exception));414 return;415 }416 417 InvocationInfo invocationInfo = null;418 if (exception is IContainsErrorRecord icer && icer.ErrorRecord != null)419 {420 invocationInfo = icer.ErrorRecord.InvocationInfo;421 }422 423 foreach (LogProvider provider in GetLogProvider(executionContext))424 {425 if (NeedToLogCommandHealthEvent(provider, executionContext))426 {427 provider.LogCommandHealthEvent(GetLogContext(executionContext, invocationInfo, severity), exception);428 }429 }430 }431 432 #endregion433 434 #region Command Lifecycle Event Logging Api435 436 /// <summary>437 /// LogCommandLifecycleEvent: Log a command lifecycle event.438 ///439 /// This is the only form of CommandLifecycleEvent logging api.440 /// </summary>441 /// <param name="executionContext">Execution Context for the current running engine.</param>442 /// <param name="commandState">New command state.</param>443 /// <param name="invocationInfo">Invocation data for current command that is running.</param>444 internal static void LogCommandLifecycleEvent(ExecutionContext executionContext,445 CommandState commandState,446 InvocationInfo invocationInfo)447 {448 if (executionContext == null)449 {450 PSTraceSource.NewArgumentNullException(nameof(executionContext));451 return;452 }453 454 if (invocationInfo == null)455 {456 PSTraceSource.NewArgumentNullException(nameof(invocationInfo));457 return;458 }459 460 if (s_ignoredCommands.Contains(invocationInfo.MyCommand.Name))461 {462 return;463 }464 465 LogContext logContext = null;466 foreach (LogProvider provider in GetLogProvider(executionContext))467 {468 if (NeedToLogCommandLifecycleEvent(provider, executionContext))469 {470 provider.LogCommandLifecycleEvent(471 () => logContext ??= GetLogContext(executionContext, invocationInfo),472 commandState);473 }474 }475 }476 477 /// <summary>478 /// LogCommandLifecycleEvent: Log a command lifecycle event.479 ///480 /// This is a form of CommandLifecycleEvent which takes a commandName instead481 /// of invocationInfo. It is likely that invocationInfo is not available if482 /// the command failed security check.483 /// </summary>484 /// <param name="executionContext">Execution Context for the current running engine.</param>485 /// <param name="commandState">New command state.</param>486 /// <param name="commandName">Current command that is running.</param>487 internal static void LogCommandLifecycleEvent(ExecutionContext executionContext,488 CommandState commandState,489 string commandName)490 {491 if (executionContext == null)492 {493 PSTraceSource.NewArgumentNullException(nameof(executionContext));494 return;495 }496 497 LogContext logContext = null;498 foreach (LogProvider provider in GetLogProvider(executionContext))499 {500 if (NeedToLogCommandLifecycleEvent(provider, executionContext))501 {502 provider.LogCommandLifecycleEvent(503 () =>504 {505 if (logContext == null)506 {507 logContext = GetLogContext(executionContext, null);508 logContext.CommandName = commandName;509 }510 511 return logContext;512 }, commandState);513 }514 }515 }516 517 #endregion518 519 #region Pipeline Execution Detail Event Logging Api520 521 /// <summary>522 /// LogPipelineExecutionDetailEvent: Log a pipeline execution detail event.523 /// </summary>524 /// <param name="executionContext">Execution Context for the current running engine.</param>525 /// <param name="detail">Detail to be logged for this pipeline execution detail.</param>526 /// <param name="invocationInfo">Invocation data for current command that is running.</param>527 internal static void LogPipelineExecutionDetailEvent(ExecutionContext executionContext,528 List<string> detail,529 InvocationInfo invocationInfo)530 531 {532 if (executionContext == null)533 {534 PSTraceSource.NewArgumentNullException(nameof(executionContext));535 return;536 }537 538 foreach (LogProvider provider in GetLogProvider(executionContext))539 {540 if (NeedToLogPipelineExecutionDetailEvent(provider, executionContext))541 {542 provider.LogPipelineExecutionDetailEvent(GetLogContext(executionContext, invocationInfo), detail);543 }544 }545 }546 547 /// <summary>548 /// LogPipelineExecutionDetailEvent: Log a pipeline execution detail event.549 ///550 /// This is a form of PipelineExecutionDetailEvent which takes a scriptName and commandLine551 /// instead of invocationInfo. This will save the need to fill in the commandName for552 /// this event.553 /// </summary>554 /// <param name="executionContext">Execution Context for the current running engine.</param>555 /// <param name="detail">Detail to be logged for this pipeline execution detail.</param>556 /// <param name="scriptName">Script that is currently running.</param>557 /// <param name="commandLine">Command line that is currently running.</param>558 internal static void LogPipelineExecutionDetailEvent(ExecutionContext executionContext,559 List<string> detail,560 string scriptName,561 string commandLine)562 {563 if (executionContext == null)564 {565 PSTraceSource.NewArgumentNullException(nameof(executionContext));566 return;567 }568 569 LogContext logContext = GetLogContext(executionContext, null);570 logContext.CommandLine = commandLine;571 logContext.ScriptName = scriptName;572 573 foreach (LogProvider provider in GetLogProvider(executionContext))574 {575 if (NeedToLogPipelineExecutionDetailEvent(provider, executionContext))576 {577 provider.LogPipelineExecutionDetailEvent(logContext, detail);578 }579 }580 }581 582 #endregion583 584 #region Provider Health Event Logging Api585 586 /// <summary>587 /// LogProviderHealthEvent: Log a Provider health event.588 /// </summary>589 /// <param name="executionContext">Execution context for the engine that is running.</param>590 /// <param name="providerName">Name of the provider.</param>591 /// <param name="exception">Exception associated with this event.</param>592 /// <param name="severity">Severity of this event.</param>593 internal static void LogProviderHealthEvent(ExecutionContext executionContext,594 string providerName,595 Exception exception,596 Severity severity597 )598 {599 if (executionContext == null)600 {601 PSTraceSource.NewArgumentNullException(nameof(executionContext));602 return;603 }604 605 if (exception == null)606 {607 PSTraceSource.NewArgumentNullException(nameof(exception));608 return;609 }610 611 InvocationInfo invocationInfo = null;612 if (exception is IContainsErrorRecord icer && icer.ErrorRecord != null)613 {614 invocationInfo = icer.ErrorRecord.InvocationInfo;615 }616 617 foreach (LogProvider provider in GetLogProvider(executionContext))618 {619 if (NeedToLogProviderHealthEvent(provider, executionContext))620 {621 provider.LogProviderHealthEvent(GetLogContext(executionContext, invocationInfo, severity), providerName, exception);622 }623 }624 }625 626 #endregion627 628 #region Provider Lifecycle Event Logging Api629 630 /// <summary>631 /// LogProviderLifecycleEvent: Log a provider lifecycle event.632 ///633 /// This is the only form of ProviderLifecycleEvent logging api.634 /// </summary>635 /// <param name="executionContext">Execution Context for current engine that is running.</param>636 /// <param name="providerName">Provider name.</param>637 /// <param name="providerState">New provider state.</param>638 internal static void LogProviderLifecycleEvent(ExecutionContext executionContext,639 string providerName,640 ProviderState providerState)641 {642 if (executionContext == null)643 {644 PSTraceSource.NewArgumentNullException(nameof(executionContext));645 return;646 }647 648 foreach (LogProvider provider in GetLogProvider(executionContext))649 {650 if (NeedToLogProviderLifecycleEvent(provider, executionContext))651 {652 provider.LogProviderLifecycleEvent(GetLogContext(executionContext, null), providerName, providerState);653 }654 }655 }656 657 #endregion658 659 #region Settings Event Logging Api660 661 /// <summary>662 /// LogSettingsEvent: Log a settings event663 ///664 /// This is the basic form of LoggingSettingsEvent API. Variation of this function defined665 /// below will make parameter invocationInfo optional.666 /// </summary>667 /// <param name="executionContext">Execution context for current running engine.</param>668 /// <param name="variableName">Variable name.</param>669 /// <param name="newValue">New value for the variable.</param>670 /// <param name="previousValue">Previous value for the variable.</param>671 /// <param name="invocationInfo">Invocation data for the command that is currently running.</param>672 internal static void LogSettingsEvent(ExecutionContext executionContext,673 string variableName,674 string newValue,675 string previousValue,676 InvocationInfo invocationInfo)677 {678 if (executionContext == null)679 {680 PSTraceSource.NewArgumentNullException(nameof(executionContext));681 return;682 }683 684 foreach (LogProvider provider in GetLogProvider(executionContext))685 {686 if (NeedToLogSettingsEvent(provider, executionContext))687 {688 provider.LogSettingsEvent(GetLogContext(executionContext, invocationInfo), variableName, newValue, previousValue);689 }690 }691 }692 693 /// <summary>694 /// This is a variation of basic LogSettingsEvent to make "invocationInfo" optional.695 /// </summary>696 /// <param name="executionContext"></param>697 /// <param name="variableName"></param>698 /// <param name="newValue"></param>699 /// <param name="previousValue"></param>700 internal static void LogSettingsEvent(ExecutionContext executionContext,701 string variableName,702 string newValue,703 string previousValue)704 {705 LogSettingsEvent(executionContext, variableName, newValue, previousValue, null);706 }707 708 #endregion709 710 #region Helper Functions711 712 /// <summary>713 /// Get current engine state for the engine instance corresponding to executionContext714 /// passed in.715 ///716 /// Engine state is stored in ExecutionContext.717 /// </summary>718 /// <param name="executionContext"></param>719 /// <returns></returns>720 private static EngineState GetEngineState(ExecutionContext executionContext)721 {722 return executionContext.EngineState;723 }724 725 /// <summary>726 /// Set current engine state for the engine instance corresponding to executionContext727 /// passed in.728 ///729 /// Engine state is stored in ExecutionContext.730 /// </summary>731 /// <param name="executionContext"></param>732 /// <param name="engineState"></param>733 private static void SetEngineState(ExecutionContext executionContext, EngineState engineState)734 {735 executionContext.EngineState = engineState;736 }737 738 /// <summary>739 /// Generate LogContext structure based on executionContext and invocationInfo passed in.740 ///741 /// LogContext structure is used in log provider interface.742 /// </summary>743 /// <param name="executionContext"></param>744 /// <param name="invocationInfo"></param>745 /// <returns></returns>746 internal static LogContext GetLogContext(ExecutionContext executionContext, InvocationInfo invocationInfo)747 {748 return GetLogContext(executionContext, invocationInfo, Severity.Informational);749 }750 751 /// <summary>752 /// Generate LogContext structure based on executionContext and invocationInfo passed in.753 ///754 /// LogContext structure is used in log provider interface.755 /// </summary>756 /// <param name="executionContext"></param>757 /// <param name="invocationInfo"></param>758 /// <param name="severity"></param>759 /// <returns></returns>760 private static LogContext GetLogContext(ExecutionContext executionContext, InvocationInfo invocationInfo, Severity severity)761 {762 if (executionContext == null)763 return null;764 765 LogContext logContext = new LogContext();766 767 string shellId = executionContext.ShellID;768 769 logContext.ExecutionContext = executionContext;770 logContext.ShellId = shellId;771 logContext.Severity = severity.ToString();772 773 if (executionContext.EngineHostInterface != null)774 {775 logContext.HostName = executionContext.EngineHostInterface.Name;776 logContext.HostVersion = executionContext.EngineHostInterface.Version.ToString();777 logContext.HostId = (string)executionContext.EngineHostInterface.InstanceId.ToString();778 }779 780 logContext.HostApplication = string.Join(' ', Environment.GetCommandLineArgs());781 782 if (executionContext.CurrentRunspace != null)783 {784 logContext.EngineVersion = executionContext.CurrentRunspace.Version.ToString();785 logContext.RunspaceId = executionContext.CurrentRunspace.InstanceId.ToString();786 787 Pipeline currentPipeline = ((RunspaceBase)executionContext.CurrentRunspace).GetCurrentlyRunningPipeline();788 if (currentPipeline != null)789 {790 logContext.PipelineId = currentPipeline.InstanceId.ToString(CultureInfo.CurrentCulture);791 }792 }793 794 logContext.SequenceNumber = NextSequenceNumber;795 796 try797 {798 if (executionContext.LogContextCache.User == null)799 {800 logContext.User = Environment.UserDomainName + "\\" + Environment.UserName;801 executionContext.LogContextCache.User = logContext.User;802 }803 else804 {805 logContext.User = executionContext.LogContextCache.User;806 }807 }808 catch (InvalidOperationException)809 {810 logContext.User = Logging.UnknownUserName;811 }812 813 if (executionContext.SessionState.PSVariable.GetValue("PSSenderInfo") is System.Management.Automation.Remoting.PSSenderInfo psSenderInfo)814 {815 logContext.ConnectedUser = psSenderInfo.UserInfo.Identity.Name;816 }817 818 logContext.Time = DateTime.Now.ToString(CultureInfo.CurrentCulture);819 820 if (invocationInfo == null)821 return logContext;822 823 logContext.ScriptName = invocationInfo.ScriptName;824 logContext.CommandLine = invocationInfo.Line;825 826 if (invocationInfo.MyCommand != null)827 {828 logContext.CommandName = invocationInfo.MyCommand.Name;829 logContext.CommandType = invocationInfo.MyCommand.CommandType.ToString();830 831 switch (invocationInfo.MyCommand.CommandType)832 {833 case CommandTypes.Application:834 logContext.CommandPath = ((ApplicationInfo)invocationInfo.MyCommand).Path;835 break;836 case CommandTypes.ExternalScript:837 logContext.CommandPath = ((ExternalScriptInfo)invocationInfo.MyCommand).Path;838 break;839 }840 }841 842 return logContext;843 }844 845 #endregion846 847 #region Logging Policy848 849 /// <summary>850 /// NeedToLogEngineHealthEvent: check whether logging engine health event is necessary.851 /// Whether to log engine event is controled by session variable "LogEngineHealthEvent"852 /// The default value for this is true (?).853 /// Reading a session variable from execution context for854 /// every single logging call may be expensive. We may need to use a different855 /// approach for this:856 /// a. ExecutionContext will cache the value for variable "LogEngineHealthEvent"857 /// b. If this variable is changed, a notification function will change the cached858 /// value in engine correspondently.859 /// This applies to other logging preference variable also.860 /// </summary>861 /// <param name="logProvider"></param>862 /// <param name="executionContext"></param>863 /// <returns></returns>864 private static bool NeedToLogEngineHealthEvent(LogProvider logProvider, ExecutionContext executionContext)865 {866 if (!logProvider.UseLoggingVariables())867 {868 return true;869 }870 871 return LanguagePrimitives.IsTrue(executionContext.GetVariableValue(SpecialVariables.LogEngineHealthEventVarPath, true));872 }873 874 /// <summary>875 /// NeedToLogEngineLifecycleEvent: check whether logging engine lifecycle event is necessary.876 /// Whether to log engine lifecycle event is controled by session variable "LogEngineLifecycleEvent"877 /// The default value for this is false (?).878 /// </summary>879 /// <param name="logProvider"></param>880 /// <param name="executionContext"></param>881 /// <returns></returns>882 private static bool NeedToLogEngineLifecycleEvent(LogProvider logProvider, ExecutionContext executionContext)883 {884 if (!logProvider.UseLoggingVariables())885 {886 return true;887 }888 889 return LanguagePrimitives.IsTrue(executionContext.GetVariableValue(SpecialVariables.LogEngineLifecycleEventVarPath, true));890 }891 892 /// <summary>893 /// NeedToLogCommandHealthEvent: check whether logging command health event is necessary.894 /// Whether to log command health event is controled by session variable "LogCommandHealthEvent"895 /// The default value for this is false (?).896 /// </summary>897 /// <param name="logProvider"></param>898 /// <param name="executionContext"></param>899 /// <returns></returns>900 private static bool NeedToLogCommandHealthEvent(LogProvider logProvider, ExecutionContext executionContext)901 {902 if (!logProvider.UseLoggingVariables())903 {904 return true;905 }906 907 return LanguagePrimitives.IsTrue(executionContext.GetVariableValue(SpecialVariables.LogCommandHealthEventVarPath, false));908 }909 910 /// <summary>911 /// NeedToLogCommandLifecycleEvent: check whether logging command event is necessary.912 /// Whether to log command lifecycle event is controled by session variable "LogCommandLifecycleEvent"913 /// The default value for this is false (?).914 /// </summary>915 /// <param name="logProvider"></param>916 /// <param name="executionContext"></param>917 /// <returns></returns>918 private static bool NeedToLogCommandLifecycleEvent(LogProvider logProvider, ExecutionContext executionContext)919 {920 if (!logProvider.UseLoggingVariables())921 {922 return true;923 }924 925 return LanguagePrimitives.IsTrue(executionContext.GetVariableValue(SpecialVariables.LogCommandLifecycleEventVarPath, false));926 }927 928 /// <summary>929 /// NeedToLogPipelineExecutionDetailEvent: check whether logging pipeline execution detail event is necessary.930 ///931 /// Whether to log command lifecycle event is controled by PSSnapin set up.932 ///933 /// Should we use session variable "LogPipelineExecutionEvent" to control this also?934 ///935 /// Currently we return true always since pipeline processor already check for whether to log936 /// logic from PSSnapin already. This may need to be changed.937 /// </summary>938 /// <param name="logProvider"></param>939 /// <param name="executionContext"></param>940 /// <returns></returns>941 private static bool NeedToLogPipelineExecutionDetailEvent(LogProvider logProvider, ExecutionContext executionContext)942 {943 if (!logProvider.UseLoggingVariables())944 {945 return true;946 }947 948 return true;949 // return LanguagePrimitives.IsTrue(executionContext.GetVariable("LogPipelineExecutionDetailEvent", false));950 }951 952 /// <summary>953 /// NeedToLogProviderHealthEvent: check whether logging Provider health event is necessary.954 /// Whether to log Provider health event is controled by session variable "LogProviderHealthEvent"955 /// The default value for this is true.956 /// </summary>957 /// <param name="logProvider"></param>958 /// <param name="executionContext"></param>959 /// <returns></returns>960 private static bool NeedToLogProviderHealthEvent(LogProvider logProvider, ExecutionContext executionContext)961 {962 if (!logProvider.UseLoggingVariables())963 {964 return true;965 }966 967 return LanguagePrimitives.IsTrue(executionContext.GetVariableValue(SpecialVariables.LogProviderHealthEventVarPath, true));968 }969 970 /// <summary>971 /// NeedToLogProviderLifecycleEvent: check whether logging Provider lifecycle event is necessary.972 /// Whether to log Provider lifecycle event is controled by session variable "LogProviderLifecycleEvent"973 /// The default value for this is true.974 /// </summary>975 /// <param name="logProvider"></param>976 /// <param name="executionContext"></param>977 /// <returns></returns>978 private static bool NeedToLogProviderLifecycleEvent(LogProvider logProvider, ExecutionContext executionContext)979 {980 if (!logProvider.UseLoggingVariables())981 {982 return true;983 }984 985 return LanguagePrimitives.IsTrue(executionContext.GetVariableValue(SpecialVariables.LogProviderLifecycleEventVarPath, true));986 }987 988 /// <summary>989 /// NeedToLogSettingsEvent: check whether logging settings event is necessary.990 /// Whether to log settings event is controled by session variable "LogSettingsEvent"991 /// The default value for this is false (?).992 /// </summary>993 /// <param name="logProvider"></param>994 /// <param name="executionContext"></param>995 /// <returns></returns>996 private static bool NeedToLogSettingsEvent(LogProvider logProvider, ExecutionContext executionContext)997 {998 if (!logProvider.UseLoggingVariables())999 {1000 return true;1001 }1002 1003 return LanguagePrimitives.IsTrue(executionContext.GetVariableValue(SpecialVariables.LogSettingsEventVarPath, true));1004 }1005 1006 #endregion1007 1008 #region Sequence Id Generator1009 1010 private static int s_nextSequenceNumber = 0;1011 1012 /// <summary>1013 /// Generate next sequence id to be attached to current event.1014 /// </summary>1015 /// <value></value>1016 private static string NextSequenceNumber1017 {1018 get1019 {1020 return Convert.ToString(Interlocked.Increment(ref s_nextSequenceNumber), CultureInfo.CurrentCulture);1021 }1022 }1023 1024 #endregion1025 1026 #region EventId Constants1027 1028 // General health issues.1029 internal const int EVENT_ID_GENERAL_HEALTH_ISSUE = 100;1030 1031 // Dependency. resource not available1032 internal const int EVENT_ID_RESOURCE_NOT_AVAILABLE = 101;1033 // Connectivity. network connection failure1034 internal const int EVENT_ID_NETWORK_CONNECTIVITY_ISSUE = 102;1035 // Settings. fail to set some configuration settings1036 internal const int EVENT_ID_CONFIGURATION_FAILURE = 103;1037 // Performance. system is experiencing some performance issues1038 internal const int EVENT_ID_PERFORMANCE_ISSUE = 104;1039 // Security: system is experiencing some security issues1040 internal const int EVENT_ID_SECURITY_ISSUE = 105;1041 // Workload. system is overloaded.1042 internal const int EVENT_ID_SYSTEM_OVERLOADED = 106;1043 1044 // Beta 1 only -- Unexpected Exception1045 internal const int EVENT_ID_UNEXPECTED_EXCEPTION = 195;1046 1047 #endregion EventId Constants1048 }1049 1050 /// <summary>1051 /// Log context cache.1052 /// </summary>1053 internal class LogContextCache1054 {1055 internal string User { get; set; } = null;1056 }1057 1058 #region Command State and Provider State1059 1060 /// <summary>1061 /// Severity of the event.1062 /// </summary>1063 internal enum Severity1064 {1065 /// <summary>1066 /// Undefined severity.1067 /// </summary>1068 None,1069 /// <summary>1070 /// Critical event causing engine not to work.1071 /// </summary>1072 Critical,1073 1074 /// <summary>1075 /// Error causing engine partially work.1076 /// </summary>1077 Error,1078 1079 /// <summary>1080 /// Problem that may not cause an immediate problem.1081 /// </summary>1082 Warning,1083 1084 /// <summary>1085 /// Informational.1086 /// </summary>1087 Informational1088 }1089 1090 /// <summary>1091 /// Enum for command states.1092 /// </summary>1093 internal enum CommandState1094 {1095 /// <summary>1096 /// </summary>1097 Started = 0,1098 1099 /// <summary>1100 /// </summary>1101 Stopped = 1,1102 1103 /// <summary>1104 /// </summary>1105 Terminated = 21106 }1107 1108 /// <summary>1109 /// Enum for provider states.1110 /// </summary>1111 internal enum ProviderState1112 {1113 /// <summary>1114 /// </summary>1115 Started = 0,1116 1117 /// <summary>1118 /// </summary>1119 Stopped = 1,1120 }1121 1122 #endregion1123}1124 