Do Pandoc logging only once

This commit is contained in:
Thorsten Sommer 2026-01-19 16:25:02 +01:00
parent ce53474714
commit ab0803272e
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
2 changed files with 118 additions and 61 deletions

View File

@ -26,6 +26,11 @@ public static partial class Pandoc
private static readonly Version MINIMUM_REQUIRED_VERSION = new (3, 7, 0, 2); private static readonly Version MINIMUM_REQUIRED_VERSION = new (3, 7, 0, 2);
private static readonly Version FALLBACK_VERSION = new (3, 7, 0, 2); private static readonly Version FALLBACK_VERSION = new (3, 7, 0, 2);
/// <summary>
/// Tracks whether the first availability check log has been written to avoid log spam on repeated calls.
/// </summary>
private static bool HAS_LOGGED_AVAILABILITY_CHECK_ONCE;
/// <summary> /// <summary>
/// Prepares a Pandoc process by using the Pandoc process builder. /// Prepares a Pandoc process by using the Pandoc process builder.
/// </summary> /// </summary>
@ -41,18 +46,26 @@ public static partial class Pandoc
/// <returns>True, if pandoc is available and the minimum required version is met, else false.</returns> /// <returns>True, if pandoc is available and the minimum required version is met, else false.</returns>
public static async Task<PandocInstallation> CheckAvailabilityAsync(RustService rustService, bool showMessages = true, bool showSuccessMessage = true) public static async Task<PandocInstallation> CheckAvailabilityAsync(RustService rustService, bool showMessages = true, bool showSuccessMessage = true)
{ {
//
// Determine if we should log (only on the first call):
//
var shouldLog = !HAS_LOGGED_AVAILABILITY_CHECK_ONCE;
try try
{ {
var preparedProcess = await PreparePandocProcess().AddArgument("--version").BuildAsync(rustService); var preparedProcess = await PreparePandocProcess().AddArgument("--version").BuildAsync(rustService);
LOG.LogDebug("Checking Pandoc availability using executable: '{Executable}' (IsLocal: {IsLocal}).", preparedProcess.StartInfo.FileName, preparedProcess.IsLocal); if (shouldLog)
LOG.LogInformation("Checking Pandoc availability using executable: '{Executable}' (IsLocal: {IsLocal}).", preparedProcess.StartInfo.FileName, preparedProcess.IsLocal);
using var process = Process.Start(preparedProcess.StartInfo); using var process = Process.Start(preparedProcess.StartInfo);
if (process == null) if (process == null)
{ {
if (showMessages) if (showMessages)
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Help, TB("Was not able to check the Pandoc installation."))); await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Help, TB("Was not able to check the Pandoc installation.")));
if (shouldLog)
LOG.LogError("The Pandoc process was not started, it was null. Executable path: '{Executable}'.", preparedProcess.StartInfo.FileName);
LOG.LogInformation("The Pandoc process was not started, it was null. Executable path: '{Executable}'.", preparedProcess.StartInfo.FileName);
return new(false, TB("Was not able to check the Pandoc installation."), false, string.Empty, preparedProcess.IsLocal); return new(false, TB("Was not able to check the Pandoc installation."), false, string.Empty, preparedProcess.IsLocal);
} }
@ -68,9 +81,11 @@ public static partial class Pandoc
if (process.ExitCode != 0) if (process.ExitCode != 0)
{ {
if (showMessages) if (showMessages)
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Error, TB("Pandoc is not available on the system or the process had issues."))); await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Error, TB("Pandoc is not available on the system or the process had issues.")));
if (shouldLog)
LOG.LogError("The Pandoc process exited with code {ProcessExitCode}. Error output: '{ErrorText}'", process.ExitCode, error);
LOG.LogError("The Pandoc process exited with code {ProcessExitCode}. Error output: '{ErrorText}'", process.ExitCode, error);
return new(false, TB("Pandoc is not available on the system or the process had issues."), false, string.Empty, preparedProcess.IsLocal); return new(false, TB("Pandoc is not available on the system or the process had issues."), false, string.Empty, preparedProcess.IsLocal);
} }
@ -78,9 +93,11 @@ public static partial class Pandoc
if (!versionMatch.Success) if (!versionMatch.Success)
{ {
if (showMessages) if (showMessages)
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Terminal, TB("Was not able to validate the Pandoc installation."))); await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Terminal, TB("Was not able to validate the Pandoc installation.")));
if (shouldLog)
LOG.LogError("Pandoc --version returned an invalid format: '{Output}'.", output);
LOG.LogError("Pandoc --version returned an invalid format: {Output}", output);
return new(false, TB("Was not able to validate the Pandoc installation."), false, string.Empty, preparedProcess.IsLocal); return new(false, TB("Was not able to validate the Pandoc installation."), false, string.Empty, preparedProcess.IsLocal);
} }
@ -93,24 +110,34 @@ public static partial class Pandoc
if (showMessages && showSuccessMessage) if (showMessages && showSuccessMessage)
await MessageBus.INSTANCE.SendSuccess(new(Icons.Material.Filled.CheckCircle, string.Format(TB("Pandoc v{0} is installed."), installedVersionString))); await MessageBus.INSTANCE.SendSuccess(new(Icons.Material.Filled.CheckCircle, string.Format(TB("Pandoc v{0} is installed."), installedVersionString)));
LOG.LogInformation("Pandoc v{0} is installed and matches the required version (v{1})", installedVersionString, MINIMUM_REQUIRED_VERSION.ToString()); if (shouldLog)
LOG.LogInformation("Pandoc v{0} is installed and matches the required version (v{1}).", installedVersionString, MINIMUM_REQUIRED_VERSION.ToString());
return new(true, string.Empty, true, installedVersionString, preparedProcess.IsLocal); return new(true, string.Empty, true, installedVersionString, preparedProcess.IsLocal);
} }
if (showMessages) if (showMessages)
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Build, string.Format(TB("Pandoc v{0} is installed, but it doesn't match the required version (v{1})."), installedVersionString, MINIMUM_REQUIRED_VERSION.ToString()))); await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Build, string.Format(TB("Pandoc v{0} is installed, but it doesn't match the required version (v{1})."), installedVersionString, MINIMUM_REQUIRED_VERSION.ToString())));
if (shouldLog)
LOG.LogWarning("Pandoc v{0} is installed, but it does not match the required version (v{1}).", installedVersionString, MINIMUM_REQUIRED_VERSION.ToString());
LOG.LogWarning("Pandoc v{0} is installed, but it does not match the required version (v{1})", installedVersionString, MINIMUM_REQUIRED_VERSION.ToString());
return new(true, string.Format(TB("Pandoc v{0} is installed, but it does not match the required version (v{1})."), installedVersionString, MINIMUM_REQUIRED_VERSION.ToString()), false, installedVersionString, preparedProcess.IsLocal); return new(true, string.Format(TB("Pandoc v{0} is installed, but it does not match the required version (v{1})."), installedVersionString, MINIMUM_REQUIRED_VERSION.ToString()), false, installedVersionString, preparedProcess.IsLocal);
} }
catch (Exception e) catch (Exception e)
{ {
if (showMessages) if (showMessages)
await MessageBus.INSTANCE.SendError(new (@Icons.Material.Filled.AppsOutage, TB("It seems that Pandoc is not installed."))); await MessageBus.INSTANCE.SendError(new(@Icons.Material.Filled.AppsOutage, TB("It seems that Pandoc is not installed.")));
if(shouldLog)
LOG.LogError(e, "Pandoc availability check failed. This usually means Pandoc is not installed or not in the system PATH.");
LOG.LogError(e, "Pandoc availability check failed. This usually means Pandoc is not installed or not in the system PATH.");
return new(false, TB("It seems that Pandoc is not installed."), false, string.Empty, false); return new(false, TB("It seems that Pandoc is not installed."), false, string.Empty, false);
} }
finally
{
HAS_LOGGED_AVAILABILITY_CHECK_ONCE = true;
}
} }
/// <summary> /// <summary>

