Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Observability APIs to the SDK and add telemetry provider reference to AWSConfigs and ClientConfig #3344

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions sdk/src/Core/AWSConfigs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
using Amazon.Util.Internal;
using System.Collections.Generic;
using Amazon.Runtime;
using Amazon.Runtime.Telemetry;

namespace Amazon
{
Expand Down Expand Up @@ -90,6 +91,7 @@ public static partial class AWSConfigs
// for reading from awsconfigs.xml
private static object _lock = new object();
private static List<string> standardConfigs = new List<string>() { "region", "logging", "correctForClockSkew" };
private static TelemetryProvider _telemetryProvider = new DefaultTelemetryProvider();

#pragma warning disable 414
private static bool configPresent = true;
Expand Down Expand Up @@ -474,6 +476,19 @@ public static bool UseAlternateUserAgentHeader
set { _rootConfig.UseAlternateUserAgentHeader = value; }
}

/// <summary>
/// Gets or sets the global <see cref="TelemetryProvider"/> instance.
/// <para>
/// This global telemetry provider is used to collect and report telemetry data
/// (such as traces and metrics) for all AWS SDK operations.
/// </para>
/// </summary>
public static TelemetryProvider TelemetryProvider
{
get { return _telemetryProvider; }
set { _telemetryProvider = value; }
}

/// <summary>
/// Configuration for the region endpoint section of AWS configuration.
/// Changes may not take effect until a new client is constructed.
Expand Down
17 changes: 17 additions & 0 deletions sdk/src/Core/Amazon.Runtime/ClientConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
using System.ComponentModel.Design;
using Amazon.Runtime.CredentialManagement;
using Amazon.Runtime.Internal.Settings;
using Amazon.Runtime.Telemetry;

#if NETSTANDARD
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -93,6 +94,7 @@ public abstract partial class ClientConfig : IClientConfig
private const long DefaultMinCompressionSizeBytes = 10240;
private bool didProcessServiceURL = false;
private IAWSTokenProvider _awsTokenProvider = new DefaultAWSTokenProviderChain();
private TelemetryProvider telemetryProvider = AWSConfigs.TelemetryProvider;

private CredentialProfileStoreChain credentialProfileStoreChain;
#if BCL
Expand Down Expand Up @@ -1239,5 +1241,20 @@ public TimeSpan? ReadWriteTimeout
/// but can be changed to use custom user supplied EndpointProvider.
/// </summary>
public IEndpointProvider EndpointProvider { get; set; }

/// <summary>
/// Gets or sets the <see cref="TelemetryProvider"/> instance for this client configuration.
/// <para>
/// This telemetry provider is used to collect and report telemetry data
/// (such as traces and metrics) for operations performed by this specific client.
/// If this property is not explicitly set, it will default to the global
/// <see cref="AWSConfigs.TelemetryProvider"/>.
/// </para>
/// </summary>
public TelemetryProvider TelemetryProvider
{
get { return this.telemetryProvider; }
set { this.telemetryProvider = value; }
}
}
}
11 changes: 11 additions & 0 deletions sdk/src/Core/Amazon.Runtime/IClientConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Amazon.Runtime.Endpoints;
using Amazon.Runtime.Internal.Auth;
using Amazon.Util;
using Amazon.Runtime.Telemetry;
#if NETSTANDARD
using System.Net.Http;
#endif
Expand Down Expand Up @@ -369,6 +370,16 @@ public partial interface IClientConfig
/// which doesn't allow to specify the User-Agent header.
/// </summary>
bool UseAlternateUserAgentHeader { get; }

/// <summary>
/// <para>
/// This telemetry provider is used to collect and report telemetry data
/// (such as traces and metrics) for operations performed by this specific client.
/// If this property is not explicitly set, it will default to the global
/// <see cref="AWSConfigs.TelemetryProvider"/>.
/// </para>
/// </summary>
TelemetryProvider TelemetryProvider { get; }
#if BCL
/// <summary>
/// Gets the TCP keep-alive values to use for service requests. Enabling TCP keep-alive sends periodic TCP keep-alive probe packets, to prevent disconnection due to
Expand Down
93 changes: 93 additions & 0 deletions sdk/src/Core/Amazon.Runtime/Telemetry/Attributes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

