mirror of
https://github.com/actions/runner.git
synced 2026-07-03 11:06:08 +08:00
feat: enhance telemetry for action download resolution and failures (#4536)
This commit is contained in:
@@ -228,7 +228,30 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
throw new Exception($"Missing download info for {lookupKey}");
|
||||
}
|
||||
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
|
||||
|
||||
Exception downloadFailure = null;
|
||||
try
|
||||
{
|
||||
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// record the exception for telemetry, and rethrow the original exception to fail the step.
|
||||
downloadFailure = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
|
||||
{
|
||||
Operation = "download_action",
|
||||
Result = downloadFailure == null ? "succeeded" : downloadFailure.GetType().Name
|
||||
}, Newtonsoft.Json.Formatting.None)}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Parse action.yml and collect composite sub-actions for batched
|
||||
@@ -398,7 +421,30 @@ namespace GitHub.Runner.Worker
|
||||
if (repositoryActions.Count > 0)
|
||||
{
|
||||
// Get the download info
|
||||
var downloadInfos = await GetDownloadInfoAsync(executionContext, repositoryActions);
|
||||
IDictionary<string, WebApi.ActionDownloadInfo> downloadInfos = null;
|
||||
Exception resolveFailure = null;
|
||||
try
|
||||
{
|
||||
downloadInfos = await GetDownloadInfoAsync(executionContext, repositoryActions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// record the exception for telemetry, and rethrow the original exception to fail the step.
|
||||
resolveFailure = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
|
||||
{
|
||||
Operation = "resolve_actions",
|
||||
Result = resolveFailure == null ? "succeeded" : resolveFailure.GetType().Name
|
||||
}, Newtonsoft.Json.Formatting.None)}"
|
||||
});
|
||||
}
|
||||
|
||||
// Download each action
|
||||
foreach (var action in repositoryActions)
|
||||
@@ -414,7 +460,29 @@ namespace GitHub.Runner.Worker
|
||||
throw new Exception($"Missing download info for {lookupKey}");
|
||||
}
|
||||
|
||||
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
|
||||
Exception downloadFailure = null;
|
||||
try
|
||||
{
|
||||
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// record the exception for telemetry, and rethrow the original exception to fail the step.
|
||||
downloadFailure = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
|
||||
{
|
||||
Operation = "download_action",
|
||||
Result = downloadFailure == null ? "succeeded" : downloadFailure.GetType().Name
|
||||
}, Newtonsoft.Json.Formatting.None)}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// More preparation based on content in the repository (action.yml)
|
||||
@@ -980,10 +1048,33 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
if (actionsToResolve.Count > 0)
|
||||
{
|
||||
var downloadInfos = await GetDownloadInfoAsync(executionContext, actionsToResolve);
|
||||
foreach (var kvp in downloadInfos)
|
||||
IDictionary<string, WebApi.ActionDownloadInfo> downloadInfos = null;
|
||||
Exception resolveFailure = null;
|
||||
try
|
||||
{
|
||||
resolvedDownloadInfos[kvp.Key] = kvp.Value;
|
||||
downloadInfos = await GetDownloadInfoAsync(executionContext, actionsToResolve);
|
||||
foreach (var kvp in downloadInfos)
|
||||
{
|
||||
resolvedDownloadInfos[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// record the exception for telemetry, and rethrow the original exception to fail the step.
|
||||
resolveFailure = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
|
||||
{
|
||||
Operation = "resolve_actions",
|
||||
Result = resolveFailure == null ? "succeeded" : resolveFailure.GetType().Name
|
||||
}, Newtonsoft.Json.Formatting.None)}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1209,6 +1300,12 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private string GetWatermarkFilePath(string directory) => directory + ".completed";
|
||||
|
||||
private sealed class ActionTelemetryPayload
|
||||
{
|
||||
public string Operation { get; set; }
|
||||
public string Result { get; set; }
|
||||
}
|
||||
|
||||
private ActionSetupInfo PrepareRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction)
|
||||
{
|
||||
var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference;
|
||||
|
||||
@@ -90,6 +90,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main", "action.yml");
|
||||
Assert.True(File.Exists(actionYamlFile));
|
||||
|
||||
var telemetryMessages = GetTelemetryMessages();
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "resolve_actions"));
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "succeeded"));
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "download_action"));
|
||||
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
|
||||
}
|
||||
finally
|
||||
@@ -148,6 +153,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
// Act + Assert
|
||||
await Assert.ThrowsAsync<InvalidActionArchiveException>(async () => await _actionManager.PrepareActionsAsync(_ec.Object, actions));
|
||||
|
||||
var telemetryMessages = GetTelemetryMessages();
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "resolve_actions"));
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "download_action"));
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "InvalidActionArchiveException"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -215,6 +225,51 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task PrepareActions_ResolveActionDownloadInfo_RecordsTelemetry_OnFailure()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Arrange
|
||||
Setup();
|
||||
_ec.Object.Global.Variables.Set(Constants.Variables.System.JobRequestType, "RunnerJobRequest");
|
||||
|
||||
_launchServer
|
||||
.Setup(x => x.ResolveActionsDownloadInfoAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<ActionReferenceList>(), It.IsAny<CancellationToken>(), It.IsAny<bool>()))
|
||||
.ThrowsAsync(new Exception("resolve failed"));
|
||||
|
||||
var actions = new List<Pipelines.JobStep>
|
||||
{
|
||||
new Pipelines.ActionStep()
|
||||
{
|
||||
Name = "action",
|
||||
Id = Guid.NewGuid(),
|
||||
Reference = new Pipelines.RepositoryPathReference()
|
||||
{
|
||||
Name = "actions/checkout",
|
||||
Ref = "v4",
|
||||
RepositoryType = "GitHub"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Act + Assert
|
||||
await Assert.ThrowsAsync<FailedToResolveActionDownloadInfoException>(async () => await _actionManager.PrepareActionsAsync(_ec.Object, actions));
|
||||
|
||||
var telemetryMessages = GetTelemetryMessages();
|
||||
Assert.Equal(1, telemetryMessages.Count(message =>
|
||||
message.Contains("resolve_actions", StringComparison.OrdinalIgnoreCase)
|
||||
&& !message.Contains("\"result\":\"succeeded\"", StringComparison.OrdinalIgnoreCase)));
|
||||
Assert.False(ContainsTelemetry(telemetryMessages, "resolve_actions\",\"result\":\"succeeded"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
#if OS_LINUX
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
@@ -3333,6 +3388,16 @@ runs:
|
||||
}
|
||||
}
|
||||
|
||||
private IList<string> GetTelemetryMessages()
|
||||
{
|
||||
return _ec.Object.Global.JobTelemetry.Select(x => x.Message).ToList();
|
||||
}
|
||||
|
||||
private static bool ContainsTelemetry(IList<string> telemetryMessages, string expectedFragment)
|
||||
{
|
||||
return telemetryMessages.Any(message => message.Contains(expectedFragment, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
|
||||
Reference in New Issue
Block a user