Skip to content

Commit

Permalink
Extended diagnose commands with useful hints plus a lot of general ou…
Browse files Browse the repository at this point in the history
…tput improvements while diagnosing
  • Loading branch information
ThomasArdal committed Dec 15, 2023
1 parent 16e6170 commit cf36ded
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/Elmah.Io.Cli/Diagnose/DiagnoseBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ protected static void DiagnosePackageVersion(Dictionary<string, string> packages

protected static void ReportError(string message)
{
AnsiConsole.MarkupLine($"[red]- {message}[/]");
AnsiConsole.MarkupLine($"[red]:cross_mark: {message}[/]");
FoundError = true;
}

Expand Down
11 changes: 10 additions & 1 deletion src/Elmah.Io.Cli/Diagnose/ElmahIo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Elmah.Io.Cli.Diagnose
{
internal class ElmahIo : DiagnoseBase
{
internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose)
internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose, Dictionary<string, List<string>> hints)
{
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]Elmah.Io[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, "elmah.io", "elmah.io.aspnet", "elmah.io.mvc", "elmah.io.webapi");
Expand Down Expand Up @@ -40,6 +40,15 @@ internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> p
{
ReportError("Web.config file not found.");
}

if (!hints.ContainsKey("Elmah.Io"))
{
hints.Add("Elmah.Io", new List<string>
{
"Make sure that you have the [rgb(13,165,142)]elmah.corelibrary[/] NuGet package installed in the latest stable version.",
"Make sure that your [grey]web.config[/] file is valid and that it contains the ELMAH configuration.",
});
}
}
}
}
38 changes: 25 additions & 13 deletions src/Elmah.Io.Cli/Diagnose/ElmahIoAspNetCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ namespace Elmah.Io.Cli.Diagnose
{
internal class ElmahIoAspNetCore : DiagnoseBase
{
internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose)
private const string PackageName = "Elmah.Io.AspNetCore";

internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose, Dictionary<string, List<string>> hints)
{
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]Elmah.Io.AspNetCore[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, "elmah.io.aspnetcore");
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]{PackageName}[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, PackageName.ToLowerInvariant());

var projectDir = packageFile.Directory;
var startupPath = Path.Combine(projectDir.FullName, "Startup.cs");
Expand Down Expand Up @@ -50,7 +52,7 @@ internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> p
}

if (!foundElmahIoConfig)
ReportError("A call to AddElmahIo and UseElmahIo was not found in Startup.cs or Program.cs.");
ReportError("A call to [invert]AddElmahIo[/] and [invert]UseElmahIo[/] was not found in [grey]Startup.cs[/] or [grey]Program.cs[/].");

else if (foundElmahIoConfig && !string.IsNullOrWhiteSpace(fileWithElmahConfig))
{
Expand All @@ -66,23 +68,23 @@ internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> p
var useUmbracoIndex = fileWithElmahConfig.IndexOf(".UseUmbraco(");

if (useDeveloperExceptionPageIndex != -1 && index < useDeveloperExceptionPageIndex)
ReportError("UseElmahIo must be called after UseDeveloperExceptionPage");
ReportError("[invert]UseElmahIo[/] must be called after [invert]UseDeveloperExceptionPage[/]");
else if (useExceptionHandlerIndex != -1 && index < useExceptionHandlerIndex)
ReportError("UseElmahIo must be called after UseExceptionHandler");
ReportError("[invert]UseElmahIo[/] must be called after [invert]UseExceptionHandler[/]");
else if (useAuthorizationIndex != -1 && index < useAuthorizationIndex)
ReportError("UseElmahIo must be called after UseAuthorization");
ReportError("[invert]UseElmahIo[/] must be called after [invert]UseAuthorization[/]");
else if (useAuthenticationIndex != -1 && index < useAuthenticationIndex)
ReportError("UseElmahIo must be called after UseAuthentication");
ReportError("[invert]UseElmahIo[/] must be called after [invert]UseAuthentication[/]");
else if (useEndpointsIndex != -1 && index > useEndpointsIndex)
ReportError("UseElmahIo must be called before UseEndpoints");
ReportError("[invert]UseElmahIo[/] must be called before [invert]UseEndpoints[/]");
else if (mapControllerRouteIndex != -1 && index > mapControllerRouteIndex)
ReportError("UseElmahIo must be called before MapControllerRoute");
ReportError("[invert]UseElmahIo[/] must be called before [invert]MapControllerRoute[/]");
else if (useMvcIndex != -1 && index > useMvcIndex)
ReportError("UseElmahIo must be called before UseMvc");
ReportError("[invert]UseElmahIo[/] must be called before [invert]UseMvc[/]");
else if (usePiranhaIndex != -1 && index > usePiranhaIndex)
ReportError("UseElmahIo must be called before UsePiranha");
ReportError("[invert]UseElmahIo[/] must be called before [invert]UsePiranha[/]");
else if (useUmbracoIndex != -1 && index > useUmbracoIndex)
ReportError("UseElmahIo must be called before UseUmbraco");
ReportError("[invert]UseElmahIo[/] must be called before [invert]UseUmbraco[/]");
}

