diff --git a/src/KPI Generator/KPI Generator.csproj b/src/KPI Generator/KPI Generator.csproj index 21034656ca121cfecab85496e1ef1b1c1e34dfdb..f91bdc1e0b1d295c0af6cf494b4e2c2fe6e3c397 100644 --- a/src/KPI Generator/KPI Generator.csproj +++ b/src/KPI Generator/KPI Generator.csproj @@ -19,10 +19,18 @@ <ItemGroup> <PackageReference Include="CommandLineParser" Version="2.9.1" /> - <PackageReference Include="Coscine.ApiCommons" Version="2.*-*" /> - <PackageReference Include="Coscine.Database" Version="2.*-*" /> - <PackageReference Include="Coscine.Metadata" Version="2.*-*" /> - <PackageReference Include="Coscine.ResourceTypes" Version="1.*-*" /> + <PackageReference Include="Coscine.ApiCommons" Version="*-*" /> + <PackageReference Include="Coscine.Database" Version="*-*" /> + <PackageReference Include="Coscine.Metadata" Version="*-*" /> + <PackageReference Include="Coscine.ResourceTypes" Version="*-*" /> <PackageReference Include="GitLabApiClient" Version="1.8.1-beta.5" /> + <PackageReference Include="NLog" Version="5.1.0" /> + <PackageReference Include="NLog.Extensions.Logging" Version="5.2.0" /> + </ItemGroup> + + <ItemGroup> + <None Update="nlog.config"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> </ItemGroup> </Project> diff --git a/src/KPI Generator/Logging/AssemblyNameLayoutRenderer.cs b/src/KPI Generator/Logging/AssemblyNameLayoutRenderer.cs new file mode 100644 index 0000000000000000000000000000000000000000..d6085f106212e338ca26ab54617d9bd0652b98e4 --- /dev/null +++ b/src/KPI Generator/Logging/AssemblyNameLayoutRenderer.cs @@ -0,0 +1,23 @@ +using NLog; +using NLog.LayoutRenderers; +using System.Text; + +namespace Coscine.KpiGenerator.Logging +{ + [LayoutRenderer("assembly-name")] + public class AssemblyNameLayoutRenderer : LayoutRenderer + { + protected override void Append(StringBuilder builder, LogEventInfo logEvent) + { + var assembly = typeof(Program).Assembly.GetName(); + if (assembly is not null) + { + builder.Append(assembly.Name); + } + else + { + builder.Append(new Guid().ToString().Take(8)); + } + } + } +} diff --git a/src/KPI Generator/Logging/AssemblyVersionLayoutRenderer.cs b/src/KPI Generator/Logging/AssemblyVersionLayoutRenderer.cs new file mode 100644 index 0000000000000000000000000000000000000000..382269a4e5b561053115ecd68a7a3e9696620802 --- /dev/null +++ b/src/KPI Generator/Logging/AssemblyVersionLayoutRenderer.cs @@ -0,0 +1,23 @@ +using NLog; +using NLog.LayoutRenderers; +using System.Text; + +namespace Coscine.KpiGenerator.Logging +{ + [LayoutRenderer("assembly-version")] + public class AssemblyVersionLayoutRenderer : LayoutRenderer + { + protected override void Append(StringBuilder builder, LogEventInfo logEvent) + { + var assembly = typeof(Program).Assembly.GetName(); + if (assembly is not null && assembly.Version is not null) + { + builder.Append(assembly.Version.ToString(3)); + } + else + { + builder.Append(new Version().ToString(3)); + } + } + } +} diff --git a/src/KPI Generator/Program.cs b/src/KPI Generator/Program.cs index 344cb78c2c9dd69413f5ecd8db7804f20a868c1b..43f4036a4a1543e05f7fb8c6a2c0ca75111311c8 100644 --- a/src/KPI Generator/Program.cs +++ b/src/KPI Generator/Program.cs @@ -1,18 +1,28 @@ using CommandLine; -using KPIGenerator.Reportings.ApplicationProfile; -using KPIGenerator.Reportings.Complete; -using KPIGenerator.Reportings.Project; -using KPIGenerator.Reportings.Resource; -using KPIGenerator.Reportings.System; -using KPIGenerator.Reportings.User; +using Coscine.KpiGenerator.Logging; +using Coscine.KpiGenerator.Reportings.ApplicationProfile; +using Coscine.KpiGenerator.Reportings.Complete; +using Coscine.KpiGenerator.Reportings.Project; +using Coscine.KpiGenerator.Reportings.Resource; +using Coscine.KpiGenerator.Reportings.System; +using Coscine.KpiGenerator.Reportings.User; +using Microsoft.Extensions.Logging; +using NLog.Config; +using NLog.Extensions.Logging; using static KPIGenerator.Utils.CommandLineOptions; -namespace KPIGenerator; +namespace Coscine.KpiGenerator; -public static class Program +public class Program { - static int Main(string[] args) + private static ILogger _logger = null!; + + public static int Main(string[] args) { + ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("assembly-name", typeof(AssemblyNameLayoutRenderer)); + ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("assembly-version", typeof(AssemblyVersionLayoutRenderer)); + _logger = LoggerFactory.Create(builder => builder.AddNLog()).CreateLogger<Program>(); + try { bool result = Parser.Default.ParseArguments< @@ -24,27 +34,30 @@ public static class Program SystemReportingOptions >(args) .MapResult( - (CompleteReportingOptions options) => new CompleteReporting(SanitizeOptions(options)).Run(), - (ProjectReportingOptions options) => new ProjectReporting(SanitizeOptions(options)).Run(), - (ResourceReportingOptions options) => new ResourceReporting(SanitizeOptions(options)).Run(), - (UserReportingOptions options) => new UserReporting(SanitizeOptions(options)).Run(), - (ApplicationProfileReportingOptions options) => new ApplicationProfileReporting(SanitizeOptions(options)).Run(), - (SystemReportingOptions options) => new SystemReporting(SanitizeOptions(options)).Run(), + (CompleteReportingOptions options) => new CompleteReporting(SanitizeOptions(options), _logger).Run(), + (ProjectReportingOptions options) => new ProjectReporting(SanitizeOptions(options), _logger).Run(), + (ResourceReportingOptions options) => new ResourceReporting(SanitizeOptions(options), _logger).Run(), + (UserReportingOptions options) => new UserReporting(SanitizeOptions(options), _logger).Run(), + (ApplicationProfileReportingOptions options) => new ApplicationProfileReporting(SanitizeOptions(options), _logger).Run(), + (SystemReportingOptions options) => new SystemReporting(SanitizeOptions(options), _logger).Run(), _ => false); if (result) { Console.WriteLine("\nFinished.\n"); + _logger.LogInformation("Finished."); return 0; // Exit Code 0 for Success } else { Console.WriteLine("Program execution was interrupted.\n"); + _logger.LogInformation("Program execution was interrupted."); return -1; // Exit Code -1 for Failure } } catch (Exception e) { Console.WriteLine(e.Message); + _logger.LogError(e, e.Message); return -1; // Exit Code -1 for Failure } } @@ -112,4 +125,12 @@ public static class Program } return unsanitizedOptions; } + private static void LogInnerException(Exception ex) + { + if (ex.InnerException is not null) + { + _logger.LogError(ex.InnerException, "InnerException: {innerException}", ex.InnerException.Message); + LogInnerException(ex.InnerException); + } + } } \ No newline at end of file diff --git a/src/KPI Generator/Reporting.cs b/src/KPI Generator/Reporting.cs index ad8888e595019ed5235d5fc627c5ba548ede6548..bc617f3d957bb5a5b2a38b84fb705e071bb3f3de 100644 --- a/src/KPI Generator/Reporting.cs +++ b/src/KPI Generator/Reporting.cs @@ -1,15 +1,16 @@ using Coscine.Configuration; +using Coscine.KpiGenerator.Utils; using Coscine.Metadata; using GitLabApiClient; using GitLabApiClient.Models.Branches.Requests; using GitLabApiClient.Models.Commits.Requests.CreateCommitRequest; -using KPIGenerator.Utils; +using Microsoft.Extensions.Logging; using System.Text; using System.Web; using VDS.RDF.Query; using static KPIGenerator.Utils.CommandLineOptions; -namespace KPIGenerator; +namespace Coscine.KpiGenerator; public abstract class Reporting<O> where O : class { @@ -29,6 +30,7 @@ public abstract class Reporting<O> where O : class private string ReportingBranch { get; init; } public string RwthRor { get; init; } + public readonly ILogger _logger = null!; public readonly Organization _otherOrganization = new() { @@ -36,9 +38,9 @@ public abstract class Reporting<O> where O : class RorUrl = "https://ror.org/_other", }; - public Reporting(O options) + public Reporting(O options, ILogger logger) { - InstanceName = this.GetType().Name; + InstanceName = GetType().Name; Options = options; Configuration = new ConsulConfiguration(); RdfStoreConnector = new RdfStoreConnector(Configuration.GetStringAndWait("coscine/local/virtuoso/additional/url")); @@ -52,6 +54,7 @@ public abstract class Reporting<O> where O : class ReportingBranch = Configuration.GetStringAndWait("coscine/local/reporting/branch"); RwthRor = Configuration.GetStringAndWait("coscine/global/organizations/rwth/ror_url"); + _logger = logger; } public abstract IEnumerable<ReportingFileObject> GenerateReporting(); @@ -64,6 +67,11 @@ public abstract class Reporting<O> where O : class if (baseOptions is not null && baseOptions.DummyMode) { Console.Write(" : DUMMY MODE"); + _logger.LogInformation("Initialized on {Domain} | {InstanceName} | DUMMY MODE {DummyMode}", Domain, InstanceName, baseOptions.DummyMode); + } + else + { + _logger.LogInformation("Initialized on {Domain} | {InstanceName}", Domain, InstanceName); } Console.WriteLine($"\n{new string('-', 80)}"); EnsureGitLabInformationIsSetAndCorrect(); @@ -71,9 +79,11 @@ public abstract class Reporting<O> where O : class var reportingFiles = GenerateReporting(); Console.WriteLine($"\n{new string('=', 80)}"); Console.WriteLine(" - Reporting generated successfully. Publishing..."); + _logger.LogInformation("Reporting generated successfully. Publishing {reportingFiles}", reportingFiles); // Publish Report var success = PublishAsync(reportingFiles).Result; Console.WriteLine(success ? " - Published successfully." : " - Publishing FAILED!"); + _logger.LogInformation("Publishing successful: {success}", success); return success; } @@ -87,6 +97,7 @@ public abstract class Reporting<O> where O : class var commitMessage = $"{InstanceName} Generated - {DateTime.Now:dd.MM.yyyy HH:mm}"; // CompleteReporting Generated - 31.08.2022 10:25 Console.WriteLine($" - Commit: \"{commitMessage}\""); + _logger.LogInformation("Commit: {commitMessage}", commitMessage); var projectTree = await GitLabClient.Trees.GetAsync(reportingDatabaseProject, o => { @@ -143,6 +154,7 @@ public abstract class Reporting<O> where O : class catch (Exception e) { Console.WriteLine(e.Message); + _logger.LogError(e, e.Message); return false; } } @@ -178,6 +190,7 @@ public abstract class Reporting<O> where O : class result.RorUrl = rorUrl; // Don't overwrite the RoR of the entry found, just use "Other" as name. Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($" WARNING!: Organization with ROR \"{rorUrl}\" could not be correctly identified. Will use \"{result.RorUrl}\" as RoR and \"{result.Name}\" as name."); + _logger.LogWarning("Organization with ROR {rorUrl} could not be correctly identified.", rorUrl); Console.ResetColor(); } } @@ -232,6 +245,7 @@ public abstract class Reporting<O> where O : class } var project = GitLabClient.Projects.GetAsync(ReportingDatabaseProjectId).Result; Console.WriteLine($" - Report Generation to be uploaded to GitLab Project \"{project.Name}\" on branch \"{ReportingBranch}\""); + _logger.LogInformation("Report Generation to be uploaded to GitLab Project {projectName} on branch {ReportingBranch}", project.Name, ReportingBranch); var branch = GitLabClient.Branches.GetAsync(project.Id, o => o.Search = ReportingBranch).Result; if (!branch.Any(b => b.Name.Equals(ReportingBranch)) && Domain.Equals("DEVLEF") && !project.DefaultBranch.Equals(ReportingBranch)) @@ -239,6 +253,7 @@ public abstract class Reporting<O> where O : class Console.WriteLine($" - Branch \"{ReportingBranch}\" does not exist. Working on Domain {Domain}. Creating branch..."); GitLabClient.Branches.CreateAsync(ReportingDatabaseProjectId, new CreateBranchRequest(ReportingBranch, project.DefaultBranch)).Wait(); Console.WriteLine($" - Branch \"{ReportingBranch}\" successfully created"); + _logger.LogInformation("Branch {ReportingBranch} successfully created", ReportingBranch); } else if (!branch.Any(b => b.Name.Equals(ReportingBranch))) { diff --git a/src/KPI Generator/Reportings/ApplicationProfile/ApplicationProfileReporting.cs b/src/KPI Generator/Reportings/ApplicationProfile/ApplicationProfileReporting.cs index bfb821687903cf5e1fd6131c031bf36b8eed7611..14c3520ab32b55d130c002464197c79614d4f421 100644 --- a/src/KPI Generator/Reportings/ApplicationProfile/ApplicationProfileReporting.cs +++ b/src/KPI Generator/Reportings/ApplicationProfile/ApplicationProfileReporting.cs @@ -1,13 +1,14 @@ -using KPIGenerator.Utils; +using Coscine.KpiGenerator.Utils; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using VDS.RDF.Query; using static KPIGenerator.Utils.CommandLineOptions; -namespace KPIGenerator.Reportings.ApplicationProfile; +namespace Coscine.KpiGenerator.Reportings.ApplicationProfile; public class ApplicationProfileReporting : Reporting<ApplicationProfileReportingOptions> { - public ApplicationProfileReporting(ApplicationProfileReportingOptions options) : base(options) + public ApplicationProfileReporting(ApplicationProfileReportingOptions options, ILogger _logger) : base(options, _logger) { ReportingFileName = "application_profiles.json"; } @@ -15,6 +16,7 @@ public class ApplicationProfileReporting : Reporting<ApplicationProfileReporting public override IEnumerable<ReportingFileObject> GenerateReporting() { Console.WriteLine($" - {GetType().Name}: Begin reporting generation"); + _logger.LogInformation("{Name}: Begin reporting generation", GetType().Name); var reportingFiles = new List<ReportingFileObject>(); var returnObjects = GetApplicationProfiles(); @@ -25,7 +27,8 @@ public class ApplicationProfileReporting : Reporting<ApplicationProfileReporting Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjects, Formatting.Indented)) }); Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathGeneral(ReportingFileName)}\" generated successfully"); - + _logger.LogInformation("{Name}: Generated successfully. {reportingFiles}", GetType().Name, reportingFiles); + Console.WriteLine(); return reportingFiles; } diff --git a/src/KPI Generator/Reportings/ApplicationProfile/ReturnObject.cs b/src/KPI Generator/Reportings/ApplicationProfile/ReturnObject.cs index 230df20eddfe06dfb76d8eb99da367d5868aa107..6369623568ffb4c02f44ce096d5533aafa76fe59 100644 --- a/src/KPI Generator/Reportings/ApplicationProfile/ReturnObject.cs +++ b/src/KPI Generator/Reportings/ApplicationProfile/ReturnObject.cs @@ -1,4 +1,4 @@ -namespace KPIGenerator.Reportings.ApplicationProfile; +namespace Coscine.KpiGenerator.Reportings.ApplicationProfile; /// <summary> /// Object containing the JSON structure for the reporting diff --git a/src/KPI Generator/Reportings/Complete/CompleteReporting.cs b/src/KPI Generator/Reportings/Complete/CompleteReporting.cs index 2043ca62667bab52004cfb506d15320a3bb3eb61..f0f1ef7bc7391c64f5c83bfc1414143a1fee9380 100644 --- a/src/KPI Generator/Reportings/Complete/CompleteReporting.cs +++ b/src/KPI Generator/Reportings/Complete/CompleteReporting.cs @@ -1,16 +1,17 @@ -using KPIGenerator.Reportings.ApplicationProfile; -using KPIGenerator.Reportings.Project; -using KPIGenerator.Reportings.Resource; -using KPIGenerator.Reportings.System; -using KPIGenerator.Reportings.User; -using KPIGenerator.Utils; +using Coscine.KpiGenerator.Reportings.ApplicationProfile; +using Coscine.KpiGenerator.Reportings.Project; +using Coscine.KpiGenerator.Reportings.Resource; +using Coscine.KpiGenerator.Reportings.System; +using Coscine.KpiGenerator.Reportings.User; +using Coscine.KpiGenerator.Utils; +using Microsoft.Extensions.Logging; using static KPIGenerator.Utils.CommandLineOptions; -namespace KPIGenerator.Reportings.Complete; +namespace Coscine.KpiGenerator.Reportings.Complete; public class CompleteReporting : Reporting<CompleteReportingOptions> { - public CompleteReporting(CompleteReportingOptions options) : base(options) + public CompleteReporting(CompleteReportingOptions options, ILogger _logger) : base(options, _logger) { } public override IEnumerable<ReportingFileObject> GenerateReporting() @@ -23,11 +24,12 @@ public class CompleteReporting : Reporting<CompleteReportingOptions> result.AddRange(new ProjectReporting(new ProjectReportingOptions { DummyMode = Options.DummyMode - }).GenerateReporting()); + }, _logger).GenerateReporting()); } catch (Exception e) { Console.WriteLine($"!! Skipping ProjectReporting: {e.Message} \n"); + _logger.LogWarning(e, "Skipping ProjectReporting: {message}", e.Message); } // Resource Reporting @@ -36,11 +38,12 @@ public class CompleteReporting : Reporting<CompleteReportingOptions> result.AddRange(new ResourceReporting(new ResourceReportingOptions { DummyMode = Options.DummyMode - }).GenerateReporting()); + }, _logger).GenerateReporting()); } catch (Exception e) { Console.WriteLine($"!! Skipping ResourceReporting: {e.Message} \n"); + _logger.LogWarning(e, "Skipping ResourceReporting: {message}", e.Message); } // User Reporting @@ -49,11 +52,12 @@ public class CompleteReporting : Reporting<CompleteReportingOptions> result.AddRange(new UserReporting(new UserReportingOptions { DummyMode = Options.DummyMode - }).GenerateReporting()); + }, _logger).GenerateReporting()); } catch (Exception e) { Console.WriteLine($"!! Skipping UserReporting: {e.Message} \n"); + _logger.LogWarning(e, "Skipping UserReporting: {message}", e.Message); } // Application Profile Reporting @@ -62,11 +66,12 @@ public class CompleteReporting : Reporting<CompleteReportingOptions> result.AddRange(new ApplicationProfileReporting(new ApplicationProfileReportingOptions { DummyMode = Options.DummyMode - }).GenerateReporting()); + }, _logger).GenerateReporting()); } catch (Exception e) { Console.WriteLine($"!! Skipping ApplicationProfileReporting: {e.Message} \n"); + _logger.LogWarning(e, "Skipping ApplicationProfileReporting: {message}", e.Message); } // System Status Reporting @@ -75,11 +80,12 @@ public class CompleteReporting : Reporting<CompleteReportingOptions> result.AddRange(new SystemReporting(new SystemReportingOptions { DummyMode = Options.DummyMode - }).GenerateReporting()); + }, _logger).GenerateReporting()); } catch (Exception e) { Console.WriteLine($"!! Skipping SystemReporting: {e.Message} \n"); + _logger.LogWarning(e, "Skipping SystemReporting: {message}", e.Message); } return result; diff --git a/src/KPI Generator/Reportings/Complete/ReturnObject.cs b/src/KPI Generator/Reportings/Complete/ReturnObject.cs index 7bcfe9b3cd3c81a033f382601eaffeebb1d02c5d..65dda7985a0e953d101b7c7d183b3f3eff81961e 100644 --- a/src/KPI Generator/Reportings/Complete/ReturnObject.cs +++ b/src/KPI Generator/Reportings/Complete/ReturnObject.cs @@ -1,4 +1,4 @@ -namespace KPIGenerator.Reportings.Complete; +namespace Coscine.KpiGenerator.Reportings.Complete; /// <summary> /// Object containing the JSON structure for the reporting diff --git a/src/KPI Generator/Reportings/Project/ProjectReporting.cs b/src/KPI Generator/Reportings/Project/ProjectReporting.cs index 66e6606d0737fb7003f2e4011f31880aeef598e4..a3efbae4f97a47f2392ea3d2cb8b897736b755bd 100644 --- a/src/KPI Generator/Reportings/Project/ProjectReporting.cs +++ b/src/KPI Generator/Reportings/Project/ProjectReporting.cs @@ -1,12 +1,13 @@ using Coscine.Database.DataModel; using Coscine.Database.Models; using Coscine.Database.ReturnObjects; +using Coscine.KpiGenerator.Utils; using Coscine.ResourceTypes; -using KPIGenerator.Utils; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using static KPIGenerator.Utils.CommandLineOptions; -namespace KPIGenerator.Reportings.Project; +namespace Coscine.KpiGenerator.Reportings.Project; public class ProjectReporting : Reporting<ProjectReportingOptions> { @@ -16,7 +17,7 @@ public class ProjectReporting : Reporting<ProjectReportingOptions> private readonly ProjectQuotaModel _projectQuotaModel; private readonly ResourceTypeModel _resourceTypeModel; - public ProjectReporting(ProjectReportingOptions options) : base(options) + public ProjectReporting(ProjectReportingOptions options, ILogger _logger) : base(options, _logger) { ReportingFileName = "projects.json"; _projectModel = new ProjectModel(); @@ -29,6 +30,7 @@ public class ProjectReporting : Reporting<ProjectReportingOptions> public override IEnumerable<ReportingFileObject> GenerateReporting() { Console.WriteLine($" - {GetType().Name}: Begin reporting generation"); + _logger.LogInformation("{Name}: Begin reporting generation", GetType().Name); var projects = _projectModel.GetAllWhere(r => r.Deleted.Equals(true) || r.Deleted.Equals(false)); var reportingFiles = new List<ReportingFileObject>(); var returnObjects = Generate(projects); @@ -40,6 +42,7 @@ public class ProjectReporting : Reporting<ProjectReportingOptions> Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjects, Formatting.Indented)), }); Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathGeneral(ReportingFileName)}\" generated successfully"); + _logger.LogInformation("{Name}: Generated successfully. {reportingFiles}", GetType().Name, reportingFiles); // Per Organization reportingFiles.AddRange(GeneratePerOrganization(returnObjects)); @@ -48,7 +51,7 @@ public class ProjectReporting : Reporting<ProjectReportingOptions> return reportingFiles; } - private List<ReturnObject> Generate(IEnumerable<Coscine.Database.DataModel.Project> projects) + private List<ReturnObject> Generate(IEnumerable<Database.DataModel.Project> projects) { var returnObjects = new List<ReturnObject>(); foreach (var project in projects) @@ -83,16 +86,19 @@ public class ProjectReporting : Reporting<ProjectReportingOptions> { organization = _otherOrganization; Console.WriteLine($" WARNING!: Organization \"{entry.RorUrl}\" could not be correctly identified. Will use \"{_otherOrganization.RorUrl}\"."); + _logger.LogWarning("Organization {ror} could not be correctly identified.", entry.RorUrl); } var returnObjectsForOrganization = returnObjects.Where(ro => ro.Organizations.Select(o => o.RorUrl).Any(e => e.Contains(entry.RorUrl))).ToList(); - reportingFilesPerOrganization.Add(new ReportingFileObject + var reportingFile = new ReportingFileObject { Path = GetReportingPathOrganization(organization.RorUrl, ReportingFileName), Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjectsForOrganization, Formatting.Indented)) - }); + }; + reportingFilesPerOrganization.Add(reportingFile); Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathOrganization(organization.RorUrl, ReportingFileName)}\" generated successfully"); + _logger.LogInformation("{Name}: Generated successfully {ReportingFile}.", GetType().Name, reportingFile); } return reportingFilesPerOrganization; @@ -131,13 +137,13 @@ public class ProjectReporting : Reporting<ProjectReportingOptions> }, TotalUsed = new QuotaDimObject() { - Value = CalculateUsedForAll(_resourceTypeModel.GetById(projectQuota.ResourceTypeId), projectId), - Unit = QuotaUnit.BYTE, + Value = CalculateUsedForAll(_resourceTypeModel.GetById(projectQuota.ResourceTypeId), projectId, QuotaUnit.GibiBYTE), + Unit = QuotaUnit.GibiBYTE, }, }; } - private int CalculateUsedForAll(ResourceType resourceType, Guid projectId) + private float CalculateUsedForAll(ResourceType resourceType, Guid projectId, QuotaUnit outputInThisUnit) { var resources = _resourceModel.GetAllWhere((resource) => (from projectResource in resource.ProjectResources @@ -151,19 +157,28 @@ public class ProjectReporting : Reporting<ProjectReportingOptions> var rt = ResourceTypeFactory.Instance.GetResourceType(resource); if (rt.GetResourceTypeInformation().Result.IsQuotaAvailable) { - return rt.GetResourceQuotaUsed(resource.Id.ToString(), _resourceModel.GetResourceTypeOptions(resource.Id)).Result; + try + { + var usedBytes = rt.GetResourceQuotaUsed(resource.Id.ToString(), _resourceModel.GetResourceTypeOptions(resource.Id)).Result; + return Helpers.ConvertCapacityUnits(new QuotaDimObject() { Value = usedBytes, Unit = QuotaUnit.BYTE }, outputInThisUnit); + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + return 0f; + } } else { - return 0; + return 0f; } } ); - return (int)used; + return used; } - private int CalculateAllocatedForAll(ResourceType resourceType, Guid projectId) + private float CalculateAllocatedForAll(ResourceType resourceType, Guid projectId) { var resources = _resourceModel.GetAllWhere((resource) => (from projectResource in resource.ProjectResources @@ -177,15 +192,23 @@ public class ProjectReporting : Reporting<ProjectReportingOptions> var rt = ResourceTypeFactory.Instance.GetResourceType(resource); if (rt.GetResourceTypeInformation().Result.IsQuotaAvailable) { - return rt.GetResourceQuotaAvailable(resource.Id.ToString(), _resourceModel.GetResourceTypeOptions(resource.Id)).Result; + try + { + return rt.GetResourceQuotaAvailable(resource.Id.ToString(), _resourceModel.GetResourceTypeOptions(resource.Id)).Result; + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + return 0f; + } } else { - return 0; + return 0f; } }); - return (int)allocated; + return allocated; } private List<Organization> GetOrganizations(ProjectObject project) diff --git a/src/KPI Generator/Reportings/Project/ResourceTypeQuotaReturnObject.cs b/src/KPI Generator/Reportings/Project/ResourceTypeQuotaReturnObject.cs index c5f3303b503b6ed0a670c7a335f40b4ffa316be9..d5c7fa78aea9dd864e5848d0a992403fd61198c4 100644 --- a/src/KPI Generator/Reportings/Project/ResourceTypeQuotaReturnObject.cs +++ b/src/KPI Generator/Reportings/Project/ResourceTypeQuotaReturnObject.cs @@ -1,6 +1,6 @@ using Coscine.Database.ReturnObjects; -namespace KPIGenerator.Reportings.Project; +namespace Coscine.KpiGenerator.Reportings.Project; public class ResourceTypeQuotaReturnObject { diff --git a/src/KPI Generator/Reportings/Project/ReturnObject.cs b/src/KPI Generator/Reportings/Project/ReturnObject.cs index b888462333e02f6bde2f08249c75b0d3a731d387..5ce9a206ed33395a0d458f78758f247f1a9083ac 100644 --- a/src/KPI Generator/Reportings/Project/ReturnObject.cs +++ b/src/KPI Generator/Reportings/Project/ReturnObject.cs @@ -1,7 +1,7 @@ using Coscine.Database.ReturnObjects; -using KPIGenerator.Utils; +using Coscine.KpiGenerator.Utils; -namespace KPIGenerator.Reportings.Project; +namespace Coscine.KpiGenerator.Reportings.Project; /// <summary> /// Object containing the JSON structure for the reporting diff --git a/src/KPI Generator/Reportings/Resource/ResourceReporting.cs b/src/KPI Generator/Reportings/Resource/ResourceReporting.cs index 0648a027edeefeca003d9332cf9b9499d718c35f..d5b79091b19ff0bab59c10fcfe22773974aac9f9 100644 --- a/src/KPI Generator/Reportings/Resource/ResourceReporting.cs +++ b/src/KPI Generator/Reportings/Resource/ResourceReporting.cs @@ -1,12 +1,13 @@ using Coscine.Database.Models; using Coscine.Database.ReturnObjects; +using Coscine.KpiGenerator.Utils; using Coscine.ResourceTypes; using Coscine.ResourceTypes.Base; -using KPIGenerator.Utils; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using static KPIGenerator.Utils.CommandLineOptions; -namespace KPIGenerator.Reportings.Resource; +namespace Coscine.KpiGenerator.Reportings.Resource; public class ResourceReporting : Reporting<ResourceReportingOptions> { @@ -14,7 +15,7 @@ public class ResourceReporting : Reporting<ResourceReportingOptions> private readonly ProjectModel _projectModel; private readonly ProjectResourceModel _projectResourceModel; - public ResourceReporting(ResourceReportingOptions options) : base(options) + public ResourceReporting(ResourceReportingOptions options, ILogger _logger) : base(options, _logger) { ReportingFileName = "resources.json"; _resourceModel = new ResourceModel(); @@ -25,6 +26,7 @@ public class ResourceReporting : Reporting<ResourceReportingOptions> public override IEnumerable<ReportingFileObject> GenerateReporting() { Console.WriteLine($" - {GetType().Name}: Begin reporting generation"); + _logger.LogInformation("{Name}: Begin reporting generation", GetType().Name); var resources = _resourceModel.GetAllWhere(r => r.Deleted.Equals(true) || r.Deleted.Equals(false)); var reportingFiles = new List<ReportingFileObject>(); var returnObjects = Generate(resources); @@ -36,6 +38,7 @@ public class ResourceReporting : Reporting<ResourceReportingOptions> Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjects, Formatting.Indented)) }); Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathGeneral(ReportingFileName)}\" generated successfully"); + _logger.LogInformation("{Name}: Generated successfully. {reportingFiles}", GetType().Name, reportingFiles); // Per Organization reportingFiles.AddRange(GeneratePerOrganization(returnObjects)); @@ -44,7 +47,7 @@ public class ResourceReporting : Reporting<ResourceReportingOptions> return reportingFiles; } - private List<ReturnObject> Generate(IEnumerable<Coscine.Database.DataModel.Resource> resources) + private List<ReturnObject> Generate(IEnumerable<Database.DataModel.Resource> resources) { var returnObjects = new List<ReturnObject>(); foreach (var resource in resources) @@ -82,15 +85,18 @@ public class ResourceReporting : Reporting<ResourceReportingOptions> { organization = _otherOrganization; Console.WriteLine($" WARNING!: Organization \"{entry.RorUrl}\" could not be correctly identified. Will use \"{_otherOrganization.RorUrl}\"."); + _logger.LogWarning("Organization {ror} could not be correctly identified.", entry.RorUrl); } var returnObjectsForOrganization = returnObjects.Where(ro => ro.Organizations.Select(o => o.RorUrl).Any(e => e.Equals(entry.RorUrl))).ToList(); - reportingFilesPerOrganization.Add(new ReportingFileObject + var reportingFile = new ReportingFileObject { Path = GetReportingPathOrganization(organization.RorUrl, ReportingFileName), Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjectsForOrganization, Formatting.Indented)) - }); + }; + reportingFilesPerOrganization.Add(reportingFile); Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathOrganization(organization.RorUrl, ReportingFileName)}\" generated successfully"); + _logger.LogInformation("{Name}: Generated successfully {ReportingFile}.", GetType().Name, reportingFile); } return reportingFilesPerOrganization; } @@ -108,7 +114,7 @@ public class ResourceReporting : Reporting<ResourceReportingOptions> } } - private static ResourceQuotaReturnObject? GetResourceQuota(Coscine.Database.DataModel.Resource resource) + private ResourceQuotaReturnObject? GetResourceQuota(Database.DataModel.Resource resource) { BaseResourceType? resourceTypeDefinition; try @@ -123,7 +129,15 @@ public class ResourceReporting : Reporting<ResourceReportingOptions> if (resourceTypeDefinition is not null && resourceTypeDefinition.GetResourceTypeInformation().Result.IsQuotaAdjustable) { - return Helpers.CreateResourceQuotaReturnObject(resource, resourceTypeDefinition); + try + { + return Helpers.CreateResourceQuotaReturnObject(resource, resourceTypeDefinition); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error: {message} for {@resource}", ex.Message, resource); + return null; + } } else { diff --git a/src/KPI Generator/Reportings/Resource/ReturnObject.cs b/src/KPI Generator/Reportings/Resource/ReturnObject.cs index 94e64e14bf41df3b0c304519e7f58c100bc99e41..e449cc859dbbb699ca1497b8020f7212b375cf27 100644 --- a/src/KPI Generator/Reportings/Resource/ReturnObject.cs +++ b/src/KPI Generator/Reportings/Resource/ReturnObject.cs @@ -1,7 +1,7 @@ using Coscine.Database.ReturnObjects; -using KPIGenerator.Utils; +using Coscine.KpiGenerator.Utils; -namespace KPIGenerator.Reportings.Resource; +namespace Coscine.KpiGenerator.Reportings.Resource; /// <summary> /// Object containing the JSON structure for the reporting diff --git a/src/KPI Generator/Reportings/System/MaintenanceReturnObject.cs b/src/KPI Generator/Reportings/System/MaintenanceReturnObject.cs index 12d723db243420234917ede8cc11c4f0c16a4793..2d71f8c017f342193ab5232f5fb8c282446843b3 100644 --- a/src/KPI Generator/Reportings/System/MaintenanceReturnObject.cs +++ b/src/KPI Generator/Reportings/System/MaintenanceReturnObject.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace KPIGenerator.Reportings.System; +namespace Coscine.KpiGenerator.Reportings.System; public class MaintenanceBannerObject { diff --git a/src/KPI Generator/Reportings/System/ReturnObject.cs b/src/KPI Generator/Reportings/System/ReturnObject.cs index 4458b8fb04889e57161070c67d97b2f369ff3c3e..01b737e2134eadb760526ffcfff1975af61c805f 100644 --- a/src/KPI Generator/Reportings/System/ReturnObject.cs +++ b/src/KPI Generator/Reportings/System/ReturnObject.cs @@ -1,4 +1,4 @@ -namespace KPIGenerator.Reportings.System; +namespace Coscine.KpiGenerator.Reportings.System; /// <summary> /// Object containing the JSON structure for the reporting diff --git a/src/KPI Generator/Reportings/System/SystemReporting.cs b/src/KPI Generator/Reportings/System/SystemReporting.cs index 23fe4529e5004895be42cffbcd2488489b224a5d..c1eac988dd6c1a4611c7e632f2f4f3369741ab42 100644 --- a/src/KPI Generator/Reportings/System/SystemReporting.cs +++ b/src/KPI Generator/Reportings/System/SystemReporting.cs @@ -1,17 +1,18 @@ using Coscine.Configuration; -using KPIGenerator.Utils; +using Coscine.KpiGenerator.Utils; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System.Net.Http.Headers; using System.Text; using static KPIGenerator.Utils.CommandLineOptions; -namespace KPIGenerator.Reportings.System; +namespace Coscine.KpiGenerator.Reportings.System; public class SystemReporting : Reporting<SystemReportingOptions> { private readonly ConsulConfiguration _configuration; - public SystemReporting(SystemReportingOptions options) : base(options) + public SystemReporting(SystemReportingOptions options, ILogger _logger) : base(options, _logger) { ReportingFileName = "system_status.json"; _configuration = new ConsulConfiguration(); @@ -20,6 +21,7 @@ public class SystemReporting : Reporting<SystemReportingOptions> public override IEnumerable<ReportingFileObject> GenerateReporting() { Console.WriteLine($" - {GetType().Name}: Begin reporting generation"); + _logger.LogInformation("{Name}: Begin reporting generation", GetType().Name); var reportingFiles = new List<ReportingFileObject>(); var returnObject = GetSystemStatus(); @@ -30,6 +32,7 @@ public class SystemReporting : Reporting<SystemReportingOptions> Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObject, Formatting.Indented)) }); Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathGeneral(ReportingFileName)}\" generated successfully"); + _logger.LogInformation("{Name}: Generated successfully. {reportingFiles}", GetType().Name, reportingFiles); Console.WriteLine(); return reportingFiles; diff --git a/src/KPI Generator/Reportings/User/ReturnObject.cs b/src/KPI Generator/Reportings/User/ReturnObject.cs index 05318a2a965f334c1bc4cf1c7c15fb56701d189f..0e5c1d7606b0a5cfa7b316c12d2b24e1274f69ae 100644 --- a/src/KPI Generator/Reportings/User/ReturnObject.cs +++ b/src/KPI Generator/Reportings/User/ReturnObject.cs @@ -1,7 +1,7 @@ using Coscine.Database.ReturnObjects; -using KPIGenerator.Utils; +using Coscine.KpiGenerator.Utils; -namespace KPIGenerator.Reportings.User; +namespace Coscine.KpiGenerator.Reportings.User; /// <summary> /// Object containing the JSON structure for the reporting diff --git a/src/KPI Generator/Reportings/User/UserReporting.cs b/src/KPI Generator/Reportings/User/UserReporting.cs index 43c8b55e9ac649fc7f7ce63e8f2f472144e989f0..87c23ba768babb42c3468288aa7c4daa0b54407a 100644 --- a/src/KPI Generator/Reportings/User/UserReporting.cs +++ b/src/KPI Generator/Reportings/User/UserReporting.cs @@ -1,12 +1,13 @@ using Coscine.Database.DataModel; using Coscine.Database.Models; +using Coscine.KpiGenerator.Utils; using Coscine.Metadata; -using KPIGenerator.Utils; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using VDS.RDF.Query; using static KPIGenerator.Utils.CommandLineOptions; -namespace KPIGenerator.Reportings.User; +namespace Coscine.KpiGenerator.Reportings.User; public class UserReporting : Reporting<UserReportingOptions> { @@ -19,7 +20,7 @@ public class UserReporting : Reporting<UserReportingOptions> private readonly LogModel _logModel; private readonly IEnumerable<ExternalAuthenticator> _loginProviders; - public UserReporting(UserReportingOptions options) : base(options) + public UserReporting(UserReportingOptions options, ILogger _logger) : base(options, _logger) { ReportingFileName = "users.json"; _externalAuthenticatorModel = new ExternalAuthenticatorModel(); @@ -36,6 +37,7 @@ public class UserReporting : Reporting<UserReportingOptions> public override IEnumerable<ReportingFileObject> GenerateReporting() { Console.WriteLine($" - {GetType().Name}: Begin reporting generation"); + _logger.LogInformation("{Name}: Begin reporting generation", GetType().Name); var users = _userModel.GetAllWhere((user) => user.Tosaccepteds.Any()); var reportingFiles = new List<ReportingFileObject>(); var returnObjects = Generate(users); @@ -47,6 +49,7 @@ public class UserReporting : Reporting<UserReportingOptions> Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjects, Formatting.Indented)) }); Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathGeneral(ReportingFileName)}\" generated successfully"); + _logger.LogInformation("{Name}: Generated successfully. {reportingFiles}", GetType().Name, reportingFiles); // Per Organization reportingFiles.AddRange(GeneratePerOrganization(returnObjects)); @@ -55,7 +58,7 @@ public class UserReporting : Reporting<UserReportingOptions> return reportingFiles; } - private List<ReturnObject> Generate(IEnumerable<Coscine.Database.DataModel.User> users) + private List<ReturnObject> Generate(IEnumerable<Database.DataModel.User> users) { var returnObjects = new List<ReturnObject>(); foreach (var user in users) @@ -86,16 +89,19 @@ public class UserReporting : Reporting<UserReportingOptions> { organization = _otherOrganization; Console.WriteLine($" WARNING!: Organization \"{entry.RorUrl}\" could not be correctly identified. Will use \"{_otherOrganization.RorUrl}\"."); + _logger.LogWarning("Organization {ror} could not be correctly identified.", entry.RorUrl); } var returnObjectsForOrganization = returnObjects.Where(ro => ro.Organizations.Select(o => o.RorUrl).Any(e => e.Contains(entry.RorUrl))).ToList(); - reportingFilesPerOrganization.Add(new ReportingFileObject + var reportingFile = new ReportingFileObject { Path = GetReportingPathOrganization(organization.RorUrl, ReportingFileName), Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjectsForOrganization, Formatting.Indented)) - }); + }; + reportingFilesPerOrganization.Add(reportingFile); Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathOrganization(organization.RorUrl, ReportingFileName)}\" generated successfully"); + _logger.LogInformation("{Name}: Generated successfully {ReportingFile}.", GetType().Name, reportingFile); } return reportingFilesPerOrganization; } @@ -158,8 +164,8 @@ public class UserReporting : Reporting<UserReportingOptions> result.Add(orgOrcid); break; case "shibboleth": - // Find the RoR first based on the Organization Entity Id, then try based on the user's External Id. - var orgShibboleth = GetOrganizationByExternalOrEntityId(externalId.Organization) ?? GetOrganizationByExternalOrEntityId(externalId.ExternalId1); + // Find the RoR first based on the user's External Id, then try based on the Organization Entity Id. + var orgShibboleth = GetOrganizationByExternalOrEntityId(externalId.ExternalId1) ?? GetOrganizationByExternalOrEntityId(externalId.Organization); if (orgShibboleth is null) { Console.WriteLine($" No {searchedEntityType} found for user with ID \"{id}\" and login provider {loginProvider.DisplayName}"); @@ -195,6 +201,19 @@ public class UserReporting : Reporting<UserReportingOptions> } } + // Special case until `https://ror.org/%20https://ror.org/` is moved to `https://ror.org/_other` + if (result.Any(o => o.RorUrl.Equals("https://ror.org/%20https://ror.org/"))) + { + foreach (var entry in result) + { + if (entry.RorUrl.Equals("https://ror.org/%20https://ror.org/")) + { + entry.Name = _otherOrganization.Name; + entry.RorUrl = _otherOrganization.RorUrl; + } + } + } + return result.DistinctBy(e => e.RorUrl).ToList(); } diff --git a/src/KPI Generator/Utils/Organization.cs b/src/KPI Generator/Utils/Organization.cs index 6a9b80a53d32ffdffbecbae1a7f452170a30a3ec..d351b884823df91bface67c5931a8fb13b3dd2ea 100644 --- a/src/KPI Generator/Utils/Organization.cs +++ b/src/KPI Generator/Utils/Organization.cs @@ -1,4 +1,4 @@ -namespace KPIGenerator.Utils; +namespace Coscine.KpiGenerator.Utils; public class Organization { diff --git a/src/KPI Generator/Utils/ReportingFile.cs b/src/KPI Generator/Utils/ReportingFile.cs index 282bbff7e0a347eb6dd69bd12140ce5d17bff5be..20fe78fa2163f65e2864e98b332e1c642287619f 100644 --- a/src/KPI Generator/Utils/ReportingFile.cs +++ b/src/KPI Generator/Utils/ReportingFile.cs @@ -1,4 +1,4 @@ -namespace KPIGenerator.Utils; +namespace Coscine.KpiGenerator.Utils; public class ReportingFileObject { diff --git a/src/KPI Generator/nlog.config b/src/KPI Generator/nlog.config new file mode 100644 index 0000000000000000000000000000000000000000..32f9d3ece355f8ede184fee4cd7a428677826340 --- /dev/null +++ b/src/KPI Generator/nlog.config @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8" ?> +<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + throwExceptions="true" + internalLogFile="Logs/internal-nlog.txt" + internalLogLevel="Trace"> + + <targets> + <!-- Write logs to File --> + <target xsi:type="File" name="fileLog" fileName="C:/coscine/logs/${assembly-name}/${assembly-version}/log-${shortdate}.log" > + <layout xsi:type="CompoundLayout"> + <layout xsi:type="JsonLayout" EscapeForwardSlash="true"> + <attribute layout="${longdate}" name="Timestamp"/> + <attribute layout="${level:upperCase=true}" name="Level"/> + <attribute layout="${message}" name="Message"/> + <attribute layout="${exception:format=tostring,StackTrace}" name="Exception"/> + <attribute layout="${ndlc}" name="Context"/> + <attribute layout="${event-properties:item=Metric}" name="Alarm" encode="false"/> + <attribute name="EventProperties" encode="false" > + <layout xsi:type='JsonLayout' includeAllProperties="true" maxRecursionLimit="2"/> + </attribute> + </layout> + <layout xsi:type='SimpleLayout' text="," /> + </layout> + </target> + </targets> + + + <rules> + <!--All logs, including from Microsoft, Level Trace--> + <logger name="*" minlevel="Trace" writeTo="fileLog" /> + </rules> +</nlog> \ No newline at end of file