Skip to content

Commit

Permalink
CP-44752: SDK(C#): Conditional activity source for JsonRPC
Browse files Browse the repository at this point in the history
Doesn't exist on .Net45.

Only creates these activity sources if a listener has been created by the caller,
otherwise `activity` will be `null`, and the code would be a no-op by default.

A listener is created by OpenTelemetry instrumentation for example.

Signed-off-by: Edwin Török <edwin.torok@cloud.com>
  • Loading branch information
edwintorok committed Jan 23, 2025
1 parent 921dae5 commit d7468f6
Showing 1 changed file with 99 additions and 1 deletion.
100 changes: 99 additions & 1 deletion ocaml/sdk-gen/csharp/autogen/src/JsonRpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,40 @@ public partial class JsonRpcClient
{
private int _globalId;

#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
private static readonly System.Reflection.AssemblyName AssemblyName = typeof(JsonRpcClient).Assembly?.GetName();
private static ActivitySource source = new ActivitySource(AssemblyName?.FullName, AssemblyName?.Version?.ToString());

// Follow naming conventions from OpenTelemetry.SemanticConventions
// Not yet on NuGet though:
// dotnet add package OpenTelemetry.SemanticConventions
private static class RpcAttributes {
public const string AttributeRpcMethod = "rpc.method";
public const string AttributeRpcSystem = "rpc.system";
public const string AttributeRpcService = "rpc.service";
public const string AttributeRpcJsonrpcErrorCode = "rpc.jsonrpc.error_code";
public const string AttributeRpcJsonrpcErrorMessage = "rpc.jsonrpc.error_message";
public const string AttributeRpcJsonrpcRequestId = "rpc.jsonrpc.request_id";
public const string AttributeRpcJsonrpcVersion = "rpc.jsonrpc.version";

public const string AttributeRpcMessageType = "rpc.message.type";
public static class RpcMessageTypeValues
{
public const string Sent = "SENT";

public const string Received = "RECEIVED";
}
}

private static class ServerAttributes {
public const string AttributeServerAddress = "server.address";
}

// not part of the SemanticConventions package
private const string ValueJsonRpc = "jsonrpc";
private const string EventRpcMessage = "rpc.message";
#endif

public JsonRpcClient(string baseUrl)
{
Url = baseUrl;
Expand Down Expand Up @@ -210,6 +244,21 @@ protected virtual T Rpc<T>(string callName, JToken parameters, JsonSerializer se
// therefore the latter will be done only in DEBUG mode
using (var postStream = new MemoryStream())
{
#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
// the semantic convention is $package.$service/$method
using (Activity activity = source.CreateActivity("XenAPI/" + callName, ActivityKind.Client))
{
// .NET 5 would use W3C format for the header by default but we build for .Net 4.x still
activity?.SetIdFormat(ActivityIdFormat.W3C);
activity?.Start();
// Set the fields described in the OpenTelemetry Semantic Conventions:
// https://web.archive.org/web/20250119181511/https://opentelemetry.io/docs/specs/semconv/rpc/json-rpc/
// https://web.archive.org/web/20241113162246/https://opentelemetry.io/docs/specs/semconv/rpc/rpc-spans/
activity?.SetTag(RpcAttributes.AttributeRpcSystem, ValueJsonRpc);
activity?.SetTag(ServerAttributes.AttributeServerAddress, new Uri(Url).Host);
activity?.SetTag(RpcAttributes.AttributeRpcMethod, callName);
activity?.SetTag(RpcAttributes.AttributeRpcJsonrpcRequestId, id.ToString());
#endif
using (var sw = new StreamWriter(postStream))
{
#if DEBUG
Expand All @@ -236,37 +285,67 @@ protected virtual T Rpc<T>(string callName, JToken parameters, JsonSerializer se
switch (JsonRpcVersion)
{
case JsonRpcVersion.v2:
#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
activity?.SetTag(RpcAttributes.AttributeRpcJsonrpcVersion, "2.0");
#endif
#if DEBUG
string json2 = responseReader.ReadToEnd();
var res2 = JsonConvert.DeserializeObject<JsonResponseV2<T>>(json2, settings);
#else
var res2 = (JsonResponseV2<T>)serializer.Deserialize(responseReader, typeof(JsonResponseV2<T>));
#endif

if (res2.Error != null)
{
var descr = new List<string> { res2.Error.Message };
descr.AddRange(res2.Error.Data.ToObject<string[]>());
#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
activity?.SetTag(RpcAttributes.AttributeRpcJsonrpcErrorCode, res2.Error.Code);
activity?.SetTag(RpcAttributes.AttributeRpcJsonrpcErrorMessage, descr);
activity?.SetStatus(ActivityStatusCode.Error);
#endif
throw new Failure(descr);
}

#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
activity?.SetStatus(ActivityStatusCode.Ok);
#endif
return res2.Result;
default:
#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
activity?.SetTag(RpcAttributes.AttributeRpcJsonrpcVersion, "1.0");
#endif
#if DEBUG
string json1 = responseReader.ReadToEnd();
var res1 = JsonConvert.DeserializeObject<JsonResponseV1<T>>(json1, settings);
#else
var res1 = (JsonResponseV1<T>)serializer.Deserialize(responseReader, typeof(JsonResponseV1<T>));
#endif

if (res1.Error != null)
{
var errorArray = res1.Error.ToObject<string[]>();
if (errorArray != null)
if (errorArray != null) {
#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
activity?.SetStatus(ActivityStatusCode.Error);
// we can't be sure whether we'll have a Code here
// the exact format of an error object is not specified in JSONRPC v1
activity?.SetTag(RpcAttributes.AttributeRpcJsonrpcErrorMessage, errorArray.ToString());
#endif
throw new Failure(errorArray);
}
}
#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
activity?.SetStatus(ActivityStatusCode.Ok);
#endif
return res1.Result;
}
}
}
}
#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
}
#endif
}
}

Expand Down Expand Up @@ -319,6 +398,15 @@ protected virtual void PerformPostRequest(Stream postStream, Stream responseStre
str.Flush();
}

#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
if (activity != null) {
var tags = new ActivityTagsCollection{
{ RpcAttributes.AttributeRpcMessageType, RpcAttributes.RpcMessageTypeValues.Sent }
};
activity.AddEvent(new ActivityEvent(EventRpcMessage, DateTimeOffset.Now, tags));
}
#endif

HttpWebResponse webResponse = null;
try
{
Expand Down Expand Up @@ -346,6 +434,16 @@ protected virtual void PerformPostRequest(Stream postStream, Stream responseStre
str.CopyTo(responseStream);
responseStream.Flush();
}

#if (NET462_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
if (activity != null) {
var tags = new ActivityTagsCollection{
{ RpcAttributes.AttributeRpcMessageType, RpcAttributes.RpcMessageTypeValues.Received }
};
activity.AddEvent(new ActivityEvent(EventRpcMessage, DateTimeOffset.Now, tags));
}
#endif

}
finally
{
Expand Down

0 comments on commit d7468f6

Please sign in to comment.