Skip to content

[API Proposal]: new ActivitySource.CreateActivity overload #103245

Open
@Wraith2

Description

@Wraith2

Background and motivation

When creating an activity using ActivitySource there are currently two overloads available which allow you to pass tags which will be available to samplers.

public Activity? CreateActivity(
	string! name, 
	ActivityKind kind, 
	ActivityContext parentContext, 
	IEnumerable<KeyValuePair<string!, object?>>? tags = null, 
	IEnumerable<ActivityLink>? links = null, 
	ActivityIdFormat idFormat = ActivityIdFormat.Unknown
);

public Activity? CreateActivity(
	string! name, 
	ActivityKind kind, 
	string? parentId, 
	IEnumerable<KeyValuePair<string!, object?>>? tags = null, 
	IEnumerable<ActivityLink>? links = null, 
	ActivityIdFormat idFormat = ActivityIdFormat.Unknown
);

If you want to pass a single tag to the sampler there isn't a good way to do it. without excessive allocations.

  • If you use a TagList you'll find that there is no single value constructor and that because it is a struct which contains 8 valuetype fields it is expensive to box.
  • You can't stackalloc because the KeyValuePair<string, object?> type contains references.
  • You can't rent an array and pass a span because ReadOnlySpan<> is a ref struct, doesn't implement enumerable and can't be boxed.
  • If you rent an array and use an ArraySegment<> it'll box the array segment because it's a struct.

The easiest thing to do is a simple single value array allocation and if you want to amortize it you'll have to manage the array pooling yourself. It would be good to have a lower allocation path to create the activity in this case because the activity is being created for every incoming request.

This came up while trying to extend OpenTelemetry TelemetryHttpModule open-telemetry/opentelemetry-dotnet-contrib#1871 (comment)

API Proposal

namespace System.Diagnostics;

public class ActivitySource
{
	public Activity? CreateActivity(
		string! name, 
		ActivityKind kind, 
		ActivityContext parentContext, 
		KeyValuePair<string!, object?>? tags = default, 
		IEnumerable<ActivityLink>? links = null, 
		ActivityIdFormat idFormat = ActivityIdFormat.Unknown
	);
}

API Usage

ActivitySource source = new ActivitySource("name");
ActivityContext context = new ActivityContext();
Activity? activity = source.CreateActivity("activity", ActivityKind.Server, context, new KeyValuePair<string,object?>("tagName","tagValue"));

Alternative Designs

If it possible to provide a way to call one of the existing overloads and avoiding per call allocations which will be discarded then that may be acceptable.

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions