Skip to content

Commit

Permalink
Add metadata telemetry support (#1632)
Browse files Browse the repository at this point in the history
* Add metadata telemetry support

* Rename variables

* Update summary

* Update Add and Update methods

* Address comments

* Update version.

* Remove throwing

* Add InternalsVisible To SAL

* Fix tests
  • Loading branch information
RojaEnnam authored Apr 26, 2021
1 parent d6f2b66 commit 876c625
Show file tree
Hide file tree
Showing 11 changed files with 422 additions and 14 deletions.
2 changes: 1 addition & 1 deletion buildConfiguration.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<dotnetArchitecture>x64</dotnetArchitecture>
<nugetVersion>3.5.0-rc-1285</nugetVersion>
<runtimes>net45,net461,netstandard2.0</runtimes>
<assemblyVersion>6.10.1</assemblyVersion>
<assemblyVersion>6.11.0</assemblyVersion>
<nugetSuffix>preview</nugetSuffix>
<projects>
<src>
Expand Down
153 changes: 153 additions & 0 deletions src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
// All rights reserved.
//
// This code is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Reflection;
using System.Net.Http;

namespace Microsoft.IdentityModel.Logging
{
/// <summary>
/// Provides a way to add and remove telemetry data.
/// </summary>
public static class IdentityModelTelemetryUtil
{
internal const string skuTelemetry = "x-client-SKU";
internal const string versionTelemetry = "x-client-Ver";
internal static readonly List<string> defaultTelemetryValues = new List<string> { skuTelemetry, versionTelemetry };
internal static readonly ConcurrentDictionary<string, string> telemetryData = new ConcurrentDictionary<string, string>()
{
[skuTelemetry] = ClientSku,
[versionTelemetry] = ClientVer
};

/// <summary>
/// Get the string that represents the client SKU.
/// </summary>
public static string ClientSku =>
#if NET45
"ID_NET45";
#elif NET461
"ID_NET461";
#elif NET472
"ID_NET472";
#elif NETSTANDARD2_0
"ID_NETSTANDARD2_0";
#endif

/// <summary>
/// Get the string that represents the client version.
/// </summary>
public static string ClientVer => typeof(IdentityModelTelemetryUtil).GetTypeInfo().Assembly.GetName().Version.ToString();

/// <summary>
/// Adds a key and its value to the collection of telemetry data.
/// </summary>
/// <param name="key"> The name of the telemetry.</param>
/// <param name="value"> The value of the telemetry.</param>
/// <returns> true if the key is successfully added; otherwise, false.</returns>
public static bool AddTelemetryData(string key, string value)
{
if (string.IsNullOrEmpty(key))
{
LogHelper.LogArgumentNullException(nameof(key));
return false;
}

if (string.IsNullOrEmpty(value))
{
LogHelper.LogArgumentNullException(nameof(value));
return false;
}

if (defaultTelemetryValues.Contains(key))
{
LogHelper.LogExceptionMessage(new ArgumentException(LogMessages.MIML10003));
return false;
}

telemetryData[key] = value;
return true;
}

/// <summary>
/// Removes a key and its value from the collection of telemetry data.
/// </summary>
/// <param name="key"> The name of the telemetry.</param>
/// <returns> true if the key is successfully removed; otherwise, false.</returns>
public static bool RemoveTelemetryData(string key)
{
if (string.IsNullOrEmpty(key))
{
LogHelper.LogArgumentNullException(nameof(key));
return false;
}

if (defaultTelemetryValues.Contains(key))
{
LogHelper.LogExceptionMessage(new ArgumentException(LogMessages.MIML10003));
return false;
}

return telemetryData.TryRemove(key, out _);
}

internal static void SetTelemetryData(HttpRequestMessage request)
{
if (request == null)
return;

foreach (var parameter in telemetryData)
{
// remove this header if it already exists.
// we don't want to add an additional value in case when a telemetry header already exists, but to overwrite it.
request.Headers.Remove(parameter.Key);
request.Headers.Add(parameter.Key, parameter.Value);
}
}

internal static bool UpdateDefaultTelemetryData(string key, string value)
{
if (string.IsNullOrEmpty(key))
{
LogHelper.LogArgumentNullException(nameof(key));
return false;
}

if (string.IsNullOrEmpty(value))
{
LogHelper.LogArgumentNullException(nameof(value));
return false;
}

telemetryData[key] = value;
return true;
}
}
}
32 changes: 32 additions & 0 deletions src/Microsoft.IdentityModel.Logging/InternalsVisibleTo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
// All rights reserved.
//
// This code is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//------------------------------------------------------------------------------

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.S2S, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tokens, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.S2S.Tokens.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
1 change: 1 addition & 0 deletions src/Microsoft.IdentityModel.Logging/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ internal static class LogMessages
internal const string MIML10000 = "MIML10000: eventData.Payload is null or empty. Not logging any messages.";
internal const string MIML10001 = "MIML10001: Cannot create the fileStream or StreamWriter to write logs. See inner exception.";
internal const string MIML10002 = "MIML10002: Unknown log level: {0}.";
internal const string MIML10003 = "MIML10003: Sku and version telemetry cannot be manipulated. They are added by default.";
#pragma warning restore 1591

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' Or '$(TargetFramework)' == 'net461' Or '$(TargetFramework)' == 'net472'">
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyInformationalVersion("0.0.1")]
Expand All @@ -35,3 +36,6 @@
[assembly: AssemblyVersion("0.0.1")]
[assembly: CLSCompliant(true)]
[assembly: ComVisible(false)]