using System.Collections.Generic;

namespace Amazon.Runtime.Telemetry
{
/// <summary>
/// Represents a collection of attributes used for telemetry purposes.
/// Attributes are key-value pairs where the key is a string and the value is an object.
/// These attributes provide additional information for spans and metrics in telemetry data.
/// </summary>
public class Attributes
{
private readonly Dictionary<string, object> _attributes;

/// <summary>
/// Initializes a new instance of the <see cref="Attributes"/> class.
/// </summary>
public Attributes()
{
_attributes = new Dictionary<string, object>();
}

/// <summary>
/// Initializes a new instance of the <see cref="Attributes"/> class.
/// If duplicate keys are present in the provided collection, the value of the last occurrence of the key will be used.
/// </summary>
/// <param name="attributes">An optional initial set of attributes to populate the collection.
/// If duplicate keys are found, the last value will override previous ones.</param>
public Attributes(IEnumerable<KeyValuePair<string, object>> attributes)
{
_attributes = new Dictionary<string, object>();

foreach (var kvp in attributes)
{
_attributes[kvp.Key] = kvp.Value;
}
}

/// <summary>
/// Sets the value for the given attribute key.
/// If the key already exists, the value is updated.
/// </summary>
/// <param name="key">The key of the attribute.</param>
/// <param name="value">The value of the attribute.</param>
public void Set(string key, object value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this allow setting null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update it to remove the existing item if the value is null.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not have a Remove method? Having null remove within a Set method is a code smell. It is also less discoverable by a user of this class. Or are you following a spec where this is the expected behavior?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec wasn't very detailed and didn't contain any directions on the Attributes API other than set and get in addition to may contain additional methods
I was trying to match how it is implemented in the ActivityTagsCollection https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityTagsCollection.cs#L57, but we don't have to stick to what they have if we think it is a code smell, will update it.

{
_attributes[key] = value;
}

/// <summary>
/// Gets the value for the given attribute key.
/// </summary>
/// <param name="key">The attribute key.</param>
/// <returns>The attribute value, or null if the key does not exist.</returns>
public object Get(string key)
{
_attributes.TryGetValue(key, out var value);
return value;
}

/// <summary>
/// Removes the attribute with the specified key.
/// </summary>
/// <param name="key">The attribute key.</param>
/// <returns>True if the attribute was successfully removed; otherwise, false.</returns>
public bool Remove(string key)
{
return _attributes.Remove(key);
}

/// <summary>
/// Gets the collection of all the attributes.
/// </summary>
public IEnumerable<KeyValuePair<string, object>> AllAttributes
{
get { return _attributes; }
}
}
}
73 changes: 73 additions & 0 deletions sdk/src/Core/Amazon.Runtime/Telemetry/DefaultTelemetryProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

using Amazon.Runtime.Telemetry.Metrics;
using Amazon.Runtime.Telemetry.Metrics.NoOp;
using Amazon.Runtime.Telemetry.Tracing;
using Amazon.Runtime.Telemetry.Tracing.NoOp;

namespace Amazon.Runtime.Telemetry
{
/// <summary>
/// Default implementation of <see cref="TelemetryProvider"/> that uses no-op providers by default.
/// </summary>
public class DefaultTelemetryProvider : TelemetryProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="DefaultTelemetryProvider"/> class with no-op providers.
/// This setup prevents any telemetry actions from being performed unless explicitly registered using the
/// registration methods.
/// </summary>
public DefaultTelemetryProvider()
{
TracerProvider = new NoOpTracerProvider();
normj marked this conversation as resolved.
Show resolved Hide resolved
MeterProvider = new NoOpMeterProvider();
}

/// <summary>
/// Gets the <see cref="TracerProvider"/> used to create new tracers.
/// This property is initialized with a no-op implementation by default.
/// </summary>
public override TracerProvider TracerProvider { get; protected set; }

/// <summary>
/// Gets the <see cref="MeterProvider"/> used to create new metrics.
/// This property is initialized with a no-op implementation by default.
/// </summary>
public override MeterProvider MeterProvider { get; protected set; }

/// <summary>
/// Registers a new <see cref="TracerProvider"/>.
/// This method sets a custom tracer provider to replace the default no-op provider,
/// enabling the collection of tracing data.
/// </summary>
/// <param name="tracerProvider">The tracer provider to register.</param>
public override void RegisterTracerProvider(TracerProvider tracerProvider)
{
TracerProvider = tracerProvider;
}

/// <summary>
/// Registers a new <see cref="MeterProvider"/>.
/// This method sets a custom meter provider to replace the default no-op provider,
/// enabling the collection of metrics data.
/// </summary>
/// <param name="meterProvider">The meter provider to register.</param>
public override void RegisterMeterProvider(MeterProvider meterProvider)
{
MeterProvider = meterProvider;
}
}
}
34 changes: 34 additions & 0 deletions sdk/src/Core/Amazon.Runtime/Telemetry/Metrics/AsyncMeasurement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

