Skip to content

Commit b43c584

Browse files
Refactoring the code
1 parent 626b379 commit b43c584

19 files changed

+2857
-287
lines changed

src/Agent.Sdk/Knob/AgentKnobs.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,15 @@ public class AgentKnobs
203203
public static readonly Knob UseNode24 = new Knob(
204204
nameof(UseNode24),
205205
"Forces the agent to use Node 24 handler for all Node-based tasks",
206+
new PipelineFeatureSource("UseNode24"),
206207
new RuntimeKnobSource("AGENT_USE_NODE24"),
207208
new EnvironmentKnobSource("AGENT_USE_NODE24"),
208209
new BuiltInDefaultKnobSource("false"));
210+
209211
public static readonly Knob UseNode24InUnsupportedSystem = new Knob(
210212
nameof(UseNode24InUnsupportedSystem),
211213
"Forces the agent to use Node 24 handler for all Node-based tasks, even if it's in an unsupported system",
214+
new PipelineFeatureSource("UseNode24InUnsupportedSystem"),
212215
new RuntimeKnobSource("AGENT_USE_NODE24_IN_UNSUPPORTED_SYSTEM"),
213216
new EnvironmentKnobSource("AGENT_USE_NODE24_IN_UNSUPPORTED_SYSTEM"),
214217
new BuiltInDefaultKnobSource("false"));
@@ -718,8 +721,8 @@ public class AgentKnobs
718721
public static readonly Knob UseNode24ToStartContainer = new Knob(
719722
nameof(UseNode24ToStartContainer),
720723
"If true, try to start container job using Node24, then fallback to Node20, then Node16.",
721-
new RuntimeKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
722724
new PipelineFeatureSource("UseNode24ToStartContainer"),
725+
new RuntimeKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
723726
new BuiltInDefaultKnobSource("false"));
724727

725728
public static readonly Knob EnableNewMaskerAndRegexes = new Knob(

src/Agent.Worker/ExecutionContext.cs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ public interface IExecutionContext : IAgentService, IKnobValueContext
6262
void QueueAttachFile(string type, string name, string filePath);
6363
ITraceWriter GetTraceWriter();
6464

65+
// correlation context for enhanced tracing
66+
void SetCorrelationStep(string stepId);
67+
void ClearCorrelationStep();
68+
void SetCorrelationTask(string taskId);
69+
void ClearCorrelationTask();
70+
string BuildCorrelationId();
71+
6572
// timeline record update methods
6673
void Start(string currentOperation = null);
6774
TaskResult Complete(TaskResult? result = null, string currentOperation = null, string resultCode = null);
@@ -93,7 +100,7 @@ public interface IExecutionContext : IAgentService, IKnobValueContext
93100
void PublishTaskRunnerTelemetry(Dictionary<string, string> taskRunnerData);
94101
}
95102

96-
public sealed class ExecutionContext : AgentService, IExecutionContext, IDisposable
103+
public sealed class ExecutionContext : AgentService, IExecutionContext, ICorrelationContext, IDisposable
97104
{
98105
private const int _maxIssueCount = 10;
99106

@@ -104,6 +111,8 @@ public sealed class ExecutionContext : AgentService, IExecutionContext, IDisposa
104111
private readonly HashSet<string> _outputvariables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
105112
private readonly List<TaskRestrictions> _restrictions = new List<TaskRestrictions>();
106113
private readonly string _buildLogsFolderName = "buildlogs";
114+
private readonly AsyncLocal<string> _correlationStep = new AsyncLocal<string>();
115+
private readonly AsyncLocal<string> _correlationTask = new AsyncLocal<string>();
107116
private IAgentLogPlugin _logPlugin;
108117
private IPagingLogger _logger;
109118
private IJobServerQueue _jobServerQueue;
@@ -202,6 +211,9 @@ public override void Initialize(IHostContext hostContext)
202211
}
203212

204213
_jobServerQueue = HostContext.GetService<IJobServerQueue>();
214+
215+
// Register this ExecutionContext for enhanced correlation
216+
HostContext.CorrelationContextManager.SetCurrentExecutionContext(this);
205217
}
206218

207219
public void CancelToken()
@@ -996,8 +1008,68 @@ public void PublishTaskRunnerTelemetry(Dictionary<string, string> taskRunnerData
9961008
PublishTelemetry(taskRunnerData, IsAgentTelemetry: true);
9971009
}
9981010

