diff --git a/src/Common/AzurePSCmdlet.cs b/src/Common/AzurePSCmdlet.cs index dc1668ec86..69db7c5090 100644 --- a/src/Common/AzurePSCmdlet.cs +++ b/src/Common/AzurePSCmdlet.cs @@ -24,9 +24,12 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Linq.Expressions; using System.Management.Automation; using System.Text; using System.Collections.Generic; +using System.Management.Automation.Runspaces; +using System.Collections.ObjectModel; namespace Microsoft.WindowsAzure.Commands.Utilities.Common { @@ -860,5 +863,65 @@ public virtual bool IsTerminatingError(Exception ex) return false; } + //The latest version of Az Wrapper in local. It will be loaded in runtime when the first cmdlet is executed. + //If there is no Az module, the version is "0.0.0" + public static string AzVersion { set; get; } + + //Initialized once AzVersion is loadded. + //Format: AzurePowershell/Az0.0.0;%AZUREPS_HOST_ENVIROMENT% + public static string UserAgent { set; get; } + + protected string LoadAzVersion() + { + Version defautVersion = new Version("0.0.0"); + if (this.Host == null) + { + WriteDebug("Cannot fetch Az version due to no host in current environment"); + return defautVersion.ToString(); + } + + Version latestAz = defautVersion; + string latestSuffix = ""; + using (var powershell = System.Management.Automation.PowerShell.Create()) + { + try + { + powershell.Runspace = RunspaceFactory.CreateRunspace(this.Host); + powershell.AddCommand("Get-Module"); + powershell.AddParameter("Name", "Az"); + powershell.AddParameter("ListAvailable", true); + powershell.Runspace.Open(); + Collection outputs = powershell.Invoke(); + foreach (PSObject obj in outputs) + { + string psVersion = obj.Properties["Version"].Value.ToString(); + int pos = psVersion.IndexOf('-'); + string currentSuffix = (pos == -1 || pos == psVersion.Length - 1) ? "" : psVersion.Substring(pos + 1); + Version currentAz = (pos == -1) ? new Version(psVersion) : new Version(psVersion.Substring(0, pos)); + if (currentAz > latestAz) + { + latestAz = currentAz; + latestSuffix = currentSuffix; + } + else if (currentAz == latestAz) + { + latestSuffix = String.Compare(latestSuffix, currentSuffix) > 0 ? latestSuffix : currentSuffix; + } + } + } + catch (Exception e) + { + WriteDebug(string.Format("Cannot fetch Az version due to exception: {0}", e.Message)); + return defautVersion.ToString(); + } + } + string ret = latestAz.ToString(); + if (!String.IsNullOrEmpty(latestSuffix)) + { + ret += "-" + latestSuffix; + } + WriteDebug(string.Format("Sought all Az modules and got latest version {0}", ret)); + return ret; + } } } diff --git a/src/Common/MetricHelper.cs b/src/Common/MetricHelper.cs index b5e4bd1eed..a7675f023f 100644 --- a/src/Common/MetricHelper.cs +++ b/src/Common/MetricHelper.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Management.Automation.Host; +using System.Net.Http.Headers; using System.Security.Cryptography; using System.Text; @@ -272,12 +273,17 @@ private void PopulatePropertiesFromQos(AzurePSQoSEvent qos, IDictionary CustomProperties { get; private set; } @@ -410,12 +422,17 @@ public void FinishQosEvent() { _timer.Stop(); Duration = _timer.Elapsed; + EndTime = DateTimeOffset.Now; } public override string ToString() { - return string.Format( - "AzureQoSEvent: CommandName - {0}; IsSuccess - {1}; Duration - {2}; Exception - {3};", - CommandName, IsSuccess, Duration, Exception); + string ret = string.Format( + "AzureQoSEvent: CommandName - {0}; IsSuccess - {1}; Duration - {2};", CommandName, IsSuccess, Duration); + if (Exception != null) + { + ret = $"{ret}; Exception - {Exception};"; + } + return ret; } } diff --git a/src/ResourceManager/Version2016_09_01/AzureRMCmdlet.cs b/src/ResourceManager/Version2016_09_01/AzureRMCmdlet.cs index 2b877b8bf3..0cbac1d813 100644 --- a/src/ResourceManager/Version2016_09_01/AzureRMCmdlet.cs +++ b/src/ResourceManager/Version2016_09_01/AzureRMCmdlet.cs @@ -29,6 +29,7 @@ using System.Globalization; using System.Linq; using System.Management.Automation; +using System.Net.Http.Headers; using System.Security.Authentication; using System.Text; @@ -172,7 +173,7 @@ protected override string DataCollectionWarning /// Whether this cmdlet requires default context. /// If false, the logic of referencing default context would be omitted. /// - protected virtual bool RequireDefaultContext => true; + protected virtual bool RequireDefaultContext() { return true; } /// /// Return a default context safely if it is available, without throwing if it is not setup @@ -307,6 +308,17 @@ protected override void InitializeQosEvent() ParameterSetName = this.ParameterSetName }; + if (AzVersion == null) + { + AzVersion = this.LoadAzVersion(); + UserAgent = new ProductInfoHeaderValue("AzurePowershell", string.Format("Az{0}", AzVersion)).ToString(); + string HostEnv = Environment.GetEnvironmentVariable("AZUREPS_HOST_ENVIRONMENT"); + if (!String.IsNullOrWhiteSpace(HostEnv)) + UserAgent += string.Format(";{0}", HostEnv.Trim()); + } + _qosEvent.AzVersion = AzVersion; + _qosEvent.UserAgent = UserAgent; + if (this.MyInvocation != null && !string.IsNullOrWhiteSpace(this.MyInvocation.InvocationName)) { _qosEvent.InvocationName = this.MyInvocation.InvocationName; @@ -321,16 +333,15 @@ protected override void InitializeQosEvent() } IAzureContext context; - if (RequireDefaultContext - && TryGetDefaultContext(out context) - && context.Account != null - && !string.IsNullOrWhiteSpace(context.Account.Id)) + _qosEvent.Uid = "defaultid"; + if (RequireDefaultContext() && TryGetDefaultContext(out context)) { - _qosEvent.Uid = MetricHelper.GenerateSha256HashString(context.Account.Id.ToString()); - } - else - { - _qosEvent.Uid = "defaultid"; + _qosEvent.SubscriptionId = context.Subscription?.Id; + _qosEvent.TenantId = context.Tenant?.Id; + if(context.Account != null && !String.IsNullOrWhiteSpace(context.Account.Id)) + { + _qosEvent.Uid = MetricHelper.GenerateSha256HashString(context.Account.Id.ToString()); + } } } @@ -338,7 +349,7 @@ protected override void LogCmdletStartInvocationInfo() { base.LogCmdletStartInvocationInfo(); IAzureContext context; - if (RequireDefaultContext + if (RequireDefaultContext() && TryGetDefaultContext(out context) && context.Account != null && context.Account.Id != null) @@ -348,13 +359,6 @@ protected override void LogCmdletStartInvocationInfo() } } - protected override void LogCmdletEndInvocationInfo() - { - base.LogCmdletEndInvocationInfo(); - string message = string.Format("{0} end processing.", this.GetType().Name); - WriteDebugWithTimestamp(message); - } - protected override void SetupDebuggingTraces() { ServiceClientTracing.IsEnabled = true; @@ -387,7 +391,7 @@ protected override void BeginProcessing() InitializeEventHandlers(); AzureSession.Instance.ClientFactory.RemoveHandler(typeof(RPRegistrationDelegatingHandler)); IAzureContext context; - if (RequireDefaultContext + if (RequireDefaultContext() && TryGetDefaultContext(out context) && context.Account != null && context.Subscription != null)