// If we haven't found API key and log ID yet, try looking in appsettings.json
Expand All @@ -99,6 +101,16 @@ internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> p
}

DiagnoseKeys(apiKey, logId, verbose);

if (!hints.ContainsKey(PackageName))
{
hints.Add(PackageName, new List<string>
{
"Make sure that you are calling both the [invert]AddElmahIo[/] and [invert]UseElmahIo[/] methods in the [grey]Program.cs[/] or [grey]Startup.cs[/] file.",
"Make sure that you call the [invert]UseElmahIo[/] method after invoking other [invert]Use*[/] methods that in any way inspect exceptions (like [invert]UseDeveloperExceptionPage[/] and [invert]UseExceptionHandler[/]).",
"Make sure that you call the [invert]UseElmahIo[/] method before invoking [invert]UseMvc[/], [invert]UseEndpoints[/], and similar.",
});
}
}
}
}
19 changes: 15 additions & 4 deletions src/Elmah.Io.Cli/Diagnose/ElmahIoExtensionsLogging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ namespace Elmah.Io.Cli.Diagnose
{
internal class ElmahIoExtensionsLogging : DiagnoseBase
{
internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose)
private const string PackageName = "Elmah.Io.Extensions.Logging";

internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose, Dictionary<string, List<string>> hints)
{
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]Elmah.Io.Extensions.Logging[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, "elmah.io.extensions.logging");
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]{PackageName}[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, PackageName.ToLowerInvariant());

var projectDir = packageFile.Directory;
var programPath = Path.Combine(projectDir.FullName, "Program.cs");
Expand All @@ -21,7 +23,7 @@ internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> p
{
var programCs = File.ReadAllText(programPath);
if (!programCs.Contains(".AddElmahIo(") || !programCs.Contains("using Elmah.Io.Extensions.Logging"))
ReportError("A call to AddElmahIo was not found in Program.cs. Both Elmah.Io.Extensions.Logging and Elmah.Io.AspNetCore provide a method named AddElmahIo. Make sure to call both if you have both packages installed.");
ReportError($"A call to [invert]AddElmahIo[/] was not found in [invert]Program.cs[/]. Both [rgb(13,165,142)]{PackageName}[/] and [rgb(13,165,142)]Elmah.Io.AspNetCore[/] provide a method named [invert]AddElmahIo[/]. Make sure to call both if you have both packages installed.");

var apiKeyLookup = LookupString(programCs, ".AddElmahIo(", ".ApiKey = \"", 32);
if (apiKeyLookup != null) apiKey = apiKeyLookup;
Expand All @@ -43,6 +45,15 @@ internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> p
}

DiagnoseKeys(apiKey, logId, verbose);

if (!hints.ContainsKey(PackageName))
{
hints.Add(PackageName, new List<string>
{
"Make sure that you are calling the [invert]AddElmahIo[/] method in the [grey]Program.cs[/] file.",
"Make sure that the logging configuration in both code and the [grey]appsettings.json[/] file allows the log severity you are expecting to see in elmah.io.",
});
}
}
}
}
17 changes: 14 additions & 3 deletions src/Elmah.Io.Cli/Diagnose/ElmahIoLog4Net.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ namespace Elmah.Io.Cli.Diagnose
{
internal class ElmahIoLog4Net : DiagnoseBase
{
internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose)
private const string PackageName = "Elmah.Io.Log4Net";

internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose, Dictionary<string, List<string>> hints)
{
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]Elmah.Io.Log4Net[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, "elmah.io.log4net");
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]{PackageName}[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, PackageName.ToLowerInvariant());

