Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
MshLog.cs1124 linesDownload Raw Back to logging
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