View File

@ -16,6 +16,9 @@ public sealed class PandocProcessBuilder
private static readonly RID CPU_ARCHITECTURE = META_DATA_ARCH.Architecture.ToRID(); private static readonly RID CPU_ARCHITECTURE = META_DATA_ARCH.Architecture.ToRID();
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(nameof(PandocProcessBuilder)); private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(nameof(PandocProcessBuilder));
// Tracks whether the first log has been written to avoid log spam on repeated calls:
private static bool HAS_LOGGED_ONCE;
private string? providedInputFile; private string? providedInputFile;
private string? providedOutputFile; private string? providedOutputFile;
private string? providedInputFormat; private string? providedInputFormat;
@ -113,67 +116,94 @@ public sealed class PandocProcessBuilder
private static async Task<PandocExecutable> PandocExecutablePath(RustService rustService) private static async Task<PandocExecutable> PandocExecutablePath(RustService rustService)
{ {
// //
// First, we try to find the pandoc executable in the data directory. // Determine if we should log (only on the first call):
// Any local installation should be preferred over the system-wide installation.
// //
var localInstallationRootDirectory = await Pandoc.GetPandocDataFolder(rustService); var shouldLog = !HAS_LOGGED_ONCE;
// try
// Check if the data directory path is valid:
//
if (string.IsNullOrWhiteSpace(localInstallationRootDirectory))
LOGGER.LogWarning("The local data directory path is empty or null. Cannot search for local Pandoc installation.");
else if (!Directory.Exists(localInstallationRootDirectory))
LOGGER.LogWarning("The local Pandoc installation directory does not exist: '{LocalInstallationRootDirectory}'.", localInstallationRootDirectory);
else
{ {
// //
// The directory exists, search for the pandoc executable: // First, we try to find the pandoc executable in the data directory.
// Any local installation should be preferred over the system-wide installation.
// //
var executableName = PandocExecutableName; var localInstallationRootDirectory = await Pandoc.GetPandocDataFolder(rustService);
LOGGER.LogDebug("Searching for Pandoc executable '{ExecutableName}' in: '{LocalInstallationRootDirectory}'.", executableName, localInstallationRootDirectory);
try //
// Check if the data directory path is valid:
//
if (string.IsNullOrWhiteSpace(localInstallationRootDirectory))
{
if (shouldLog)
LOGGER.LogWarning("The local data directory path is empty or null. Cannot search for local Pandoc installation.");
}
else if (!Directory.Exists(localInstallationRootDirectory))
{
if (shouldLog)
LOGGER.LogWarning("The local Pandoc installation directory does not exist: '{LocalInstallationRootDirectory}'.", localInstallationRootDirectory);
}
else
{ {
// //
// First, check the root directory itself: // The directory exists, search for the pandoc executable:
// //
var rootExecutablePath = Path.Combine(localInstallationRootDirectory, executableName); var executableName = PandocExecutableName;
if (File.Exists(rootExecutablePath)) if (shouldLog)
{ LOGGER.LogInformation("Searching for Pandoc executable '{ExecutableName}' in: '{LocalInstallationRootDirectory}'.", executableName, localInstallationRootDirectory);
LOGGER.LogInformation("Found local Pandoc installation at the root path: '{Path}'.", rootExecutablePath);
return new(rootExecutablePath, true);
}
// try
// Then, search all subdirectories:
//
var subdirectories = Directory.GetDirectories(localInstallationRootDirectory, "*", SearchOption.AllDirectories);
foreach (var subdirectory in subdirectories)
{ {
var pandocPath = Path.Combine(subdirectory, executableName); //
if (File.Exists(pandocPath)) // First, check the root directory itself:
//
var rootExecutablePath = Path.Combine(localInstallationRootDirectory, executableName);
if (File.Exists(rootExecutablePath))
{ {
LOGGER.LogInformation("Found local Pandoc installation at: '{Path}'.", pandocPath); if (shouldLog)
return new(pandocPath, true); LOGGER.LogInformation("Found local Pandoc installation at the root path: '{Path}'.", rootExecutablePath);
HAS_LOGGED_ONCE = true;
return new(rootExecutablePath, true);
} }
//
// Then, search all subdirectories:
//
var subdirectories = Directory.GetDirectories(localInstallationRootDirectory, "*", SearchOption.AllDirectories);
foreach (var subdirectory in subdirectories)
{
var pandocPath = Path.Combine(subdirectory, executableName);
if (File.Exists(pandocPath))
{
if (shouldLog)
LOGGER.LogInformation("Found local Pandoc installation at: '{Path}'.", pandocPath);
HAS_LOGGED_ONCE = true;
return new(pandocPath, true);
}
}
if (shouldLog)
LOGGER.LogWarning("No Pandoc executable found in local installation directory or its subdirectories.");
} }
catch (Exception ex)
{
if (shouldLog)
LOGGER.LogWarning(ex, "Error while searching for a local Pandoc installation in: '{LocalInstallationRootDirectory}'.", localInstallationRootDirectory);
}
}
LOGGER.LogWarning("No Pandoc executable found in local installation directory or its subdirectories."); //
} // When no local installation was found, we assume that the pandoc executable is in the system PATH:
catch (Exception ex) //
{ if (shouldLog)
LOGGER.LogWarning(ex, "Error while searching for a local Pandoc installation in: '{LocalInstallationRootDirectory}'.", localInstallationRootDirectory); LOGGER.LogWarning("Falling back to system PATH for the Pandoc executable: '{ExecutableName}'.", PandocExecutableName);
}
return new(PandocExecutableName, false);
}
finally
{
HAS_LOGGED_ONCE = true;
} }
//
// When no local installation was found, we assume that the pandoc executable is in the system PATH:
//
LOGGER.LogWarning("Falling back to system PATH for the Pandoc executable: '{ExecutableName}'.", PandocExecutableName);
return new(PandocExecutableName, false);
} }
/// <summary> /// <summary>