This is an automated email from the ASF dual-hosted git repository.

curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new e8642f824 feat(csharp/src/Telemetry): re-enable compile-time JSON 
serializer context for trace activity (#4013)
e8642f824 is described below

commit e8642f824393a99d4a2ac3cde89f15b10e7f3101
Author: Bruce Irschick <[email protected]>
AuthorDate: Tue Feb 24 09:19:53 2026 -0800

    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
---
 .../Traces/Exporters/FileExporter/FileExporter.cs  |  11 +-
 .../Listeners/FileListener/ActivityProcessor.cs    |  11 +-
 .../SerializableActivitySerializerContext.cs       |  61 +++++++
 .../FileListener/SerializableActivityTests.cs      | 188 +++++++++++++++++++++
 4 files changed, 269 insertions(+), 2 deletions(-)

diff --git a/csharp/src/Telemetry/Traces/Exporters/FileExporter/FileExporter.cs 
b/csharp/src/Telemetry/Traces/Exporters/FileExporter/FileExporter.cs
index 08d4336ab..90c69abd9 100644
--- a/csharp/src/Telemetry/Traces/Exporters/FileExporter/FileExporter.cs
+++ b/csharp/src/Telemetry/Traces/Exporters/FileExporter/FileExporter.cs
@@ -21,6 +21,7 @@ using System.Diagnostics;
 using System.IO;
 using System.Text;
 using System.Text.Json;
+using System.Text.Json.Serialization.Metadata;
 using System.Threading;
 using System.Threading.Channels;
 using System.Threading.Tasks;
@@ -38,6 +39,12 @@ namespace 
Apache.Arrow.Adbc.Telemetry.Traces.Exporters.FileExporter
 
         private static readonly ConcurrentDictionary<string, 
Lazy<FileExporterInstance>> s_fileExporters = new();
         private static readonly byte[] s_newLine = 
Encoding.UTF8.GetBytes(Environment.NewLine);
+        private static readonly JsonSerializerOptions s_serializerOptions = 
new()
+        {
+            TypeInfoResolver = JsonTypeInfoResolver.Combine(
+                SerializableActivitySerializerContext.Default,
+                new DefaultJsonTypeInfoResolver())
+        };
 
         private readonly TracingFile _tracingFile;
         private readonly string _fileBaseName;
@@ -151,7 +158,9 @@ namespace 
Apache.Arrow.Adbc.Telemetry.Traces.Exporters.FileExporter
                     SerializableActivity serializableActivity = new(activity);
                     await JsonSerializer.SerializeAsync(
                         stream,
-                        serializableActivity, cancellationToken: 
cancellationToken).ConfigureAwait(false);
+                        serializableActivity,
+                        s_serializerOptions,
+                        cancellationToken: 
cancellationToken).ConfigureAwait(false);
                     stream.Write(s_newLine, 0, s_newLine.Length);
                     stream.Position = 0;
 
diff --git 
a/csharp/src/Telemetry/Traces/Listeners/FileListener/ActivityProcessor.cs 
b/csharp/src/Telemetry/Traces/Listeners/FileListener/ActivityProcessor.cs
index 20fff5840..f34c79bfc 100644
--- a/csharp/src/Telemetry/Traces/Listeners/FileListener/ActivityProcessor.cs
+++ b/csharp/src/Telemetry/Traces/Listeners/FileListener/ActivityProcessor.cs
@@ -20,6 +20,7 @@ using System.Diagnostics;
 using System.IO;
 using System.Text;
 using System.Text.Json;
+using System.Text.Json.Serialization.Metadata;
 using System.Threading;
 using System.Threading.Channels;
 using System.Threading.Tasks;
@@ -29,6 +30,12 @@ namespace 
Apache.Arrow.Adbc.Telemetry.Traces.Listeners.FileListener
     internal sealed class ActivityProcessor : IDisposable
     {
         private static readonly byte[] s_newLine = 
Encoding.UTF8.GetBytes(Environment.NewLine);
+        private static readonly JsonSerializerOptions s_serializerOptions = 
new()
+        {
+            TypeInfoResolver = JsonTypeInfoResolver.Combine(
+                SerializableActivitySerializerContext.Default,
+                new DefaultJsonTypeInfoResolver())
+        };
         private Task? _processingTask;
         private readonly Channel<Activity> _channel;
         private readonly Func<Stream, CancellationToken, Task> 
_streamWriterFunc;
@@ -91,7 +98,9 @@ namespace 
Apache.Arrow.Adbc.Telemetry.Traces.Listeners.FileListener
                     SerializableActivity serializableActivity = new(activity);
                     await JsonSerializer.SerializeAsync(
                         stream,
-                        serializableActivity, cancellationToken: 
cancellationToken).ConfigureAwait(false);
+                        serializableActivity,
+                        s_serializerOptions,
+                        cancellationToken: 
cancellationToken).ConfigureAwait(false);
                     stream.Write(s_newLine, 0, s_newLine.Length);
                     stream.Position = 0;
 
