Skip to content

Commit dffd9ad

Browse files
authored
Map dictionary values to maps, consistent with Serilog's JsonValueFormatter (#88)
1 parent f18765a commit dffd9ad

File tree

5 files changed

+64
-28
lines changed

5 files changed

+64
-28
lines changed

Build.ps1

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ foreach ($src in Get-ChildItem src/*) {
2424
Write-Output "build: Packaging project in $src"
2525

2626
& dotnet build -c Release --version-suffix=$buildSuffix
27-
if($LASTEXITCODE -ne 0) { exit 1 }
27+
if($LASTEXITCODE -ne 0) { throw "Failed" }
2828

2929
if($suffix) {
3030
& dotnet pack -c Release --include-source --no-build -o ../../artifacts --version-suffix=$suffix
3131
} else {
3232
& dotnet pack -c Release --include-source --no-build -o ../../artifacts
3333
}
34-
if($LASTEXITCODE -ne 0) { exit 1 }
34+
if($LASTEXITCODE -ne 0) { throw "Failed" }
3535

3636
Pop-Location
3737
}
@@ -42,7 +42,7 @@ foreach ($test in Get-ChildItem test/*.Tests) {
4242
Write-Output "build: Testing project in $test"
4343

4444
& dotnet test -c Release
45-
if($LASTEXITCODE -ne 0) { exit 3 }
45+
if($LASTEXITCODE -ne 0) { throw "Failed" }
4646

4747
Pop-Location
4848
}
@@ -53,7 +53,7 @@ foreach ($test in ls test/*.PerformanceTests) {
5353
Write-Output "build: Building performance test project in $test"
5454

5555
& dotnet build -c Release
56-
if($LASTEXITCODE -ne 0) { exit 2 }
56+
if($LASTEXITCODE -ne 0) { throw "Failed" }
5757

5858
Pop-Location
5959
}

src/Serilog.Sinks.OpenTelemetry/Sinks/OpenTelemetry/ProtocolHelpers/PrimitiveConversions.cs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,17 @@ public static AnyValue ToOpenTelemetryMap(StructureValue value)
120120
var map = new AnyValue();
121121
var kvList = new KeyValueList();
122122
map.KvlistValue = kvList;
123+
124+
// Per the OTLP protos, attribute keys MUST be unique.
125+
var seen = new HashSet<string>();
126+
123127
foreach (var prop in value.Properties)
124128
{
129+
if (seen.Contains(prop.Name))
130+
continue;
131+
132+
seen.Add(prop.Name);
133+
125134
var v = ToOpenTelemetryAnyValue(prop.Value);
126135
var kv = new KeyValue
127136
{
@@ -134,29 +143,24 @@ public static AnyValue ToOpenTelemetryMap(StructureValue value)
134143
return map;
135144
}
136145

137-
public static AnyValue ToOpenTelemetryArray(DictionaryValue value)
146+
public static AnyValue ToOpenTelemetryMap(DictionaryValue value)
138147
{
139-
var array = new AnyValue();
140-
var values = new ArrayValue();
141-
array.ArrayValue = values;
148+
var map = new AnyValue();
149+
var kvList = new KeyValueList();
150+
map.KvlistValue = kvList;
151+
142152
foreach (var element in value.Elements)
143153
{
144-
var v = new AnyValue();
145-
var kvList = new KeyValueList();
146-
v.KvlistValue = kvList;
147-
kvList.Values.Add(new KeyValue
148-
{
149-
Key = "Key",
150-
Value = ToOpenTelemetryAnyValue(element.Key)
151-
});
154+
var k = element.Key.Value?.ToString() ?? "null";
155+
var v = ToOpenTelemetryAnyValue(element.Value);
152156
kvList.Values.Add(new KeyValue
153157
{
154-
Key = "Value",
155-
Value = ToOpenTelemetryAnyValue(element.Value)
158+
Key = k,
159+
Value = v
156160
});
157-
values.Values.Add(v);
158161
}
159-
return array;
162+
163+
return map;
160164
}
161165

162166
public static AnyValue ToOpenTelemetryArray(SequenceValue value)
@@ -177,9 +181,9 @@ internal static AnyValue ToOpenTelemetryAnyValue(LogEventPropertyValue value)
177181
return value switch
178182
{
179183
ScalarValue scalar => ToOpenTelemetryScalar(scalar),
180-
StructureValue map => ToOpenTelemetryMap(map),
181-
SequenceValue array => ToOpenTelemetryArray(array),
182-
DictionaryValue d => ToOpenTelemetryArray(d),
184+
StructureValue structure => ToOpenTelemetryMap(structure),
185+
SequenceValue sequence => ToOpenTelemetryArray(sequence),
186+
DictionaryValue dictionary => ToOpenTelemetryMap(dictionary),
183187
_ => ToOpenTelemetryPrimitive(value.ToString()),
184188
};
185189
}

test/Serilog.Sinks.OpenTelemetry.Tests/PrimitiveConversionsTests.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,4 +285,37 @@ public void TestMd5Hash()
285285
Assert.Equal(PrimitiveConversions.Md5Hash("alpha"), PrimitiveConversions.Md5Hash("alpha"));
286286
Assert.NotEqual(PrimitiveConversions.Md5Hash("alpha"), PrimitiveConversions.Md5Hash("beta"));
287287
}
288-
}
288+
289+
[Fact]
290+
public void DictionariesMapToMaps()
291+
{
292+
var dict = new DictionaryValue(new[]
293+
{
294+
new KeyValuePair<ScalarValue, LogEventPropertyValue>(new ScalarValue(0), new ScalarValue("test"))
295+
});
296+
297+
var any = PrimitiveConversions.ToOpenTelemetryAnyValue(dict);
298+
299+
Assert.NotNull(any.KvlistValue);
300+
var value = Assert.Single(any.KvlistValue.Values);
301+
Assert.Equal("0", value.Key);
302+
Assert.Equal("test", value.Value.StringValue);
303+
}
304+
305+
[Fact]
306+
public void StructureKeysAreDeduplicated()
307+
{
308+
var structure = new StructureValue(new[]
309+
{
310+
new LogEventProperty("a", new ScalarValue("test")),
311+
new LogEventProperty("a", new ScalarValue("test")),
312+
new LogEventProperty("b", new ScalarValue("test"))
313+
});
314+
315+
Assert.Equal(3, structure.Properties.Count);
316+
317+
var any = PrimitiveConversions.ToOpenTelemetryAnyValue(structure);
318+
319+
Assert.Equal(2, any.KvlistValue.Values.Count);
320+
}
321+
}

test/Serilog.Sinks.OpenTelemetry.Tests/PublicApiVisibilityTests.approved.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
{
55
public static Serilog.LoggerConfiguration OpenTelemetry(this Serilog.Configuration.LoggerAuditSinkConfiguration loggerAuditSinkConfiguration, System.Action<Serilog.Sinks.OpenTelemetry.OpenTelemetrySinkOptions> configure) { }
66
public static Serilog.LoggerConfiguration OpenTelemetry(this Serilog.Configuration.LoggerSinkConfiguration loggerSinkConfiguration, System.Action<Serilog.Sinks.OpenTelemetry.BatchedOpenTelemetrySinkOptions> configure) { }
7-
public static Serilog.LoggerConfiguration OpenTelemetry(this Serilog.Configuration.LoggerAuditSinkConfiguration loggerAuditSinkConfiguration, string endpoint = "http://localhost:4317/v1/logs", Serilog.Sinks.OpenTelemetry.OtlpProtocol protocol = 0) { }
8-
public static Serilog.LoggerConfiguration OpenTelemetry(this Serilog.Configuration.LoggerSinkConfiguration loggerSinkConfiguration, string endpoint = "http://localhost:4317/v1/logs", Serilog.Sinks.OpenTelemetry.OtlpProtocol protocol = 0) { }
7+
public static Serilog.LoggerConfiguration OpenTelemetry(this Serilog.Configuration.LoggerAuditSinkConfiguration loggerAuditSinkConfiguration, string endpoint = "http://localhost:4317", Serilog.Sinks.OpenTelemetry.OtlpProtocol protocol = 0) { }
8+
public static Serilog.LoggerConfiguration OpenTelemetry(this Serilog.Configuration.LoggerSinkConfiguration loggerSinkConfiguration, string endpoint = "http://localhost:4317", Serilog.Sinks.OpenTelemetry.OtlpProtocol protocol = 0) { }
99
}
1010
}
1111
namespace Serilog.Sinks.OpenTelemetry

test/Serilog.Sinks.OpenTelemetry.Tests/RequiredResourceAttributeTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ public void MissingServiceNameDefaultsToExecutableName()
2525
{
2626
var actual = RequiredResourceAttributes.AddDefaults(new Dictionary<string, object>());
2727

28-
// .exe on Windows; true for all of our current targets, but this test may need some tuning in the future.
29-
Assert.StartsWith("unknown_service:dotnet", (string)actual["service.name"]);
28+
Assert.StartsWith("unknown_service:", (string)actual["service.name"]);
3029
}
3130

3231
[Fact]

0 commit comments

Comments
 (0)