1011+
// Correlation context methods for enhanced tracing
1012+
public void SetCorrelationStep(string stepId)
1013+
{
1014+
_correlationStep.Value = stepId;
1015+
}
1016+
1017+
public void ClearCorrelationStep()
1018+
{
1019+
_correlationStep.Value = null;
1020+
}
1021+
1022+
public void SetCorrelationTask(string taskId)
1023+
{
1024+
_correlationTask.Value = taskId;
1025+
}
1026+
1027+
public void ClearCorrelationTask()
1028+
{
1029+
_correlationTask.Value = null;
1030+
}
1031+
1032+
public string BuildCorrelationId()
1033+
{
1034+
var step = _correlationStep.Value;
1035+
var task = _correlationTask.Value;
1036+
1037+
if (string.IsNullOrEmpty(step))
1038+
{
1039+
return string.IsNullOrEmpty(task) ? string.Empty : $"TASK-{ShortenGuid(task)}";
1040+
}
1041+
1042+
return string.IsNullOrEmpty(task) ? $"STEP-{ShortenGuid(step)}" : $"STEP-{ShortenGuid(step)}|TASK-{ShortenGuid(task)}";
1043+
}
1044+
1045+
/// <summary>
1046+
/// Shorten a GUID to first 12 characters for more readable logs while maintaining uniqueness
1047+
/// </summary>
1048+
private static string ShortenGuid(string guid)
1049+
{
1050+
if (string.IsNullOrEmpty(guid))
1051+
return guid;
1052+
1053+
// Use first 12 characters total: 8 from first segment + 4 from second segment
1054+
// This ensures consistent output length regardless of input length
1055+
// e.g., "verylongstring-1234..." becomes "verylong1234" (12 chars)
1056+
// e.g., "60cf5508-70a7-..." becomes "60cf550870a7" (12 chars)
1057+
var parts = guid.Split('-');
1058+
if (parts.Length >= 2 && parts[0].Length >= 8 && parts[1].Length >= 4)
1059+
{
1060+
return parts[0].Substring(0, 8) + parts[1].Substring(0, 4);
1061+
}
1062+
1063+
// Fallback: remove hyphens and take first 12 chars
1064+
var cleaned = guid.Replace("-", "");
1065+
return cleaned.Length > 12 ? cleaned.Substring(0, 12) : cleaned;
1066+
}
1067+
9991068
public void Dispose()
10001069
{
1070+
// Clear the correlation context registration
1071+
HostContext.CorrelationContextManager.ClearCurrentExecutionContext();
1072+
10011073
_cancellationTokenSource?.Dispose();
10021074
_forceCompleteCancellationTokenSource?.Dispose();
10031075

src/Agent.Worker/Handlers/NodeHandler.cs

Lines changed: 50 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public async Task RunAsync()
125125
ExecutionContext.PublishTaskRunnerTelemetry(telemetryData);
126126

127127
await VsoTaskLibManager.DownloadVsoTaskLibAsync(ExecutionContext);
128-
128+
129129
// Ensure compat vso-task-lib exist at the root of _work folder
130130
// This will make vsts-agent work against 2015 RTM/QU1 TFS, since tasks in those version doesn't package with task lib
131131
// Put the 0.5.5 version vso-task-lib into the root of _work/node_modules folder, so tasks are able to find those lib.
@@ -205,7 +205,7 @@ public async Task RunAsync()
205205
}
206206
else
207207
{
208-
node20ResultsInGlibCErrorHost = await CheckIfNode20ResultsInGlibCError();
208+
node20ResultsInGlibCErrorHost = await CheckIfNodeResultsInGlibCError(NodeHandler.Node20_1Folder);
209209
ExecutionContext.EmitHostNode20FallbackTelemetry(node20ResultsInGlibCErrorHost);
210210
supportsNode20 = !node20ResultsInGlibCErrorHost;
211211
}
@@ -218,8 +218,8 @@ public async Task RunAsync()
218218
}
219219
else
220220
{
221-
node24ResultsInGlibCErrorHost = await CheckIfNode24ResultsInGlibCError();
222-
ExecutionContext.EmitHostNode24FallbackTelemetry(node24ResultsInGlibCErrorHost); // Add this method
221+
node24ResultsInGlibCErrorHost = await CheckIfNodeResultsInGlibCError(NodeHandler.Node24Folder);
222+
ExecutionContext.EmitHostNode24FallbackTelemetry(node24ResultsInGlibCErrorHost);
223223
supportsNode24 = !node24ResultsInGlibCErrorHost;
224224
}
225225
}
@@ -317,21 +317,47 @@ public async Task RunAsync()
317317
}
318318
}
319319