namespace Amazon.Runtime.Telemetry.Metrics
{
/// <summary>
/// Represents an async measurement.
/// An async measurement records values, such as periodically or based on events.
/// The generic type parameter T specifies the type of value being recorded, such as int, long, etc.
Comment on lines +19 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you intend these lines to show up with newlines in intelliSense? They won't without . This same comment applies to other /// comments in this review

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took me a while to understand They won't without . because <para> doesn't appear in the formatted markdown 😅

This is how it looks without <para>
image

And here after adding <para> wrapping every sentence
image

I don't think it looks this bad without the newlines, but I'm open to change it if you think it is better this way, I've seen both ways done in our SDK.
For now I only added it in ClientConfig and AWSConfigs, since the comments there are kinda long.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it looks fine/better without the <para> tags. I was only mentioning if you were trying to format your comment which it looks like you were that it wouldn't show up that way in intelliSense.

/// </summary>
/// <typeparam name="T">The type of value being measured.</typeparam>
public abstract class AsyncMeasurement<T> where T : struct
{
/// <summary>
/// Records a value.
/// This method is called to record a measurement value.
/// </summary>
/// <param name="value">The value to be recorded. This value is of type T.</param>
/// <param name="attributes">Optional attributes associated with the measurement.</param>
public abstract void Record(T value, Attributes attributes = null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

namespace Amazon.Runtime.Telemetry.Metrics
{
/// <summary>
/// Represents a handle for async measurements.
/// The handle is used to manage and stop the async recording of measurements.
/// </summary>
/// <typeparam name="T">The type of value being measured.</typeparam>
public abstract class AsyncMeasurementHandle<T> where T : struct
{
/// <summary>
/// Gets the meter that created this handle.
/// </summary>
public Meter Meter { get; protected set; }

/// <summary>
/// Initializes a new instance of the <see cref="AsyncMeasurementHandle{T}"/> class.
/// </summary>
/// <param name="meter">The meter that created this handle.</param>
protected AsyncMeasurementHandle(Meter meter)
{
Meter = meter;
}
}
}
33 changes: 33 additions & 0 deletions sdk/src/Core/Amazon.Runtime/Telemetry/Metrics/Histogram.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

namespace Amazon.Runtime.Telemetry.Metrics
{
/// <summary>
/// Abstract class representing a histogram used to record values of a specified type.
/// A histogram is a type of metric instrument that tracks the distribution of values.
/// </summary>
/// <typeparam name="T">The type of the histogram value. Must be a struct.</typeparam>
public abstract class Histogram<T> where T : struct
{
/// <summary>
/// Records a value in the histogram.
/// </summary>
/// <param name="value">The value to record. This represents a measurement or observation.</param>
/// <param name="attributes">Optional attributes associated with the measurement. These can be used to
/// provide additional context or dimensions to the recorded value.</param>
public abstract void Record(T value, Attributes attributes = null);
}
}
Loading