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

Experiment: ResourceValue as struct #378

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion samples/LoggingTracer/LoggingTracer/LoggingExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public override Task ShutdownAsync(CancellationToken cancellationToken)

private static string StringifyResource(Resource resource)
{
return string.Join(", ", resource.Labels.Select(l => l.Value));
return string.Join(", ", resource.Attributes.Select(l => l.Value));
}

private static void AppendIndentedLine(StringBuilder sb, int indentationLevel, string line)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ option java_outer_classname = "ResourceProto";

// Resource information.
message Resource {
// Set of labels that describe the resource.
map<string,string> labels = 1;
// Set of attributes that describe the resource.
map<string,string> attributes = 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using OpenTelemetry.Resources;

namespace OpenTelemetry.Exporter.Zipkin.Implementation
{
Expand Down Expand Up @@ -137,7 +138,7 @@ internal Builder Shared(bool val)
return this;
}

internal Builder PutTag(string key, string value)
internal Builder PutTag(ResourceKey key, string value)
{
if (this.result.Tags == null)
{
Expand All @@ -149,7 +150,7 @@ internal Builder PutTag(string key, string value)
throw new ArgumentNullException(nameof(key));
}

this.result.Tags[key] = value ?? throw new ArgumentNullException(nameof(value));
this.result.Tags[key.ToString()] = value ?? throw new ArgumentNullException(nameof(value));

return this;
}
Expand Down
6 changes: 3 additions & 3 deletions src/OpenTelemetry.Exporter.Zipkin/ZipkinTraceExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@ internal ZipkinSpan GenerateSpan(Span otelSpan, ZipkinEndpoint defaultLocalEndpo
string serviceName = string.Empty;
string serviceNamespace = string.Empty;

foreach (var label in otelSpan.LibraryResource.Labels)
foreach (var attribute in otelSpan.LibraryResource.Attributes)
{
string key = label.Key;
string val = label.Value;
var key = attribute.Key;
string val = attribute.Value;

if (key == "service.name")
{
Expand Down
51 changes: 23 additions & 28 deletions src/OpenTelemetry/Resources/Resource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,27 @@ public class Resource
/// <summary>
/// Maximum length of the resource type name.
/// </summary>
private const int MaxResourceTypeNameLength = 255;
internal const int MaxResourceTypeNameLength = 255;

/// <summary>
/// Creates a new <see cref="Resource"/>.
/// </summary>
/// <param name="labels">An <see cref="IDictionary{String, String}"/> of labels that describe the resource.</param>
public Resource(IEnumerable<KeyValuePair<string, string>> labels)
/// <param name="attributes">An <see cref="IDictionary{ResourceKey, String}"/> of attributes that describe the resource.</param>
public Resource(IEnumerable<KeyValuePair<ResourceKey, string>> attributes)
{
ValidateLabels(labels);
this.Labels = labels;
ValidateAttributes(attributes);
this.Attributes = attributes;
}

/// <summary>
/// Gets an empty Resource.
/// </summary>
public static Resource Empty { get; } = new Resource(Enumerable.Empty<KeyValuePair<string, string>>());
public static Resource Empty { get; } = new Resource(Enumerable.Empty<KeyValuePair<ResourceKey, string>>());

/// <summary>
/// Gets the collection of key-value pairs describing the resource.
/// </summary>
public IEnumerable<KeyValuePair<string, string>> Labels { get; }
public IEnumerable<KeyValuePair<ResourceKey, string>> Attributes { get; }

/// <summary>
/// Returns a new, merged <see cref="Resource"/> by merging the current <see cref="Resource"/> with the.
Expand All @@ -61,56 +61,51 @@ public Resource(IEnumerable<KeyValuePair<string, string>> labels)
/// <returns><see cref="Resource"/>.</returns>
public Resource Merge(Resource other)
{
var newLabels = new Dictionary<string, string>();
var newAttributes = new Dictionary<ResourceKey, string>();

foreach (var label in this.Labels)
foreach (var attribute in this.Attributes)
{
if (!newLabels.TryGetValue(label.Key, out var value) || string.IsNullOrEmpty(value))
if (!newAttributes.TryGetValue(attribute.Key, out var value) || string.IsNullOrEmpty(value))
{
newLabels[label.Key] = label.Value;
newAttributes[attribute.Key] = attribute.Value;
}
}

if (other != null)
{
foreach (var label in other.Labels)
foreach (var attribute in other.Attributes)
{
if (!newLabels.TryGetValue(label.Key, out var value) || string.IsNullOrEmpty(value))
if (!newAttributes.TryGetValue(attribute.Key, out var value) || string.IsNullOrEmpty(value))
{
newLabels[label.Key] = label.Value;
newAttributes[attribute.Key] = attribute.Value;
}
}
}

return new Resource(newLabels);
return new Resource(newAttributes);
}

private static void ValidateLabels(IEnumerable<KeyValuePair<string, string>> labels)
private static void ValidateAttributes(IEnumerable<KeyValuePair<ResourceKey, string>> attributes)
{
if (labels == null)
if (attributes == null)
{
throw new ArgumentNullException(nameof(labels));
throw new ArgumentNullException(nameof(attributes));
}

foreach (var label in labels)
foreach (var attribute in attributes)
{
if (!IsValidAndNotEmpty(label.Key))
if (!attribute.Key.IsValid())
{
throw new ArgumentException($"Label key should be a string with a length greater than 0 and not exceeding {MaxResourceTypeNameLength} characters.");
throw new ArgumentException($"Attribute key should be a string with a length greater than 0 and not exceeding {MaxResourceTypeNameLength} characters.");
}

if (!IsValid(label.Value))
if (!IsValid(attribute.Value))
{
throw new ArgumentException($"Label value should be a string with a length not exceeding {MaxResourceTypeNameLength} characters.");
throw new ArgumentException($"Attribute value should be a string with a length not exceeding {MaxResourceTypeNameLength} characters.");
}
}
}

private static bool IsValidAndNotEmpty(string name)
{
return !string.IsNullOrEmpty(name) && IsValid(name);
}

private static bool IsValid(string name)
{
return name != null && name.Length <= MaxResourceTypeNameLength && StringUtil.IsPrintableString(name);
Expand Down
102 changes: 102 additions & 0 deletions src/OpenTelemetry/Resources/ResourceKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// <copyright file="ResourceKey.cs" company="OpenTelemetry Authors">
// Copyright 2018, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License 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.
// </copyright>

using System;
using System.Diagnostics;
using System.Globalization;
using OpenTelemetry.Utils;

namespace OpenTelemetry.Resources
{
public readonly struct ResourceKey : IEquatable<ResourceKey>
{
private readonly int intValue;
private readonly double doubleValue;
private readonly string stringValue;
private readonly Type type;

private ResourceKey(Type type, int intValue = 0, double doubleValue = 0, string stringValue = null)
{
this.type = type;
this.intValue = intValue;
this.doubleValue = doubleValue;
this.stringValue = stringValue;
}

public static implicit operator ResourceKey(int value) => new ResourceKey(typeof(int), value);

public static implicit operator ResourceKey(double value) =>
new ResourceKey(typeof(double), doubleValue: value);

public static implicit operator ResourceKey(string value) =>
new ResourceKey(typeof(string), stringValue: value);

public static bool operator ==(ResourceKey left, ResourceKey right) => left.Equals(right);

public static bool operator !=(ResourceKey left, ResourceKey right) => !left.Equals(right);

public bool Equals(ResourceKey other)
=> this.intValue == other.intValue
&& this.doubleValue == other.doubleValue
&& this.stringValue == other.stringValue
&& Equals(this.type, other.type);

public override bool Equals(object obj) => obj is ResourceKey other && this.Equals(other);

public override int GetHashCode()
{
unchecked
{
var hashCode = this.intValue;
hashCode = (hashCode * 397) ^ this.doubleValue.GetHashCode();
hashCode = (hashCode * 397) ^ (this.stringValue != null ? this.stringValue.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (this.type != null ? this.type.GetHashCode() : 0);
return hashCode;
}
}

public override string ToString()
{
if (this.type == typeof(int))
{
return this.intValue.ToString(CultureInfo.InvariantCulture);
}

if (this.type == typeof(double))
{
return this.doubleValue.ToString(CultureInfo.InvariantCulture);
}

if (this.type == typeof(string))
{
return this.stringValue;
}

Debug.Fail("private ctor, only supported types are defined as implicit operators.");
return null;
}

internal bool IsValid()
=> this != default
// TODO: Any validation for int, double, bool?
// && this.type != typeof(int) || this.intValue == 0
// && this.type != typeof(double) || this.intValue == 0
&& (this.type != typeof(string)
|| !string.IsNullOrWhiteSpace(this.stringValue)
|| this.stringValue.Length > Resource.MaxResourceTypeNameLength
|| !StringUtil.IsPrintableString(this.stringValue));
}
}
10 changes: 5 additions & 5 deletions src/OpenTelemetry/Trace/Configuration/TracerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private TracerFactory(TracerBuilder builder)
}
else if (builder.ProcessingPipelines.Count == 1)
{
// if there is only one pipeline - use it's outer processor as a
// if there is only one pipeline - use it's outer processor as a
// single processor on the tracer.
var processorFactory = builder.ProcessingPipelines[0];
this.spanProcessor = processorFactory.Build();
Expand Down Expand Up @@ -156,15 +156,15 @@ public void Dispose()
}
}

private static IEnumerable<KeyValuePair<string, string>> CreateLibraryResourceLabels(string name, string version)
private static IEnumerable<KeyValuePair<ResourceKey, string>> CreateLibraryResourceLabels(string name, string version)
{
var labels = new Dictionary<string, string> { { "name", name } };
var attributes = new Dictionary<ResourceKey, string> { { "name", name } };
if (!string.IsNullOrEmpty(version))
{
labels.Add("version", version);
attributes.Add("version", version);
}

return labels;
return attributes;
}

private readonly struct TracerRegistryKey
Expand Down