[assembly: InternalsVisibleTo("Microsoft.IdentityModel.Protocols, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("Microsoft.IdentityModel.Logging.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
Original file line number Diff line number Diff line change
Expand Up @@ -540,16 +540,7 @@ public string Sid
/// <summary>
/// Gets the string that is sent as telemetry data in an OpenIdConnectMessage.
/// </summary>
public string SkuTelemetryValue { get; set; } =
#if NET45
"ID_NET45";
#elif NET461
"ID_NET461";
#elif NET472
"ID_NET472";
#elif NETSTANDARD2_0
"ID_NETSTANDARD2_0";
#endif
public string SkuTelemetryValue { get; set; } = IdentityModelTelemetryUtil.ClientSku;

/// <summary>
/// Gets or sets 'state'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ public class HttpDocumentRetriever : IDocumentRetriever
private HttpClient _httpClient;
private static readonly HttpClient _defaultHttpClient = new HttpClient();

/// <summary>
/// Gets or sets whether additional default headers are added to a <see cref="HttpRequestMessage"/> headers. Set to true by default.
/// </summary>
public static bool DefaultSendAdditionalHeaderData { get; set; } = true;

private bool _sendAdditionalHeaderData = DefaultSendAdditionalHeaderData;

/// <summary>
/// Gets or sets whether additional headers are added to a <see cref="HttpRequestMessage"/> headers
/// </summary>
public bool SendAdditionalHeaderData
{
get { return _sendAdditionalHeaderData; }
set { _sendAdditionalHeaderData = value; }
}

/// <summary>
/// Initializes a new instance of the <see cref="HttpDocumentRetriever"/> class.
/// </summary>
Expand Down Expand Up @@ -83,14 +99,21 @@ public async Task<string> GetDocumentAsync(string address, CancellationToken can
throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX20108, address), nameof(address)));

Exception unsuccessfulHttpResponseException;
HttpResponseMessage response;
try
{
LogHelper.LogVerbose(LogMessages.IDX20805, address);
var httpClient = _httpClient ?? _defaultHttpClient;
var uri = new Uri(address, UriKind.RelativeOrAbsolute);
var response = await httpClient.GetAsync(uri, cancel).ConfigureAwait(false);
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
using (var message = new HttpRequestMessage(HttpMethod.Get, uri))
{
if (SendAdditionalHeaderData)
IdentityModelTelemetryUtil.SetTelemetryData(message);

response = await httpClient.SendAsync(message).ConfigureAwait(false);
}

var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.IsSuccessStatusCode)
return responseContent;

Expand Down
Loading

0 comments on commit 876c625

Please sign in to comment.