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 DistributedContext implementation and Delete ITag* Types #366

Merged
merged 5 commits into from
Dec 6, 2019
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
7 changes: 3 additions & 4 deletions samples/Exporters/TestApplicationInsights.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,19 @@ namespace Samples
{
internal class TestApplicationInsights
{
private static readonly ITagger Tagger = Tags.Tagger;

private static readonly string FrontendKey = "my.org/keys/frontend";

internal static object Run()
{
var tagContextBuilder = Tagger.CurrentBuilder.Put(FrontendKey, "mobile-ios9.3.5");
DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; // Enable asynclocal carrier for the context
DistributedContext dc = new DistributedContext(FrontendKey, "mobile-ios9.3.5");

using (var tracerFactory = TracerFactory.Create(builder => builder
.UseApplicationInsights(config => config.InstrumentationKey = "instrumentation-key")))
{
var tracer = tracerFactory.GetTracer("application-insights-test");

using (tagContextBuilder.BuildScoped())
using (DistributedContext.SetCurrent(dc))
using (tracer.StartActiveSpan("incoming request", out var span))
{
span.AddEvent("Start processing video.");
Expand Down
2 changes: 0 additions & 2 deletions samples/Exporters/TestPrometheus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ namespace Samples
{
internal class TestPrometheus
{
private static readonly ITagger Tagger = Tags.Tagger;

internal static object Run()
{
var promOptions = new PrometheusExporterOptions() { Url = "http://localhost:9184/metrics/" };
Expand Down
7 changes: 3 additions & 4 deletions samples/Exporters/TestStackdriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ namespace Samples
{
internal class TestStackdriver
{
private static readonly ITagger Tagger = Tags.Tagger;

private static readonly string FrontendKey = "my_org/keys/frontend";

internal static object Run(string projectId)
Expand All @@ -37,9 +35,10 @@ internal static object Run(string projectId)
{
var tracer = tracerFactory.GetTracer("stackdriver-test");

var tagContextBuilder = Tagger.CurrentBuilder.Put(FrontendKey, "mobile-ios9.3.5");
DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; // Enable asynclocal carrier for the context
DistributedContext dc = new DistributedContext(FrontendKey, "mobile-ios9.3.5");

using (tagContextBuilder.BuildScoped())
using (DistributedContext.SetCurrent(dc))
{
using (tracer.StartActiveSpan("incoming request", out var span))
{
Expand Down
114 changes: 106 additions & 8 deletions src/OpenTelemetry.Api/DistributedContext/DistributedContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization.Formatters;

namespace OpenTelemetry.Context
{
/// <summary>
/// Distributed context.
/// </summary>
public readonly struct DistributedContext
public readonly struct DistributedContext : IEquatable<DistributedContext>
{
private static DistributedContextCarrier carrier = NoopDistributedContextCarrier.Instance;
private static List<DistributedContextEntry> emptyList = new List<DistributedContextEntry>();
private readonly IEnumerable<DistributedContextEntry> entries;

/// <summary>
Expand All @@ -33,13 +36,92 @@ public readonly struct DistributedContext
/// <param name="entries">Entries for distributed context.</param>
public DistributedContext(IEnumerable<DistributedContextEntry> entries)
{
this.entries = entries;
if (carrier is NoopDistributedContextCarrier || entries is null || entries.Count() == 0)
{
this.entries = emptyList;
}
else
{
// Filter the default and duplicate entries.
List<DistributedContextEntry> list = new List<DistributedContextEntry>(entries.Count());
for (int i = 0; i < entries.Count(); i++)
{
DistributedContextEntry entry = entries.ElementAt(i);
if (entry == default)
{
continue;
}

int j;
for (j = entries.Count() - 1; j > i; j--)
{
if (entry.Key == entries.ElementAt(j).Key)
{
break;
}
}

if (j <= i)
{
list.Add(entry);
}
}

this.entries = list;
}
}

/// <summary>
/// Initializes a new instance of the <see cref="DistributedContext"/> struct.
/// </summary>
/// <param name="key">The key of the context entry.</param>
/// <param name="value">The value of the context entry.</param>
public DistributedContext(string key, string value)
tarekgh marked this conversation as resolved.
Show resolved Hide resolved
{
if (key is null || value is null)
{
throw new ArgumentNullException(key is null ? nameof(key) : nameof(value));
}

this.entries = carrier is NoopDistributedContextCarrier ? emptyList : new List<DistributedContextEntry>(1) { new DistributedContextEntry(key, value) };
}

/// <summary>
/// Initializes a new instance of the <see cref="DistributedContext"/> struct.
/// </summary>
/// <param name="entry">The distributed context entry.</param>
public DistributedContext(DistributedContextEntry entry)
{
this.entries = carrier is NoopDistributedContextCarrier || entry == default ? emptyList : new List<DistributedContextEntry>(1) { entry };
}

/// <summary>
/// Gets empty object of <see cref="DistributedContext"/> struct.
/// </summary>
public static DistributedContext Empty { get; } = new DistributedContext(emptyList);

/// <summary>
/// Gets the current <see cref="DistributedContext"/>.
/// </summary>
public static DistributedContext Current { get; /* TODO: to be implemented */ }
public static DistributedContext Current => carrier.Current;

/// <summary>
/// Gets or sets the default carrier instance of the <see cref="DistributedContextCarrier"/> class.
/// SDK will need to override the value to AsyncLocalDistributedContextCarrier.Instance.
/// </summary>
public static DistributedContextCarrier Carrier
{
get => carrier;
set
{
if (value is null)
SergeyKanzhelev marked this conversation as resolved.
Show resolved Hide resolved
{
throw new ArgumentNullException(nameof(value));
}

carrier = value;
}
}

/// <summary>
/// Gets all the <see cref="DistributedContextEntry"/> in this <see cref="DistributedContext"/>.
Expand All @@ -51,16 +133,32 @@ public DistributedContext(IEnumerable<DistributedContextEntry> entries)
/// </summary>
/// <param name="context">Context to set as current.</param>
/// <returns>Scope object. On disposal - original context will be restored.</returns>
public static IDisposable SetCurrent(in DistributedContext context)
{
/* TODO: to be implemented */ return null;
}
public static IDisposable SetCurrent(in DistributedContext context) => carrier.SetCurrent(context);

/// <summary>
/// Gets the <see cref="DistributedContextEntry"/> with the specified name.
/// </summary>
/// <param name="key">Name of the <see cref="DistributedContextEntry"/> to get.</param>
/// <returns>The <see cref="DistributedContextEntry"/> with the specified name. If not found - null.</returns>
public string GetEntryValue(string key) => this.entries.FirstOrDefault(x => x.Key == key)?.Value;
public string GetEntryValue(string key) => this.entries.LastOrDefault(x => x.Key == key).Value;

/// <inheritdoc/>
public bool Equals(DistributedContext other)
{
if (this.entries.Count() != other.entries.Count())
{
return false;
}

foreach (DistributedContextEntry entry in this.entries)
{
if (other.GetEntryValue(entry.Key) != entry.Value)
{
return false;
}
}

return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="ITagContextBinarySerializer.cs" company="OpenTelemetry Authors">
// <copyright file="DistributedContextCarrier.cs" company="OpenTelemetry Authors">
// Copyright 2018, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -14,25 +14,25 @@
// limitations under the License.
// </copyright>

namespace OpenTelemetry.Context.Propagation
using System;

namespace OpenTelemetry.Context
{
/// <summary>
/// Binary serializer and deserializer of tags.
/// Abstraction to the carrier of the DistributedContext current object.
/// </summary>
public interface ITagContextBinarySerializer
public abstract class DistributedContextCarrier
{
/// <summary>
/// Converts tags into byte array.
/// Gets the current <see cref="DistributedContext"/>.
/// </summary>
/// <param name="tags">Tags to serialize.</param>
/// <returns>Binary representation of tags.</returns>
byte[] ToByteArray(ITagContext tags);
public abstract DistributedContext Current { get; }

/// <summary>
/// Deserialize tags from byte array.
/// Sets the current <see cref="DistributedContext"/>.
/// </summary>
/// <param name="bytes">Bytes to deserialize.</param>
/// <returns>Tags deserialized from bytes.</returns>
ITagContext FromByteArray(byte[] bytes);
/// <param name="context">Context to set as current.</param>
/// <returns>Scope object. On disposal - original context will be restored.</returns>
public abstract IDisposable SetCurrent(in DistributedContext context);
}
}
57 changes: 38 additions & 19 deletions src/OpenTelemetry.Api/DistributedContext/DistributedContextEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ namespace OpenTelemetry.Context
/// <summary>
/// Distributed Context entry with the key, value and metadata.
/// </summary>
public sealed class DistributedContextEntry
public readonly struct DistributedContextEntry
{
/// <summary>
/// Initializes a new instance of the <see cref="DistributedContextEntry"/> class with the key and value.
/// Initializes a new instance of the <see cref="DistributedContextEntry"/> struct with the key and value.
/// </summary>
/// <param name="key">Key name for the entry.</param>
/// <param name="key">Key name for the entry.</param>
/// <param name="value">Value associated with the key name.</param>
public DistributedContextEntry(string key, string value)
: this(key, value, EntryMetadata.NoPropagationEntry)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="DistributedContextEntry"/> class with the key and value.
/// Initializes a new instance of the <see cref="DistributedContextEntry"/> struct with the key, value, and metadata.
/// </summary>
/// <param name="key">Key name for the entry.</param>
/// <param name="value">Value associated with the key name.</param>
Expand All @@ -61,36 +61,55 @@ public DistributedContextEntry(string key, string value, in EntryMetadata metada
/// </summary>
public EntryMetadata Metadata { get; }

/// <inheritdoc/>
public override string ToString()
{
return nameof(DistributedContextEntry)
+ "{"
+ nameof(this.Key) + "=" + this.Key + ", "
+ nameof(this.Value) + "=" + this.Value
+ "}";
}
/// <summary>
/// Compare two entries of <see cref="DistributedContextEntry"/> for equality.
/// </summary>
/// <param name="entry1">First Entry to compare.</param>
/// <param name="entry2">Second Entry to compare.</param>
public static bool operator ==(DistributedContextEntry entry1, DistributedContextEntry entry2) => entry1.Equals(entry2);

/// <summary>
/// Compare two entries of <see cref="DistributedContextEntry"/> for not equality.
/// </summary>
/// <param name="entry1">First Entry to compare.</param>
/// <param name="entry2">Second Entry to compare.</param>
public static bool operator !=(DistributedContextEntry entry1, DistributedContextEntry entry2) => !entry1.Equals(entry2);

/// <inheritdoc/>
public override bool Equals(object o)
{
if (o == this)
if (o is DistributedContextEntry that)
{
return true;
return this.Key == that.Key && this.Value == that.Value;
}

if (o is DistributedContextEntry that)
return false;
}

/// <inheritdoc/>
public override string ToString()
{
if (this.Key is null)
{
return this.Key.Equals(that.Key)
&& this.Value.Equals(that.Value);
return "{}";
}

return false;
return nameof(DistributedContextEntry)
+ "{"
+ nameof(this.Key) + "=" + this.Key + ", "
+ nameof(this.Value) + "=" + this.Value
+ "}";
}

/// <inheritdoc/>
public override int GetHashCode()
{
if (this.Key is null)
{
// Default instance
return 0;
}

var h = 1;
h *= 1000003;
h ^= this.Key.GetHashCode();
Expand Down
Loading