From fe6e4f91cf10dee04659a7aebaeabd8994c12611 Mon Sep 17 00:00:00 2001 From: Panu Oksala Date: Tue, 11 Jan 2022 19:55:16 +0200 Subject: [PATCH] Improved logging with Serilog --- AzureDevOpsRunnerAction.cs | 17 ++- Services/AzureDevOpsService.cs | 258 +++++++++++++++++++-------------- 2 files changed, 160 insertions(+), 115 deletions(-) diff --git a/AzureDevOpsRunnerAction.cs b/AzureDevOpsRunnerAction.cs index a7cdb51..b4f6ace 100644 --- a/AzureDevOpsRunnerAction.cs +++ b/AzureDevOpsRunnerAction.cs @@ -1,4 +1,6 @@ -using StreamDeckAzureDevOps.Models; +using Microsoft.Extensions.Logging; +using Serilog; +using StreamDeckAzureDevOps.Models; using StreamDeckAzureDevOps.Services; using StreamDeckLib; using StreamDeckLib.Messages; @@ -11,7 +13,12 @@ namespace StreamDeckAzureDevOps [ActionUuid(Uuid = "net.oksala.azuredevops.runner")] public class AzureDevOpsRunnerAction : BaseAction { - private readonly AzureDevOpsService _service = new AzureDevOpsService(); + private readonly AzureDevOpsService _service; + + public AzureDevOpsRunnerAction() + { + _service = new AzureDevOpsService(Logger); + } public async Task UpdateStatus(string context) { @@ -31,7 +38,7 @@ public async Task UpdateStatus(string context) } public override async Task OnDidReceiveSettings(StreamDeckEventPayload args) - { + { await base.OnDidReceiveSettings(args); } @@ -94,14 +101,14 @@ private async Task ExecuteKeyPress(StreamDeckEventPayload args, KeyPressAction k } await Manager.ShowOkAsync(args.context); - if((StatusUpdateFrequency)SettingsModel.UpdateStatusEverySecond == StatusUpdateFrequency.Never) + if ((StatusUpdateFrequency)SettingsModel.UpdateStatusEverySecond == StatusUpdateFrequency.Never) { await Manager.SetImageAsync(args.context, "images/Azure-DevOps-success.png"); } else { await Manager.SetImageAsync(args.context, "images/Azure-DevOps-waiting.png"); - } + } break; } diff --git a/Services/AzureDevOpsService.cs b/Services/AzureDevOpsService.cs index 621a5e6..4baf862 100644 --- a/Services/AzureDevOpsService.cs +++ b/Services/AzureDevOpsService.cs @@ -1,4 +1,5 @@ -using Microsoft.TeamFoundation.Build.WebApi; +using Microsoft.Extensions.Logging; +using Microsoft.TeamFoundation.Build.WebApi; using Microsoft.TeamFoundation.Core.WebApi; using Microsoft.VisualStudio.Services.Common; using Microsoft.VisualStudio.Services.ReleaseManagement.WebApi; @@ -16,165 +17,202 @@ namespace StreamDeckAzureDevOps.Services { public class AzureDevOpsService { + private readonly ILogger logger; + public TimeSpan StaleInProgressBuild { get; set; } = TimeSpan.FromDays(1); public TimeSpan StaleInProgressDeployment { get; set; } = TimeSpan.FromDays(1); + public AzureDevOpsService(ILogger logger) + { + this.logger = logger; + } + public async Task GetBuildStatusImage(AzureDevOpsSettingsModel settings) { - var connection = GetConnection(settings); - var buildClient = connection.GetClient(); - var projectClient = connection.GetClient(); + try + { + var connection = GetConnection(settings); + var buildClient = connection.GetClient(); + var projectClient = connection.GetClient(); - var teamProject = await projectClient.GetProject(settings.ProjectName); + var teamProject = await projectClient.GetProject(settings.ProjectName); - // Definition ID == 0 means that we take whatever build definitions are available. - Build build; - if (settings.DefinitionId > 0) - { - // First check the latest in progress build. - // It's more useful to show in progress build than latest one because - // latest build might be waiting for in progress one to complete. - var latestInProgressBuilds = await buildClient.GetBuildsAsync( - teamProject.Id, - top: 1, - queryOrder: BuildQueryOrder.QueueTimeDescending, - definitions: new[] { settings.DefinitionId }, - statusFilter: BuildStatus.InProgress); - - // Ignore if it's 1 day old. (probably waiting for approval) - build = latestInProgressBuilds?.FirstOrDefault(x => x.StartTime > DateTime.UtcNow.Subtract(StaleInProgressBuild)); - if (build == null) + // Definition ID == 0 means that we take whatever build definitions are available. + Build build; + if (settings.DefinitionId > 0) { - // Get latest build if there are no active builds. - build = await buildClient.GetLatestBuildAsync(teamProject.Id, settings.DefinitionId.ToString()); + // First check the latest in progress build. + // It's more useful to show in progress build than latest one because + // latest build might be waiting for in progress one to complete. + var latestInProgressBuilds = await buildClient.GetBuildsAsync( + teamProject.Id, + top: 1, + queryOrder: BuildQueryOrder.QueueTimeDescending, + definitions: new[] { settings.DefinitionId }, + statusFilter: BuildStatus.InProgress); + + // Ignore if it's 1 day old. (probably waiting for approval) + build = latestInProgressBuilds?.FirstOrDefault(x => x.StartTime > DateTime.UtcNow.Subtract(StaleInProgressBuild)); + if (build == null) + { + // Get latest build if there are no active builds. + build = await buildClient.GetLatestBuildAsync(teamProject.Id, settings.DefinitionId.ToString()); + } } - } - else - { - // Get latest in-progress builds. - var latestInProgressBuilds = await buildClient.GetBuildsAsync( - teamProject.Id, - top: 1, - queryOrder: BuildQueryOrder.QueueTimeDescending, - statusFilter: BuildStatus.InProgress); - - // Ignore if it's 1 day old. (probably waiting for approval) - build = latestInProgressBuilds?.FirstOrDefault(x => x.StartTime > DateTime.UtcNow.Subtract(StaleInProgressBuild)); - - // No in progress builds. - if (build == null) + else { - // Get latest build if there are no active builds. - var latestBuild = await buildClient.GetBuildsAsync(teamProject.Id, top: 1, queryOrder: BuildQueryOrder.QueueTimeDescending); - build = latestBuild?.FirstOrDefault(); + // Get latest in-progress builds. + var latestInProgressBuilds = await buildClient.GetBuildsAsync( + teamProject.Id, + top: 1, + queryOrder: BuildQueryOrder.QueueTimeDescending, + statusFilter: BuildStatus.InProgress); + + // Ignore if it's 1 day old. (probably waiting for approval) + build = latestInProgressBuilds?.FirstOrDefault(x => x.StartTime > DateTime.UtcNow.Subtract(StaleInProgressBuild)); + + // No in progress builds. + if (build == null) + { + // Get latest build if there are no active builds. + var latestBuild = await buildClient.GetBuildsAsync(teamProject.Id, top: 1, queryOrder: BuildQueryOrder.QueueTimeDescending); + build = latestBuild?.FirstOrDefault(); + } } - } - return GetBuildStatusImage(build); + return GetBuildStatusImage(build); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to get build status for build status image."); + return string.Empty; + } } public async Task StartBuild(AzureDevOpsSettingsModel settings) { - var connection = GetConnection(settings); - var buildClient = connection.GetClient(); - var projectClient = connection.GetClient(); + try + { + var connection = GetConnection(settings); + var buildClient = connection.GetClient(); + var projectClient = connection.GetClient(); - var teamProjectTask = projectClient.GetProject(settings.ProjectName); + var teamProjectTask = projectClient.GetProject(settings.ProjectName); - List buildDefinitions; - if (settings.DefinitionId > 0) - { - BuildDefinition buildDefinition = await buildClient.GetDefinitionAsync(settings.ProjectName, settings.DefinitionId); - if (buildDefinition == null) + List buildDefinitions; + if (settings.DefinitionId > 0) { - throw new ArgumentException($"Build definition {settings.DefinitionId} not found"); - } + BuildDefinition buildDefinition = await buildClient.GetDefinitionAsync(settings.ProjectName, settings.DefinitionId); + if (buildDefinition == null) + { + throw new ArgumentException($"Build definition {settings.DefinitionId} not found"); + } - buildDefinitions = new List + buildDefinitions = new List { buildDefinition }; - } - else - { - buildDefinitions = await buildClient.GetDefinitionsAsync(settings.ProjectName); - if (buildDefinitions?.Any() != true) + } + else { - throw new ArgumentException($"No build definitions found"); + buildDefinitions = await buildClient.GetDefinitionsAsync(settings.ProjectName); + if (buildDefinitions?.Any() != true) + { + throw new ArgumentException($"No build definitions found"); + } } - } - var teamProject = await teamProjectTask; - foreach (var buildDef in buildDefinitions) + var teamProject = await teamProjectTask; + foreach (var buildDef in buildDefinitions) + { + await buildClient.QueueBuildAsync(new Build() { Definition = buildDef, Project = teamProject }); + } + } + catch (Exception ex) { - await buildClient.QueueBuildAsync(new Build() { Definition = buildDef, Project = teamProject }); + logger.LogError(ex, $"Failed to start build {settings.DefinitionId}."); } } public async Task GetReleaseStatusImage(AzureDevOpsSettingsModel settings) { - var connection = GetConnection(settings); - var releaseClient = connection.GetClient(); - var projectClient = connection.GetClient(); + try + { + var connection = GetConnection(settings); + var releaseClient = connection.GetClient(); + var projectClient = connection.GetClient(); - var teamProject = await projectClient.GetProject(settings.ProjectName); + var teamProject = await projectClient.GetProject(settings.ProjectName); - int? definitionId = settings.DefinitionId > 0 - ? settings.DefinitionId - : null; + int? definitionId = settings.DefinitionId > 0 + ? settings.DefinitionId + : null; - // Prioritize in-progress deployments over waiting/completed. - List releases = await releaseClient.GetDeploymentsAsync( - teamProject.Id, - definitionId: definitionId, - queryOrder: ReleaseQueryOrder.Descending, - deploymentStatus: DeploymentStatus.InProgress); + // Prioritize in-progress deployments over waiting/completed. + List releases = await releaseClient.GetDeploymentsAsync( + teamProject.Id, + definitionId: definitionId, + queryOrder: ReleaseQueryOrder.Descending, + deploymentStatus: DeploymentStatus.InProgress); - Deployment deployment = releases?.FirstOrDefault(x => x.QueuedOn > DateTime.UtcNow.Subtract(StaleInProgressBuild)); - if (deployment == null) + Deployment deployment = releases?.FirstOrDefault(x => x.QueuedOn > DateTime.UtcNow.Subtract(StaleInProgressBuild)); + if (deployment == null) + { + releases = await releaseClient.GetDeploymentsAsync( + teamProject.Id, + definitionId: definitionId, + queryOrder: ReleaseQueryOrder.Descending); + deployment = releases?.FirstOrDefault(); + } + + return GetBuildStatusImage(deployment); + } + catch (Exception ex) { - releases = await releaseClient.GetDeploymentsAsync( - teamProject.Id, - definitionId: definitionId, - queryOrder: ReleaseQueryOrder.Descending); - deployment = releases?.FirstOrDefault(); + logger.LogError(ex, "Failed to get release status image."); + return string.Empty; } - - return GetBuildStatusImage(deployment); } public async Task StartRelease(AzureDevOpsSettingsModel settings) { - var connection = GetConnection(settings); - var releaseClient = connection.GetClient(); - - List definitionIds = new List(); - if (settings.DefinitionId > 0) + try { - definitionIds.Add(settings.DefinitionId); - } - else - { - var projectClient = connection.GetClient(); - var teamProject = await projectClient.GetProject(settings.ProjectName); + var connection = GetConnection(settings); + var releaseClient = connection.GetClient(); - var releaseDefinitions = await releaseClient.GetReleaseDefinitionsAsync(teamProject.Id); - if (releaseDefinitions?.Any() != true) + List definitionIds = new List(); + if (settings.DefinitionId > 0) { - throw new ArgumentException($"No release definitions found"); + definitionIds.Add(settings.DefinitionId); } + else + { + var projectClient = connection.GetClient(); + var teamProject = await projectClient.GetProject(settings.ProjectName); - definitionIds.AddRange(releaseDefinitions.Select(x => x.Id)); - } + var releaseDefinitions = await releaseClient.GetReleaseDefinitionsAsync(teamProject.Id); + if (releaseDefinitions?.Any() != true) + { + throw new ArgumentException($"No release definitions found"); + } - foreach (var definitionId in definitionIds) - { - var releaseMetaData = new ReleaseStartMetadata + definitionIds.AddRange(releaseDefinitions.Select(x => x.Id)); + } + + foreach (var definitionId in definitionIds) { - DefinitionId = definitionId, - }; + var releaseMetaData = new ReleaseStartMetadata + { + DefinitionId = definitionId, + }; - await releaseClient.CreateReleaseAsync(releaseMetaData, settings.ProjectName); + await releaseClient.CreateReleaseAsync(releaseMetaData, settings.ProjectName); + } + } + catch (Exception ex) + { + logger.LogError(ex, $"Failed to start release {settings.DefinitionId}."); } }