320-
private async Task<bool> CheckIfNode20ResultsInGlibCError()
320+
private async Task<bool> CheckIfNodeResultsInGlibCError(string nodeFolder)
321321
{
322-
var node20 = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node20_1Folder, "bin", $"node{IOUtil.ExeExtension}");
323-
List<string> nodeVersionOutput = await ExecuteCommandAsync(ExecutionContext, node20, "-v", requireZeroExitCode: false, showOutputOnFailureOnly: true);
324-
var node20ResultsInGlibCError = WorkerUtilities.IsCommandResultGlibcError(ExecutionContext, nodeVersionOutput, out string nodeInfoLine);
322+
var nodePath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeFolder, "bin", $"node{IOUtil.ExeExtension}");
323+
List<string> nodeVersionOutput = await ExecuteCommandAsync(ExecutionContext, nodePath, "-v", requireZeroExitCode: false, showOutputOnFailureOnly: true);
324+
var nodeResultsInGlibCError = WorkerUtilities.IsCommandResultGlibcError(ExecutionContext, nodeVersionOutput, out string nodeInfoLine);
325325

326-
return node20ResultsInGlibCError;
326+
return nodeResultsInGlibCError;
327327
}
328-
private async Task<bool> CheckIfNode24ResultsInGlibCError()
328+
329+
private string GetNodeFolderWithFallback(string preferredNodeFolder, bool node20ResultsInGlibCError, bool node24ResultsInGlibCError, bool inContainer)
329330
{
330-
var node24 = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node24Folder, "bin", $"node{IOUtil.ExeExtension}");
331-
List<string> nodeVersionOutput = await ExecuteCommandAsync(ExecutionContext, node24, "-v", requireZeroExitCode: false, showOutputOnFailureOnly: true);
332-
var node24ResultsInGlibCError = WorkerUtilities.IsCommandResultGlibcError(ExecutionContext, nodeVersionOutput, out string nodeInfoLine);
331+
switch (preferredNodeFolder)
332+
{
333+
case var folder when folder == NodeHandler.Node24Folder:
334+
if (node24ResultsInGlibCError)
335+
{
336+
// Fallback to Node20, then Node16 if Node20 also fails
337+
if (node20ResultsInGlibCError)
338+
{
339+
NodeFallbackWarning("20", "16", inContainer);
340+
return NodeHandler.Node16Folder;
341+
}
342+
else
343+
{
344+
NodeFallbackWarning("24", "20", inContainer);
345+
return NodeHandler.Node20_1Folder;
346+
}
347+
}
348+
return NodeHandler.Node24Folder;
349+
350+
case var folder when folder == NodeHandler.Node20_1Folder:
351+
if (node20ResultsInGlibCError)
352+
{
353+
NodeFallbackWarning("20", "16", inContainer);
354+
return NodeHandler.Node16Folder;
355+
}
356+
return NodeHandler.Node20_1Folder;
333357

334-
return node24ResultsInGlibCError;
358+
default:
359+
return preferredNodeFolder;
360+
}
335361
}
336362

