Skip to content

Commit f6ed260

Browse files
authored
Merge pull request #168 from dingmeng-xue/telemetry
Add Az version, time, and subscription id to client telemetry
2 parents e527f5d + 58ddee7 commit f6ed260

File tree

4 files changed

+91
-29
lines changed

4 files changed

+91
-29
lines changed

src/Common/AzurePSCmdlet.cs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@
1515
using Microsoft.ApplicationInsights;
1616
using Microsoft.Azure.Commands.Common.Authentication;
1717
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
18-
using Microsoft.WindowsAzure.Commands.Common.CustomAttributes;
1918
using Microsoft.Azure.ServiceManagement.Common.Models;
2019
using Microsoft.WindowsAzure.Commands.Common;
21-
using Microsoft.WindowsAzure.Commands.Utilities.Common;
20+
using Microsoft.WindowsAzure.Commands.Common.CustomAttributes;
2221
using System;
2322
using System.Collections.Concurrent;
23+
using System.Collections.ObjectModel;
2424
using System.Diagnostics;
2525
using System.IO;
2626
using System.Linq;
2727
using System.Management.Automation;
28+
using System.Management.Automation.Runspaces;
2829
using System.Text;
29-
using System.Collections.Generic;
3030

3131
namespace Microsoft.WindowsAzure.Commands.Utilities.Common
3232
{
@@ -860,5 +860,50 @@ public virtual bool IsTerminatingError(Exception ex)
860860

861861
return false;
862862
}
863+
//The latest version of Az Wrapper in local. It will be loaded in runtime when the first cmdlet is executed.
864+
//If there is no Az module, the version is "0.0.0"
865+
public static string AzVersion { set; get; }
866+
867+
//Initialized once AzVersion is loadded.
868+
//Format: AzurePowershell/Az0.0.0;%AZUREPS_HOST_ENVIROMENT%
869+
public static string UserAgent { set; get; }
870+
871+
protected string LoadAzVersion()
872+
{
873+
Version latestAz = new Version("0.0.0");
874+
string latestSuffix = "";
875+
using (var powershell = System.Management.Automation.PowerShell.Create())
876+
{
877+
powershell.Runspace = RunspaceFactory.CreateRunspace(this.Host);
878+
powershell.AddCommand("Get-Module");
879+
powershell.AddParameter("Name", "Az");
880+
powershell.AddParameter("ListAvailable", true);
881+
powershell.Runspace.Open();
882+
Collection<PSObject> outputs = powershell.Invoke();
883+
foreach (PSObject obj in outputs)
884+
{
885+
string psVersion = obj.Properties["Version"].Value.ToString();
886+
int pos = psVersion.IndexOf('-');
887+
string currentSuffix = (pos == -1 || pos == psVersion.Length - 1) ? "" : psVersion.Substring(pos + 1);
888+
Version currentAz = (pos == -1) ? new Version(psVersion) : new Version(psVersion.Substring(0, pos));
889+
if (currentAz > latestAz)
890+
{
891+
latestAz = currentAz;
892+
latestSuffix = currentSuffix;
893+
}
894+
else if (currentAz == latestAz)
895+
{
896+
latestSuffix = String.Compare(latestSuffix, currentSuffix) > 0 ? latestSuffix : currentSuffix;
897+
}
898+
}
899+
}
900+
string ret = latestAz.ToString();
901+
if (!String.IsNullOrEmpty(latestSuffix))
902+
{
903+
ret += "-" + latestSuffix;
904+
}
905+
WriteDebug(string.Format("Sought all Az modules and got latest version {0}", ret));
906+
return ret;
907+
}
863908
}
864909
}

src/Common/AzurePowerShell.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@
1414

1515
using System;
1616
using System.IO;
17-
using System.Net.Http.Headers;
1817

