Skip to content

Commit 0144129

Browse files
committed
Pass TraceContext to out-of-proc workers
1 parent 48e2fb2 commit 0144129

File tree

5 files changed

+123
-29
lines changed

5 files changed

+123
-29
lines changed

src/WebJobs.Script/Description/Rpc/ScriptInvocationContext.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ public class ScriptInvocationContext
1414

1515
public ExecutionContext ExecutionContext { get; set; }
1616

17+
public string Traceparent { get; set; }
18+
19+
public string Tracestate { get; set; }
20+
21+
public IEnumerable<KeyValuePair<string, string>> Attributes { get; set; }
22+
1723
public IEnumerable<(string name, DataType type, object val)> Inputs { get; set; }
1824

1925
public Dictionary<string, object> BindingData { get; set; }

src/WebJobs.Script/Description/Rpc/WorkerLanguageInvoker.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Collections.ObjectModel;
7+
using System.Diagnostics;
78
using System.IO;
89
using System.Linq;
910
using System.Reactive.Linq;
@@ -67,6 +68,8 @@ protected override async Task<object> InvokeCore(object[] parameters, FunctionIn
6768
Inputs = inputs,
6869
ResultSource = new TaskCompletionSource<ScriptInvocationResult>(),
6970
AsyncExecutionContext = System.Threading.ExecutionContext.Capture(),
71+
Traceparent = Activity.Current?.Id,
72+
Attributes = Activity.Current?.Tags,
7073

7174
// TODO: link up cancellation token to parameter descriptors
7275
CancellationToken = CancellationToken.None,

src/WebJobs.Script/Rpc/LanguageWorkerChannel.cs

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
using System.Collections;
66
using System.Collections.Concurrent;
77
using System.Collections.Generic;
8+
using System.Diagnostics;
89
using System.IO;
910
using System.Linq;
1011
using System.Reactive.Linq;
11-
using System.Threading;
1212
using System.Threading.Tasks;
1313
using System.Threading.Tasks.Dataflow;
1414
using Microsoft.AspNetCore.Http;
@@ -300,34 +300,7 @@ internal void SendInvocationRequest(ScriptInvocationContext context)
300300
context.ResultSource.SetCanceled();
301301
return;
302302
}
303-
304-
var functionMetadata = context.FunctionMetadata;
305-
306-
InvocationRequest invocationRequest = new InvocationRequest()
307-
{
308-
FunctionId = functionMetadata.FunctionId,
309-
InvocationId = context.ExecutionContext.InvocationId.ToString(),
310-
};
311-
foreach (var pair in context.BindingData)
312-
{
313-
if (pair.Value != null)
314-
{
315-
if ((pair.Value is HttpRequest) && IsTriggerMetadataPopulatedByWorker())
316-
{
317-
continue;
318-
}
319-
invocationRequest.TriggerMetadata.Add(pair.Key, pair.Value.ToRpc(_workerChannelLogger, _workerCapabilities));
320-
}
321-
}
322-
foreach (var input in context.Inputs)
323-
{
324-
invocationRequest.InputData.Add(new ParameterBinding()
325-
{
326-
Name = input.name,
327-
Data = input.val.ToRpc(_workerChannelLogger, _workerCapabilities)
328-
});
329-
}
330-
303+
InvocationRequest invocationRequest = context.ToRpcInvocationRequest(IsTriggerMetadataPopulatedByWorker(), _workerChannelLogger, _workerCapabilities);
331304
_executingInvocations.TryAdd(invocationRequest.InvocationId, context);
332305

333306
SendStreamingMessage(new StreamingMessage
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.Azure.WebJobs.Script.Description;
8+
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace Microsoft.Azure.WebJobs.Script.Rpc
12+
{
13+
internal static class ScriptInvocationContextExtensions
14+
{
15+
public static InvocationRequest ToRpcInvocationRequest(this ScriptInvocationContext context, bool isTriggerMetadataPopulatedByWorker, ILogger logger, Capabilities capabilities)
16+
{
17+
InvocationRequest invocationRequest = new InvocationRequest()
18+
{
19+
FunctionId = context.FunctionMetadata.FunctionId,
20+
InvocationId = context.ExecutionContext.InvocationId.ToString(),
21+
TraceContext = GetRpcTraceContext(context.Traceparent, context.Tracestate, context.Attributes, logger),
22+
};
23+
24+
foreach (var pair in context.BindingData)
25+
{
26+
if (pair.Value != null)
27+
{
28+
if ((pair.Value is HttpRequest) && isTriggerMetadataPopulatedByWorker)
29+
{
30+
continue;
31+
}
32+
invocationRequest.TriggerMetadata.Add(pair.Key, pair.Value.ToRpc(logger, capabilities));
33+
}
34+
}
35+
foreach (var input in context.Inputs)
36+
{
37+
invocationRequest.InputData.Add(new ParameterBinding()
38+
{
39+
Name = input.name,
40+
Data = input.val.ToRpc(logger, capabilities)
41+
});
42+
}
43+
44+
return invocationRequest;
45+
}
46+
47+
internal static RpcTraceContext GetRpcTraceContext(string activityId, string traceStateString, IEnumerable<KeyValuePair<string, string>> tags, ILogger logger)
48+
{
49+
RpcTraceContext traceContext = new RpcTraceContext
50+
{
51+
TraceParent = activityId ?? string.Empty,
52+
TraceState = traceStateString ?? string.Empty,
53+
};
54+
55+
foreach (KeyValuePair<string, string> tag in tags ?? Enumerable.Empty<KeyValuePair<string, string>>())
56+
{
57+
if (string.IsNullOrEmpty(tag.Value))
58+
{
59+
logger?.LogDebug($"Excluding {tag.Key} from being added to TraceContext.Attributes since it's value is null/empty");
60+
}
61+
else
62+
{
63+
traceContext.Attributes.Add(tag.Key, tag.Value);
64+
}
65+
}
66+
67+
return traceContext;
68+
}
69+
}
70+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
8+
using Microsoft.Azure.WebJobs.Script.Rpc;
9+
using Microsoft.Extensions.Logging.Abstractions;
10+
using Xunit;
11+
12+
namespace Microsoft.Azure.WebJobs.Script.Tests.Rpc
13+
{
14+
public class ScriptInvocationContextExtensionsTests
15+
{
16+
[Theory]
17+
[InlineData("someTraceParent", "someTraceState", null)]
18+
[InlineData("", "", null)]
19+
[InlineData(null, null, null)]
20+
public void TestGetRpcTraceContext_WithExpectedValues(string traceparent, string tracestate, IEnumerable<KeyValuePair<string, string>> attributes)
21+
{
22+
if (!string.IsNullOrEmpty(traceparent))
23+
{
24+
attributes = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("key1", "value1") };
25+
}
26+
27+
RpcTraceContext traceContext = ScriptInvocationContextExtensions.GetRpcTraceContext(traceparent, tracestate, attributes, NullLogger.Instance);
28+
29+
Assert.Equal(traceparent ?? string.Empty, traceContext.TraceParent);
30+
Assert.Equal(tracestate ?? string.Empty, traceContext.TraceState);
31+
32+
if (attributes != null)
33+
{
34+
Assert.True(attributes.SequenceEqual(traceContext.Attributes));
35+
}
36+
else
37+
{
38+
Assert.Equal(0, traceContext.Attributes.Count);
39+
}
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)