diff --git 
a/csharp/src/Telemetry/Traces/Listeners/FileListener/SerializableActivitySerializerContext.cs
 
b/csharp/src/Telemetry/Traces/Listeners/FileListener/SerializableActivitySerializerContext.cs
new file mode 100644
index 000000000..7e503bb52
--- /dev/null
+++ 
b/csharp/src/Telemetry/Traces/Listeners/FileListener/SerializableActivitySerializerContext.cs
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+using System;
+using System.Text.Json.Serialization;
+
+namespace Apache.Arrow.Adbc.Telemetry.Traces.Listeners.FileListener
+{
+    [JsonSerializable(typeof(SerializableActivity))]
+
+    [JsonSerializable(typeof(string))]
+    [JsonSerializable(typeof(byte))]
+    [JsonSerializable(typeof(sbyte))]
+    [JsonSerializable(typeof(ushort))]
+    [JsonSerializable(typeof(short))]
+    [JsonSerializable(typeof(uint))]
+    [JsonSerializable(typeof(int))]
+    [JsonSerializable(typeof(ulong))]
+    [JsonSerializable(typeof(long))]
+    [JsonSerializable(typeof(ulong))]
+    [JsonSerializable(typeof(float))]
+    [JsonSerializable(typeof(double))]
+    [JsonSerializable(typeof(decimal))]
+    [JsonSerializable(typeof(char))]
+    [JsonSerializable(typeof(bool))]
+
+    [JsonSerializable(typeof(string[]))]
+    [JsonSerializable(typeof(byte[]))]
+    [JsonSerializable(typeof(sbyte[]))]
+    [JsonSerializable(typeof(ushort[]))]
+    [JsonSerializable(typeof(short[]))]
+    [JsonSerializable(typeof(uint[]))]
+    [JsonSerializable(typeof(int[]))]
+    [JsonSerializable(typeof(ulong[]))]
+    [JsonSerializable(typeof(long[]))]
+    [JsonSerializable(typeof(ulong[]))]
+    [JsonSerializable(typeof(float[]))]
+    [JsonSerializable(typeof(double[]))]
+    [JsonSerializable(typeof(decimal[]))]
+    [JsonSerializable(typeof(char[]))]
+    [JsonSerializable(typeof(bool[]))]
+
+    [JsonSerializable(typeof(Uri))]
+    internal partial class SerializableActivitySerializerContext : 
JsonSerializerContext
+    {
+    }
+}
diff --git 
a/csharp/test/Telemetry/Traces/Listeners/FileListener/SerializableActivityTests.cs
 