var projectDir = packageFile.Directory;
var webConfigPath = Path.Combine(projectDir.FullName, "web.config");
Expand Down Expand Up @@ -58,6 +60,15 @@ bool FindConfig(string file)
{
AnsiConsole.MarkupLine("[grey]No file content found for log4net[/]");
}

if (!hints.ContainsKey(PackageName))
{
hints.Add(PackageName, new List<string>
{
$"Make sure that your [grey]log4net.config[/] file is valid and contains the code for [rgb(13,165,142)]{PackageName}[/].",
"Include the following app setting in the [grey]app.config[/]/[grey]web.config[/] file to enable log4net's internal logger and inspect the console for any errors: [invert]<add key=\"log4net.Internal.Debug\" value=\"true\"/>[/]."
});
}
}
}
}
20 changes: 16 additions & 4 deletions src/Elmah.Io.Cli/Diagnose/ElmahIoNLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ namespace Elmah.Io.Cli.Diagnose
{
internal class ElmahIoNLog : DiagnoseBase
{
internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose)
private const string PackageName = "Elmah.Io.NLog";

internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose, Dictionary<string, List<string>> hints)
{
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]Elmah.Io.NLog[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, "elmah.io.nlog");
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]{PackageName}[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, PackageName.ToLowerInvariant());

var projectDir = packageFile.Directory;
var webConfigPath = Path.Combine(projectDir.FullName, "web.config");
Expand Down Expand Up @@ -42,7 +44,7 @@ bool FindConfig(string file)
{
foundElmahIoConfig = true;
fileContent = File.ReadAllText(nlogConfigPath);
AnsiConsole.MarkupLine("Validating nlog.config. Any errors may be resolved by changing the target type from [grey]elmah.io[/] to [grey]elmahio:elmah.io[/].");
AnsiConsole.MarkupLine("Validating [grey]nlog.config[/]. Any errors may be resolved by changing the target type from [invert]elmah.io[/] to [invert]elmahio:elmah.io[/].");
ValidateXmlAgainstSchema(
"nlog.config",
fileContent,
Expand Down Expand Up @@ -74,6 +76,16 @@ bool FindConfig(string file)
{
AnsiConsole.MarkupLine("[grey]No file content found for log4net[/]");
}

if (!hints.ContainsKey(PackageName))
{
hints.Add(PackageName, new List<string>
{
$"Make sure that your [grey]nlog.config[/] file is valid and contains the code for [rgb(13,165,142)]{PackageName}[/].",
"Always make sure to call [invert]LogManager.Shutdown()[/] before exiting the application to make sure that all log messages are flushed.",
"Extend the [invert]nlog[/] element with [invert]internalLogLevel=\"Warn\" internalLogFile=\"c:\\temp\\nlog-internal.log[/] and inspect that log file for any internal NLog errors.",
});
}
}
}
}
18 changes: 15 additions & 3 deletions src/Elmah.Io.Cli/Diagnose/SerilogSinksElmahIo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ namespace Elmah.Io.Cli.Diagnose
{
internal class SerilogSinksElmahIo : DiagnoseBase
{
internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose)
private const string PackageName = "Serilog.Sinks.ElmahIo";

internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> packagesFound, bool verbose, Dictionary<string, List<string>> hints)
{
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]Serilog.Sinks.ElmahIo[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, "serilog.sinks.elmahio");
AnsiConsole.MarkupLine($"Found [rgb(13,165,142)]{PackageName}[/] in [grey]{packageFile.FullName}[/].");
DiagnosePackageVersion(packagesFound, verbose, PackageName.ToLowerInvariant());

var projectDir = packageFile.Directory;
var options = new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true };
Expand All @@ -33,6 +35,16 @@ internal static void Diagnose(FileInfo packageFile, Dictionary<string, string> p

if (!foundElmahIoConfig)
ReportError("Serilog configuration for the elmah.io sink could not be found.");

if (!hints.ContainsKey(PackageName))
{
hints.Add(PackageName, new List<string>
{
$"Make sure that your Serilog configuration calls the [invert].ElmahIo[/] method to set up the sink.",
"Always make sure to call [invert]Log.CloseAndFlush()[/] before exiting the application to make sure that all log messages are flushed.",
"Set up Serilog's SelfLog to inspect any errors happening inside Serilog or the elmah.io sink: [invert]Serilog.Debugging.SelfLog.Enable(msg => Debug.WriteLine(msg));[/].",
});
}
}
}
}
Loading

0 comments on commit cf36ded

Please sign in to comment.