337363
public string GetNodeLocation(bool node20ResultsInGlibCError, bool node24ResultsInGlibCError, bool inContainer)
@@ -350,39 +376,12 @@ public string GetNodeLocation(bool node20ResultsInGlibCError, bool node24Results
350376
if (taskHasNode24Data)
351377
{
352378
Trace.Info($"Task.json has node24 handler data: {taskHasNode24Data}");
353-
354-
if (node24ResultsInGlibCError)
355-
{
356-
// Fallback to Node20, then Node16 if Node20 also fails
357-
if (node20ResultsInGlibCError)
358-
{
359-
nodeFolder = NodeHandler.Node16Folder;
360-
Node16FallbackWarning(inContainer);
361-
}
362-
else
363-
{
364-
nodeFolder = NodeHandler.Node20_1Folder;
365-
Node20FallbackWarning(inContainer);
366-
}
367-
}
368-
else
369-
{
370-
nodeFolder = NodeHandler.Node24Folder;
371-
}
379+
nodeFolder = GetNodeFolderWithFallback(NodeHandler.Node24Folder, node20ResultsInGlibCError, node24ResultsInGlibCError, inContainer);
372380
}
373381
else if (taskHasNode20_1Data)
374382
{
375383
Trace.Info($"Task.json has node20_1 handler data: {taskHasNode20_1Data} node20ResultsInGlibCError = {node20ResultsInGlibCError}");
376-
377-
if (node20ResultsInGlibCError)
378-
{
379-
nodeFolder = NodeHandler.Node16Folder;
380-
Node16FallbackWarning(inContainer);
381-
}
382-
else
383-
{
384-
nodeFolder = NodeHandler.Node20_1Folder;
385-
}
384+
nodeFolder = GetNodeFolderWithFallback(NodeHandler.Node20_1Folder, node20ResultsInGlibCError, node24ResultsInGlibCError, inContainer);
386385
}
387386
else if (taskHasNode16Data)
388387
{
@@ -403,42 +402,15 @@ public string GetNodeLocation(bool node20ResultsInGlibCError, bool node24Results
403402
if (useNode24)
404403
{
405404
Trace.Info($"Found UseNode24 knob, using node24 for node tasks: {useNode24}");
406-
407-
if (node24ResultsInGlibCError)
408-
{
409-
// Fallback to Node20, then Node16 if Node20 also fails
410-
if (node20ResultsInGlibCError)
411-
{
412-
nodeFolder = NodeHandler.Node16Folder;
413-
Node16FallbackWarning(inContainer);
414-
}
415-
else
416-
{
417-
nodeFolder = NodeHandler.Node20_1Folder;
418-
Node20FallbackWarning(inContainer);
419-
}
420-
}
421-
else
422-
{
423-
nodeFolder = NodeHandler.Node24Folder;
424-
}
405+
nodeFolder = GetNodeFolderWithFallback(NodeHandler.Node24Folder, node20ResultsInGlibCError, node24ResultsInGlibCError, inContainer);
425406
}
426-
if (useNode20_1)
407+
else if (useNode20_1)
427408
{
428409
Trace.Info($"Found UseNode20_1 knob, using node20_1 for node tasks {useNode20_1} node20ResultsInGlibCError = {node20ResultsInGlibCError}");
429-
430-
if (node20ResultsInGlibCError)
431-
{
432-
nodeFolder = NodeHandler.Node16Folder;
433-
Node16FallbackWarning(inContainer);
434-
}
435-
else
436-
{
437-
nodeFolder = NodeHandler.Node20_1Folder;
438-
}
410+
nodeFolder = GetNodeFolderWithFallback(NodeHandler.Node20_1Folder, node20ResultsInGlibCError, node24ResultsInGlibCError, inContainer);
439411
}
440412

441-
if (useNode10)
413+
else if (useNode10)
442414
{
443415
Trace.Info($"Found UseNode10 knob, use node10 for node tasks: {useNode10}");
444416
nodeFolder = NodeHandler.node10Folder;
@@ -501,37 +473,14 @@ public string GetNodeLocation(bool node20ResultsInGlibCError, bool node24Results
501473
return nodeHandlerHelper.GetNodeFolderPath(nodeFolder, HostContext);
502474
}
503475

504-
private void Node16FallbackWarning(bool inContainer)
476+
private void NodeFallbackWarning(string fromVersion, string toVersion, bool inContainer)
505477
{
506-
if (inContainer)
507-
{
508-
ExecutionContext.Warning($"The container operating system doesn't support Node20. Using Node16 instead. " +
509-
"Please upgrade the operating system of the container to remain compatible with future updates of tasks: " +
510-
"https://github.com/nodesource/distributions");
511-
}
512-
else
513-
{
514-
ExecutionContext.Warning($"The agent operating system doesn't support Node20. Using Node16 instead. " +
515-
"Please upgrade the operating system of the agent to remain compatible with future updates of tasks: " +
478+
string systemType = inContainer ? "container" : "agent";
479+
ExecutionContext.Warning($"The {systemType} operating system doesn't support Node{fromVersion}. Using Node{toVersion} instead. " +
480+
$"Please upgrade the operating system of the {systemType} to remain compatible with future updates of tasks: " +
516481
"https://github.com/nodesource/distributions");
517-
}
518482
}
519483

520-
private void Node20FallbackWarning(bool inContainer)
521-
{
522-
if (inContainer)
523-
{
524-
ExecutionContext.Warning($"The container operating system doesn't support Node24. Using Node20 instead. " +
525-
"Please upgrade the operating system of the container to remain compatible with future updates of tasks.");
526-
}
527-
else
528-
{
529-
ExecutionContext.Warning($"The agent operating system doesn't support Node24. Using Node20 instead. " +
530-
"Please upgrade the operating system of the agent to remain compatible with future updates of tasks.");
531-
}
532-
}
533-
534-
535484
private void OnDataReceived(object sender, ProcessDataReceivedEventArgs e)
536485
{
537486
// drop any outputs after the task get force completed.

0 commit comments

Comments
 (0)