Skip to content

Commit e8642f8

Browse files
authored
feat(csharp/src/Telemetry): re-enable compile-time JSON serializer context for trace activity (#4013)
Re-enables compile time JSON serializer context for the SerializableActivity class. - The context include a number of well-known data types - The serializer now includes an option to handle unknown types with the default, reflection-based serialization
1 parent 7b38cf4 commit e8642f8

File tree

4 files changed

+269
-2
lines changed

4 files changed

+269
-2
lines changed

csharp/src/Telemetry/Traces/Exporters/FileExporter/FileExporter.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.IO;
2222
using System.Text;
2323
using System.Text.Json;
24+
using System.Text.Json.Serialization.Metadata;
2425
using System.Threading;
2526
using System.Threading.Channels;
2627
using System.Threading.Tasks;
@@ -38,6 +39,12 @@ internal class FileExporter : BaseExporter<Activity>
3839

3940
private static readonly ConcurrentDictionary<string, Lazy<FileExporterInstance>> s_fileExporters = new();
4041
private static readonly byte[] s_newLine = Encoding.UTF8.GetBytes(Environment.NewLine);
42+
private static readonly JsonSerializerOptions s_serializerOptions = new()
43+
{
44+
TypeInfoResolver = JsonTypeInfoResolver.Combine(
45+
SerializableActivitySerializerContext.Default,
46+
new DefaultJsonTypeInfoResolver())
47+
};
4148

4249
private readonly TracingFile _tracingFile;
4350
private readonly string _fileBaseName;
@@ -151,7 +158,9 @@ private static async Task ProcessActivitiesAsync(FileExporter fileExporter, Canc
151158
SerializableActivity serializableActivity = new(activity);
152159
await JsonSerializer.SerializeAsync(
153160
stream,
154-
serializableActivity, cancellationToken: cancellationToken).ConfigureAwait(false);
161+
serializableActivity,
162+
s_serializerOptions,
163+
cancellationToken: cancellationToken).ConfigureAwait(false);
155164
stream.Write(s_newLine, 0, s_newLine.Length);
156165
stream.Position = 0;
157166

csharp/src/Telemetry/Traces/Listeners/FileListener/ActivityProcessor.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.IO;
2121
using System.Text;
2222
using System.Text.Json;
23+
using System.Text.Json.Serialization.Metadata;
2324
using System.Threading;
2425
using System.Threading.Channels;
2526
using System.Threading.Tasks;
@@ -29,6 +30,12 @@ namespace Apache.Arrow.Adbc.Telemetry.Traces.Listeners.FileListener
2930
internal sealed class ActivityProcessor : IDisposable
3031
{
3132
private static readonly byte[] s_newLine = Encoding.UTF8.GetBytes(Environment.NewLine);
33+
private static readonly JsonSerializerOptions s_serializerOptions = new()
34+
{
35+
TypeInfoResolver = JsonTypeInfoResolver.Combine(
36+
SerializableActivitySerializerContext.Default,
37+
new DefaultJsonTypeInfoResolver())
38+
};
3239
private Task? _processingTask;
3340
private readonly Channel<Activity> _channel;
3441
private readonly Func<Stream, CancellationToken, Task> _streamWriterFunc;
@@ -91,7 +98,9 @@ private async Task ProcessActivitiesAsync(CancellationToken cancellationToken)
9198
SerializableActivity serializableActivity = new(activity);
9299
await JsonSerializer.SerializeAsync(
93100
stream,
94-
serializableActivity, cancellationToken: cancellationToken).ConfigureAwait(false);
101+
serializableActivity,
102+
s_serializerOptions,
103+
cancellationToken: cancellationToken).ConfigureAwait(false);
95104
stream.Write(s_newLine, 0, s_newLine.Length);
96105
stream.Position = 0;
97106

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
using System;
19+
using System.Text.Json.Serialization;
20+
21+
namespace Apache.Arrow.Adbc.Telemetry.Traces.Listeners.FileListener
22+
{
23+
[JsonSerializable(typeof(SerializableActivity))]
24+
25+
[JsonSerializable(typeof(string))]
26+
[JsonSerializable(typeof(byte))]
27+
[JsonSerializable(typeof(sbyte))]
28+
[JsonSerializable(typeof(ushort))]
29+
[JsonSerializable(typeof(short))]
30+
[JsonSerializable(typeof(uint))]
31+
[JsonSerializable(typeof(int))]
32+
[JsonSerializable(typeof(ulong))]
33+
[JsonSerializable(typeof(long))]
34+
[JsonSerializable(typeof(ulong))]
35+
[JsonSerializable(typeof(float))]
36+
[JsonSerializable(typeof(double))]
37+
[JsonSerializable(typeof(decimal))]
38+
[JsonSerializable(typeof(char))]
39+
[JsonSerializable(typeof(bool))]
40+
41+
[JsonSerializable(typeof(string[]))]
42+
[JsonSerializable(typeof(byte[]))]
43+
[JsonSerializable(typeof(sbyte[]))]
44+
[JsonSerializable(typeof(ushort[]))]
45+
[JsonSerializable(typeof(short[]))]
46+
[JsonSerializable(typeof(uint[]))]
47+
[JsonSerializable(typeof(int[]))]
48+
[JsonSerializable(typeof(ulong[]))]
49+
[JsonSerializable(typeof(long[]))]
50+
[JsonSerializable(typeof(ulong[]))]
51+
[JsonSerializable(typeof(float[]))]
52+
[JsonSerializable(typeof(double[]))]
53+
[JsonSerializable(typeof(decimal[]))]
54+
[JsonSerializable(typeof(char[]))]
55+
[JsonSerializable(typeof(bool[]))]
56+
57+
[JsonSerializable(typeof(Uri))]
58+
internal partial class SerializableActivitySerializerContext : JsonSerializerContext
59+
{
60+
}
61+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
using System.Diagnostics;
19+
using System.Text;
20+
using System.Text.Json;
21+
using System.Text.Json.Serialization.Metadata;
22+
using Apache.Arrow.Adbc.Telemetry.Traces.Listeners.FileListener;
23+
using Xunit.Abstractions;
24+
25+
namespace Apache.Arrow.Adbc.Tests.Telemetry.Traces.Listeners.FileListener
26+
{
27+
public class SerializableActivityTests
28+
{
29+
private readonly ITestOutputHelper _output;
30+
31+
public class SerializableActivityTestData : TheoryData<Activity>
32+
{
33+
public SerializableActivityTestData()
34+
{
35+
var activityWithTags = new Activity("TestActivityWithTags");
36+
int index = 0;
37+
38+
activityWithTags.AddTag("key" + index++, "value1");
39+
activityWithTags.AddTag("key" + index++, (sbyte)123);
40+
activityWithTags.AddTag("key" + index++, (byte)123);
41+
activityWithTags.AddTag("key" + index++, (short)123);
42+
activityWithTags.AddTag("key" + index++, (ushort)123);
43+
activityWithTags.AddTag("key" + index++, (int)123);
44+
activityWithTags.AddTag("key" + index++, (uint)123);
45+
activityWithTags.AddTag("key" + index++, (long)123);
46+
activityWithTags.AddTag("key" + index++, (ulong)123);
47+
activityWithTags.AddTag("key" + index++, (float)123);
48+
activityWithTags.AddTag("key" + index++, (double)123);
49+
activityWithTags.AddTag("key" + index++, (decimal)123);
50+
activityWithTags.AddTag("key" + index++, true);
51+
activityWithTags.AddTag("key" + index++, 'A');
52+
53+
activityWithTags.AddTag("key" + index++, new string[] { "val1" });
54+
activityWithTags.AddTag("key" + index++, new byte[] { 123 });
55+
activityWithTags.AddTag("key" + index++, new sbyte[] { 123 });
56+
activityWithTags.AddTag("key" + index++, new ushort[] { 123 });
57+
activityWithTags.AddTag("key" + index++, new short[] { 123 });
58+
activityWithTags.AddTag("key" + index++, new uint[] { 123 });
59+
activityWithTags.AddTag("key" + index++, new int[] { 123 });
60+
activityWithTags.AddTag("key" + index++, new ulong[] { 123 });
61+
activityWithTags.AddTag("key" + index++, new long[] { 123 });
62+
activityWithTags.AddTag("key" + index++, new float[] { 123 });
63+
activityWithTags.AddTag("key" + index++, new double[] { 123 });
64+
activityWithTags.AddTag("key" + index++, new decimal[] { 123 });
65+
activityWithTags.AddTag("key" + index++, new bool[] { true });
66+
activityWithTags.AddTag("key" + index++, new char[] { 'A' });
67+
68+
activityWithTags.AddTag("key" + index++, new string[] { "val1" }.AsEnumerable());
69+
activityWithTags.AddTag("key" + index++, new byte[] { 123 }.AsEnumerable());
70+
activityWithTags.AddTag("key" + index++, new sbyte[] { 123 }.AsEnumerable());
71+
activityWithTags.AddTag("key" + index++, new ushort[] { 123 }.AsEnumerable());
72+
activityWithTags.AddTag("key" + index++, new short[] { 123 }.AsEnumerable());
73+
activityWithTags.AddTag("key" + index++, new uint[] { 123 }.AsEnumerable());
74+
activityWithTags.AddTag("key" + index++, new int[] { 123 }.AsEnumerable());
75+
activityWithTags.AddTag("key" + index++, new ulong[] { 123 }.AsEnumerable());
76+
activityWithTags.AddTag("key" + index++, new long[] { 123 }.AsEnumerable());
77+
activityWithTags.AddTag("key" + index++, new float[] { 123 }.AsEnumerable());
78+
activityWithTags.AddTag("key" + index++, new double[] { 123 }.AsEnumerable());
79+
activityWithTags.AddTag("key" + index++, new decimal[] { 123 }.AsEnumerable());
80+
activityWithTags.AddTag("key" + index++, new bool[] { true }.AsEnumerable());
81+
activityWithTags.AddTag("key" + index++, new char[] { 'A' }.AsEnumerable());
82+
83+
activityWithTags.AddTag("key" + index++, new Uri("http://example.com"));
84+
Add(activityWithTags);
85+
}
86+
}
87+
88+
public SerializableActivityTests(ITestOutputHelper output)
89+
{
90+
_output = output;
91+
}
92+
93+
[Fact]
94+
public async Task CannnotSerializeAnonymousObjectWithSerializerContext()
95+
{
96+
Activity activity = new Activity("activity");
97+
using (activity.Start())
98+
{
99+
activity.AddTag("key1", new { Field1 = "value1" });
100+
SerializableActivity serializableActivity = new(activity);
101+
var stream = new MemoryStream();
102+
var serializerOptions = new JsonSerializerOptions
103+
{
104+
WriteIndented = true,
105+
IncludeFields = true,
106+
TypeInfoResolver = SerializableActivitySerializerContext.Default,
107+
};
108+
await Assert.ThrowsAnyAsync<Exception>(async () => await JsonSerializer.SerializeAsync(
109+
stream,
110+
serializableActivity,
111+
serializerOptions));
112+
}
113+
}
114+
115+
[Theory]
116+
[ClassData(typeof(SerializableActivityTestData))]
117+
public async Task CanSerializeWithNoDefaultTypeInfoResolver(Activity activity)
118+
{
119+
using (activity.Start())
120+
{
121+
SerializableActivity serializableActivity = new(activity);
122+
var stream = new MemoryStream();
123+
var serializerOptions = new JsonSerializerOptions
124+
{
125+
WriteIndented = true,
126+
IncludeFields = true,
127+
TypeInfoResolver = SerializableActivitySerializerContext.Default,
128+
};
129+
await JsonSerializer.SerializeAsync(
130+
stream,
131+
serializableActivity,
132+
serializerOptions);
133+
Assert.NotNull(stream);
134+
_output.WriteLine("Serialized Activity: {0}", Encoding.UTF8.GetString(stream.ToArray()));
135+
}
136+
}
137+
138+
[Theory]
139+
[ClassData(typeof(SerializableActivityTestData))]
140+
public async Task CanSerializeWithDefaultTypeInfoResolver(Activity activity)
141+
{
142+
using (activity.Start())
143+
{
144+
SerializableActivity serializableActivity = new(activity);
145+
var stream = new MemoryStream();
146+
var serializerOptions = new JsonSerializerOptions
147+
{
148+
WriteIndented = true,
149+
IncludeFields = true,
150+
TypeInfoResolver = JsonTypeInfoResolver.Combine(
151+
SerializableActivitySerializerContext.Default,
152+
new DefaultJsonTypeInfoResolver()),
153+
};
154+
await JsonSerializer.SerializeAsync(
155+
stream,
156+
serializableActivity,
157+
serializerOptions);
158+
Assert.NotNull(stream);
159+
_output.WriteLine("Serialized Activity: {0}", Encoding.UTF8.GetString(stream.ToArray()));
160+
}
161+
}
162+
163+
[Fact]
164+
public async Task CanSerializeAnonymousObjectWithDefaultTypeInfoResolver()
165+
{
166+
Activity activity = new Activity("activity");
167+
using (activity.Start())
168+
{
169+
activity.AddTag("key1", new { Field1 = "value1" });
170+
SerializableActivity serializableActivity = new(activity);
171+
var stream = new MemoryStream();
172+
var serializerOptions = new JsonSerializerOptions
173+
{
174+
WriteIndented = true,
175+
IncludeFields = true,
176+
TypeInfoResolver = JsonTypeInfoResolver.Combine(
177+
SerializableActivitySerializerContext.Default,
178+
new DefaultJsonTypeInfoResolver()),
179+
};
180+
await JsonSerializer.SerializeAsync(
181+
stream,
182+
serializableActivity,
183+
serializerOptions);
184+
_output.WriteLine("Serialized Activity: {0}", Encoding.UTF8.GetString(stream.ToArray()));
185+
}
186+
}
187+
}
188+
}

0 commit comments

Comments
 (0)