b/csharp/test/Telemetry/Traces/Listeners/FileListener/SerializableActivityTests.cs
new file mode 100644
index 000000000..d8e03c6fa
--- /dev/null
+++ 
b/csharp/test/Telemetry/Traces/Listeners/FileListener/SerializableActivityTests.cs
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+using System.Diagnostics;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization.Metadata;
+using Apache.Arrow.Adbc.Telemetry.Traces.Listeners.FileListener;
+using Xunit.Abstractions;
+
+namespace Apache.Arrow.Adbc.Tests.Telemetry.Traces.Listeners.FileListener
+{
+    public class SerializableActivityTests
+    {
+        private readonly ITestOutputHelper _output;
+
+        public class SerializableActivityTestData : TheoryData<Activity>
+        {
+            public SerializableActivityTestData()
+            {
+                var activityWithTags = new Activity("TestActivityWithTags");
+                int index = 0;
+
+                activityWithTags.AddTag("key" + index++, "value1");
+                activityWithTags.AddTag("key" + index++, (sbyte)123);
+                activityWithTags.AddTag("key" + index++, (byte)123);
+                activityWithTags.AddTag("key" + index++, (short)123);
+                activityWithTags.AddTag("key" + index++, (ushort)123);
+                activityWithTags.AddTag("key" + index++, (int)123);
+                activityWithTags.AddTag("key" + index++, (uint)123);
+                activityWithTags.AddTag("key" + index++, (long)123);
+                activityWithTags.AddTag("key" + index++, (ulong)123);
+                activityWithTags.AddTag("key" + index++, (float)123);
+                activityWithTags.AddTag("key" + index++, (double)123);
+                activityWithTags.AddTag("key" + index++, (decimal)123);
+                activityWithTags.AddTag("key" + index++, true);
+                activityWithTags.AddTag("key" + index++, 'A');
+
+                activityWithTags.AddTag("key" + index++, new string[] { "val1" 
});
+                activityWithTags.AddTag("key" + index++, new byte[] { 123 });
+                activityWithTags.AddTag("key" + index++, new sbyte[] { 123 });
+                activityWithTags.AddTag("key" + index++, new ushort[] { 123 });
+                activityWithTags.AddTag("key" + index++, new short[] { 123 });
+                activityWithTags.AddTag("key" + index++, new uint[] { 123 });
+                activityWithTags.AddTag("key" + index++, new int[] { 123 });
+                activityWithTags.AddTag("key" + index++, new ulong[] { 123 });
+                activityWithTags.AddTag("key" + index++, new long[] { 123 });
+                activityWithTags.AddTag("key" + index++, new float[] { 123 });
+                activityWithTags.AddTag("key" + index++, new double[] { 123 });
+                activityWithTags.AddTag("key" + index++, new decimal[] { 123 
});
+                activityWithTags.AddTag("key" + index++, new bool[] { true });
+                activityWithTags.AddTag("key" + index++, new char[] { 'A' });
+
+                activityWithTags.AddTag("key" + index++, new string[] { "val1" 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new byte[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new sbyte[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new ushort[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new short[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new uint[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new int[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new ulong[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new long[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new float[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new double[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new decimal[] { 123 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new bool[] { true 
}.AsEnumerable());
+                activityWithTags.AddTag("key" + index++, new char[] { 'A' 
}.AsEnumerable());
+
+                activityWithTags.AddTag("key" + index++, new 
Uri("http://example.com";));
+                Add(activityWithTags);
+            }
+        }
+
+        public SerializableActivityTests(ITestOutputHelper output)
+        {
+            _output = output;
+        }
+
+        [Fact]
+        public async Task 
CannnotSerializeAnonymousObjectWithSerializerContext()
+        {
+            Activity activity = new Activity("activity");
+            using (activity.Start())
+            {
+                activity.AddTag("key1", new { Field1 = "value1" });
+                SerializableActivity serializableActivity = new(activity);
+                var stream = new MemoryStream();
+                var serializerOptions = new JsonSerializerOptions
+                {
+                    WriteIndented = true,
+                    IncludeFields = true,
+                    TypeInfoResolver = 
SerializableActivitySerializerContext.Default,
+                };
+                await Assert.ThrowsAnyAsync<Exception>(async () => await 
JsonSerializer.SerializeAsync(
+                    stream,
+                    serializableActivity,
+                    serializerOptions));
+            }
+        }
+
+        [Theory]
+        [ClassData(typeof(SerializableActivityTestData))]
+        public async Task CanSerializeWithNoDefaultTypeInfoResolver(Activity 
activity)
+        {
+            using (activity.Start())
+            {
+                SerializableActivity serializableActivity = new(activity);
+                var stream = new MemoryStream();
+                var serializerOptions = new JsonSerializerOptions
+                {
+                    WriteIndented = true,
+                    IncludeFields = true,
+                    TypeInfoResolver = 
SerializableActivitySerializerContext.Default,
+                };
+                await JsonSerializer.SerializeAsync(
+                    stream,
+                    serializableActivity,
+                    serializerOptions);
+                Assert.NotNull(stream);
+                _output.WriteLine("Serialized Activity: {0}", 
Encoding.UTF8.GetString(stream.ToArray()));
+            }
+        }
+
+        [Theory]
+        [ClassData(typeof(SerializableActivityTestData))]
+        public async Task CanSerializeWithDefaultTypeInfoResolver(Activity 
activity)
+        {
+            using (activity.Start())
+            {
+                SerializableActivity serializableActivity = new(activity);
+                var stream = new MemoryStream();
+                var serializerOptions = new JsonSerializerOptions
+                {
+                    WriteIndented = true,
+                    IncludeFields = true,
+                    TypeInfoResolver = JsonTypeInfoResolver.Combine(
+                        SerializableActivitySerializerContext.Default,
+                        new DefaultJsonTypeInfoResolver()),
+                };
+                await JsonSerializer.SerializeAsync(
+                    stream,
+                    serializableActivity,
+                    serializerOptions);
+                Assert.NotNull(stream);
+                _output.WriteLine("Serialized Activity: {0}", 
Encoding.UTF8.GetString(stream.ToArray()));
+            }
+        }
+
+        [Fact]
+        public async Task 
CanSerializeAnonymousObjectWithDefaultTypeInfoResolver()
+        {
+            Activity activity = new Activity("activity");
+            using (activity.Start())
+            {
+                activity.AddTag("key1", new { Field1 = "value1" });
+                SerializableActivity serializableActivity = new(activity);
+                var stream = new MemoryStream();
+                var serializerOptions = new JsonSerializerOptions
+                {
+                    WriteIndented = true,
+                    IncludeFields = true,
+                    TypeInfoResolver = JsonTypeInfoResolver.Combine(
+                        SerializableActivitySerializerContext.Default,
+                        new DefaultJsonTypeInfoResolver()),
+                };
+                await JsonSerializer.SerializeAsync(
+                    stream,
+                    serializableActivity,
+                    serializerOptions);
+                _output.WriteLine("Serialized Activity: {0}", 
Encoding.UTF8.GetString(stream.ToArray()));
+            }
+        }
+    }
+}

Reply via email to