1918
namespace Microsoft.WindowsAzure.Commands.Common
2019
{
2120
public class AzurePowerShell
2221
{
2322
public const string AssemblyCompany = "Microsoft";
2423

25-
public const string AssemblyProduct = "Microsoft Azure Powershell";
24+
public const string AssemblyProduct = "Microsoft Azure PowerShell";
2625

2726
public const string AssemblyCopyright = "Copyright © Microsoft";
2827

@@ -38,10 +37,6 @@ public class AzurePowerShell
3837

3938
public const string TokenCacheFile = "TokenCache.dat";
4039

41-
public static ProductInfoHeaderValue UserAgentValue = new ProductInfoHeaderValue(
42-
"AzurePowershell",
43-
string.Format("Az{0}", AzurePowerShell.AssemblyVersion));
44-
4540
public static string ProfileDirectory = Path.Combine(
4641
#if NETSTANDARD
4742
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),

src/Common/MetricHelper.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
using System.Collections.Generic;
2525
using System.Diagnostics;
2626
using System.Management.Automation.Host;
27+
using System.Net.Http.Headers;
2728
using System.Security.Cryptography;
2829
using System.Text;
2930

@@ -272,12 +273,17 @@ private void PopulatePropertiesFromQos(AzurePSQoSEvent qos, IDictionary<string,
272273
eventProperties.Add("CommandParameters", qos.Parameters);
273274
eventProperties.Add("UserId", qos.Uid);
274275
eventProperties.Add("x-ms-client-request-id", qos.ClientRequestId);
275-
eventProperties.Add("UserAgent", AzurePowerShell.UserAgentValue.ToString());
276+
eventProperties.Add("UserAgent", qos.UserAgent);
276277
eventProperties.Add("HashMacAddress", HashMacAddress);
277278
eventProperties.Add("PowerShellVersion", PSVersion);
278-
eventProperties.Add("Version", AzurePowerShell.AssemblyVersion);
279+
eventProperties.Add("Version", qos.AzVersion);
279280
eventProperties.Add("CommandParameterSetName", qos.ParameterSetName);
280281
eventProperties.Add("CommandInvocationName", qos.InvocationName);
282+
eventProperties.Add("start-time", qos.StartTime.ToUniversalTime().ToString("o"));
283+
eventProperties.Add("end-time", qos.EndTime.ToUniversalTime().ToString("o"));
284+
eventProperties.Add("duration", qos.Duration.ToString("c"));
285+
eventProperties.Add("subscription-id", qos.SubscriptionId);
286+
eventProperties.Add("tenant-id", qos.TenantId);
281287

282288
if (qos.InputFromPipeline != null)
283289
{
@@ -371,19 +377,25 @@ public class AzurePSQoSEvent
371377
private readonly Stopwatch _timer;
372378

373379
public DateTimeOffset StartTime { get; set; }
380+
public DateTimeOffset EndTime { get; set; }
374381
public TimeSpan Duration { get; set; }
375382
public bool IsSuccess { get; set; }
376383
public string CommandName { get; set; }
377384
public string ModuleName { get; set; }
378385
public string ModuleVersion { get; set; }
379386
public string HostVersion { get; set; }
387+
public string AzVersion { get; set; }
388+
public string UserAgent { get; set; }
380389
public string Parameters { get; set; }
381390
public bool? InputFromPipeline { get; set; }
382391
public bool? OutputToPipeline { get; set; }
383392
public Exception Exception { get; set; }
384393
public string Uid { get; set; }
385394
public string ClientRequestId { get; set; }
386395
public string SessionId { get; set; }
396+
public string SubscriptionId { get; set; }
397+
public string TenantId { get; set; }
398+
387399
public string ParameterSetName { get; set; }
388400
public string InvocationName { get; set; }
389401
public Dictionary<string, string> CustomProperties { get; private set; }
@@ -410,12 +422,17 @@ public void FinishQosEvent()
410422
{
411423
_timer.Stop();
412424
Duration = _timer.Elapsed;
425+
EndTime = DateTimeOffset.Now;
413426
}
414427

415428
public override string ToString()
416429
{
417-
return string.Format(
418-
"AzureQoSEvent: CommandName - {0}; IsSuccess - {1}; Duration - {2}; Exception - {3};",
419-
CommandName, IsSuccess, Duration, Exception);
430+
string ret = string.Format(
431+
"AzureQoSEvent: CommandName - {0}; IsSuccess - {1}; Duration - {2};", CommandName, IsSuccess, Duration);
432+
if (Exception != null)
433+
{
434+
ret = $"{ret}; Exception - {Exception};";
435+
}
436+
return ret;
420437
}
421438
}

src/ResourceManager/Version2016_09_01/AzureRMCmdlet.cs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
using System.Globalization;
3030
using System.Linq;
3131
using System.Management.Automation;
32+
using System.Net.Http.Headers;
3233
using System.Security.Authentication;
3334
using System.Text;
3435

@@ -295,6 +296,17 @@ protected override void InitializeQosEvent()
295296
ParameterSetName = this.ParameterSetName
296297
};
297298

299+
if (AzVersion == null)
300+
{
301+
AzVersion = this.LoadAzVersion();
302+
UserAgent = new ProductInfoHeaderValue("AzurePowershell", string.Format("Az{0}", AzVersion)).ToString();
303+
string hostEnv = Environment.GetEnvironmentVariable("AZUREPS_HOST_ENVIRONMENT");
304+
if (!String.IsNullOrWhiteSpace(hostEnv))
305+
UserAgent += string.Format(";{0}", hostEnv.Trim());
306+
}
307+
_qosEvent.AzVersion = AzVersion;
308+
_qosEvent.UserAgent = UserAgent;
309+
298310
if (this.MyInvocation != null && !string.IsNullOrWhiteSpace(this.MyInvocation.InvocationName))
299311
{
300312
_qosEvent.InvocationName = this.MyInvocation.InvocationName;
@@ -309,15 +321,15 @@ protected override void InitializeQosEvent()
309321
}
310322

311323
IAzureContext context;
312-
if (TryGetDefaultContext(out context)
313-
&& context.Account != null
314-
&& !string.IsNullOrWhiteSpace(context.Account.Id))
315-
{
316-
_qosEvent.Uid = MetricHelper.GenerateSha256HashString(context.Account.Id.ToString());
317-
}
318-
else
324+
_qosEvent.Uid = "defaultid";
325+
if (TryGetDefaultContext(out context))
319326
{
320-
_qosEvent.Uid = "defaultid";
327+
_qosEvent.SubscriptionId = context.Subscription?.Id;
328+
_qosEvent.TenantId = context.Tenant?.Id;
329+
if(context.Account != null && !String.IsNullOrWhiteSpace(context.Account.Id))
330+
{
331+
_qosEvent.Uid = MetricHelper.GenerateSha256HashString(context.Account.Id.ToString());
332+
}
321333
}
322334
}
323335

@@ -334,13 +346,6 @@ protected override void LogCmdletStartInvocationInfo()
334346
}
335347
}
336348

337-
protected override void LogCmdletEndInvocationInfo()
338-
{
339-
base.LogCmdletEndInvocationInfo();
340-
string message = string.Format("{0} end processing.", this.GetType().Name);
341-
WriteDebugWithTimestamp(message);
342-
}
343-
344349
protected override void SetupDebuggingTraces()
345350
{
346351
ServiceClientTracing.IsEnabled = true;

0 commit comments

Comments
 (0)