mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
C# library writes to file + custom location at same time
This commit is contained in:
@@ -11,37 +11,37 @@ namespace Velopack.Locators
|
||||
public interface IVelopackLocator
|
||||
{
|
||||
/// <summary> The unique application Id. This is used in various app paths. </summary>
|
||||
public string? AppId { get; }
|
||||
string? AppId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The root directory of the application. On Windows, this folder contains all
|
||||
/// the application files, but that may not be the case on other operating systems.
|
||||
/// </summary>
|
||||
public string? RootAppDir { get; }
|
||||
string? RootAppDir { get; }
|
||||
|
||||
/// <summary> The directory in which nupkg files are stored for this application. </summary>
|
||||
public string? PackagesDir { get; }
|
||||
string? PackagesDir { get; }
|
||||
|
||||
/// <summary> The directory in which versioned application files are stored. </summary>
|
||||
public string? AppContentDir { get; }
|
||||
string? AppContentDir { get; }
|
||||
|
||||
/// <summary> The temporary directory for this application. </summary>
|
||||
public string? AppTempDir { get; }
|
||||
string? AppTempDir { get; }
|
||||
|
||||
/// <summary> The path to the current Update.exe or similar on other operating systems. </summary>
|
||||
public string? UpdateExePath { get; }
|
||||
string? UpdateExePath { get; }
|
||||
|
||||
/// <summary> The currently installed version of the application, or null if the app is not installed. </summary>
|
||||
public SemanticVersion? CurrentlyInstalledVersion { get; }
|
||||
SemanticVersion? CurrentlyInstalledVersion { get; }
|
||||
|
||||
/// <summary> The path from <see cref="AppContentDir"/> to this executable. </summary>
|
||||
public string? ThisExeRelativePath { get; }
|
||||
string? ThisExeRelativePath { get; }
|
||||
|
||||
/// <summary> The release channel this package was built for. </summary>
|
||||
public string? Channel { get; }
|
||||
string? Channel { get; }
|
||||
|
||||
/// <summary> The logging interface to use for Velopack diagnostic messages. </summary>
|
||||
public IVelopackLogger Log { get; }
|
||||
IVelopackLogger Log { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A flag indicating if this is a portable build, and that the settings should be self-contained in the package.
|
||||
@@ -49,34 +49,34 @@ namespace Velopack.Locators
|
||||
/// On OSX and Linux, this is always false, because settings and application files should be stored in the user's
|
||||
/// home directory.
|
||||
/// </summary>
|
||||
public bool IsPortable { get; }
|
||||
bool IsPortable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The process for which the Velopack Locator has been constructed. This should usually be the current process path.
|
||||
/// </summary>
|
||||
public string ProcessExePath { get; }
|
||||
string ProcessExePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The process ID for which the Velopack Locator has been constructed. This should usually be the current process ID.
|
||||
/// Setting this to zero will disable some features of Velopack (like the ability to wait for the process to exit
|
||||
/// before installing updates).
|
||||
/// </summary>
|
||||
public uint ProcessId { get; }
|
||||
uint ProcessId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Finds .nupkg files in the PackagesDir and returns a list of ReleaseEntryName objects.
|
||||
/// </summary>
|
||||
public List<VelopackAsset> GetLocalPackages();
|
||||
List<VelopackAsset> GetLocalPackages();
|
||||
|
||||
/// <summary>
|
||||
/// Finds latest .nupkg file in the PackagesDir or null if not found.
|
||||
/// </summary>
|
||||
public VelopackAsset? GetLatestLocalFullPackage();
|
||||
VelopackAsset? GetLatestLocalFullPackage();
|
||||
|
||||
/// <summary>
|
||||
/// Unique identifier for this user which is used to calculate whether this user is eligible for
|
||||
/// staged roll outs.
|
||||
/// </summary>
|
||||
public Guid? GetOrCreateStagedUserId();
|
||||
Guid? GetOrCreateStagedUserId();
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace Velopack.Locators
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? Channel { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IVelopackLogger Log { get; }
|
||||
|
||||
@@ -50,10 +50,10 @@ namespace Velopack.Locators
|
||||
|
||||
/// <summary> File path of the .AppImage which mounted and ran this application. </summary>
|
||||
public string? AppImagePath => Environment.GetEnvironmentVariable("APPIMAGE");
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override uint ProcessId { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ProcessExePath { get; }
|
||||
|
||||
@@ -61,46 +61,63 @@ namespace Velopack.Locators
|
||||
/// Creates a new <see cref="OsxVelopackLocator"/> and auto-detects the
|
||||
/// app information from metadata embedded in the .app.
|
||||
/// </summary>
|
||||
public LinuxVelopackLocator(string currentProcessPath, uint currentProcessId, IVelopackLogger? logger)
|
||||
public LinuxVelopackLocator(string currentProcessPath, uint currentProcessId, IVelopackLogger? customLog)
|
||||
{
|
||||
if (!VelopackRuntimeInfo.IsLinux)
|
||||
throw new NotSupportedException("Cannot instantiate LinuxVelopackLocator on a non-linux system.");
|
||||
|
||||
throw new NotSupportedException($"Cannot instantiate {nameof(LinuxVelopackLocator)} on a non-linux system.");
|
||||
|
||||
ProcessId = currentProcessId;
|
||||
var ourPath = ProcessExePath = currentProcessPath;
|
||||
|
||||
logger ??= new FileVelopackLogger("/tmp/velopack.log", currentProcessId);
|
||||
logger.Info($"Initialising {nameof(LinuxVelopackLocator)}");
|
||||
Log = logger;
|
||||
var combinedLog = new CombinedVelopackLogger();
|
||||
combinedLog.Add(customLog);
|
||||
Log = combinedLog;
|
||||
|
||||
using var initLog = new CachedVelopackLogger(combinedLog);
|
||||
initLog.Info($"Initialising {nameof(LinuxVelopackLocator)}");
|
||||
var logFilePath = Path.Combine(Path.GetTempPath(), DefaultLoggingFileName);
|
||||
|
||||
// are we inside a mounted .AppImage?
|
||||
var ix = ourPath.IndexOf("/usr/bin/", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (ix <= 0) {
|
||||
logger.Warn(
|
||||
$"Unable to locate .AppImage root from '{ourPath}'. " +
|
||||
$"This warning indicates that the application is not running from a mounted .AppImage, for example during development.");
|
||||
return;
|
||||
}
|
||||
if (ix > 0) {
|
||||
var rootDir = ourPath.Substring(0, ix);
|
||||
var contentsDir = Path.Combine(rootDir, "usr", "bin");
|
||||
var updateExe = Path.Combine(contentsDir, "UpdateNix");
|
||||
var metadataPath = Path.Combine(contentsDir, CoreUtil.SpecVersionFileName);
|
||||
|
||||
var rootDir = ourPath.Substring(0, ix);
|
||||
var contentsDir = Path.Combine(rootDir, "usr", "bin");
|
||||
var updateExe = Path.Combine(contentsDir, "UpdateNix");
|
||||
var metadataPath = Path.Combine(contentsDir, CoreUtil.SpecVersionFileName);
|
||||
|
||||
if (!String.IsNullOrEmpty(AppImagePath) && File.Exists(AppImagePath)) {
|
||||
if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) {
|
||||
logger.Info("Located valid manifest file at: " + metadataPath);
|
||||
AppId = manifest.Id;
|
||||
RootAppDir = rootDir;
|
||||
AppContentDir = contentsDir;
|
||||
UpdateExePath = updateExe;
|
||||
CurrentlyInstalledVersion = manifest.Version;
|
||||
Channel = manifest.Channel;
|
||||
if (!String.IsNullOrEmpty(AppImagePath) && File.Exists(AppImagePath)) {
|
||||
if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) {
|
||||
initLog.Info("Located valid manifest file at: " + metadataPath);
|
||||
AppId = manifest.Id;
|
||||
RootAppDir = rootDir;
|
||||
AppContentDir = contentsDir;
|
||||
UpdateExePath = updateExe;
|
||||
CurrentlyInstalledVersion = manifest.Version;
|
||||
Channel = manifest.Channel;
|
||||
logFilePath = Path.Combine(Path.GetTempPath(), $"velopack_{manifest.Id}.log");
|
||||
} else {
|
||||
initLog.Error("Unable to locate UpdateNix in " + contentsDir);
|
||||
}
|
||||
} else {
|
||||
logger.Error("Unable to locate UpdateNix in " + contentsDir);
|
||||
initLog.Error("Unable to locate .AppImage ($APPIMAGE)");
|
||||
}
|
||||
} else {
|
||||
logger.Error("Unable to locate .AppImage ($APPIMAGE)");
|
||||
initLog.Warn(
|
||||
$"Unable to locate .AppImage root from '{ourPath}'. " +
|
||||
$"This warning indicates that the application is not running from a mounted .AppImage, for example during development.");
|
||||
}
|
||||
|
||||
try {
|
||||
var fileLog = new FileVelopackLogger(logFilePath, currentProcessId);
|
||||
combinedLog.Add(fileLog);
|
||||
} catch (Exception ex) {
|
||||
initLog.Error("Unable to create file logger: " + ex);
|
||||
}
|
||||
|
||||
if (AppId == null) {
|
||||
initLog.Warn($"Failed to initialise {nameof(LinuxVelopackLocator)}. This could be because the program is not in an .AppImage.");
|
||||
} else {
|
||||
initLog.Info($"Initialised {nameof(LinuxVelopackLocator)} for {AppId} v{CurrentlyInstalledVersion}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ namespace Velopack.Locators
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? Channel { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override uint ProcessId { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ProcessExePath { get; }
|
||||
|
||||
@@ -58,40 +58,66 @@ namespace Velopack.Locators
|
||||
/// Creates a new <see cref="OsxVelopackLocator"/> and auto-detects the
|
||||
/// app information from metadata embedded in the .app.
|
||||
/// </summary>
|
||||
public OsxVelopackLocator(string currentProcessPath, uint currentProcessId, IVelopackLogger? logger)
|
||||
public OsxVelopackLocator(string currentProcessPath, uint currentProcessId, IVelopackLogger? customLog)
|
||||
{
|
||||
if (!VelopackRuntimeInfo.IsOSX)
|
||||
throw new NotSupportedException("Cannot instantiate OsxLocator on a non-osx system.");
|
||||
|
||||
throw new NotSupportedException($"Cannot instantiate {nameof(OsxVelopackLocator)} on a non-osx system.");
|
||||
|
||||
ProcessId = currentProcessId;
|
||||
var ourPath = ProcessExePath = currentProcessPath;
|
||||
|
||||
var userLogDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Logs");
|
||||
var logPath = Directory.Exists(userLogDir) ? Path.Combine(userLogDir, "velopack.log") : "/tmp/velopack.log";
|
||||
logger ??= new FileVelopackLogger(logPath, currentProcessId);
|
||||
logger.Info($"Initialising {nameof(OsxVelopackLocator)}");
|
||||
Log = logger;
|
||||
var combinedLog = new CombinedVelopackLogger();
|
||||
combinedLog.Add(customLog);
|
||||
Log = combinedLog;
|
||||
|
||||
using var initLog = new CachedVelopackLogger(combinedLog);
|
||||
initLog.Info($"Initialising {nameof(OsxVelopackLocator)}");
|
||||
|
||||
string logFolder = Path.GetTempPath();
|
||||
|
||||
if (!string.IsNullOrEmpty(HomeDir) && Directory.Exists(HomeDir)) {
|
||||
var userLogsFolder = Path.Combine(HomeDir!, "Library", "Logs");
|
||||
if (!Directory.Exists(userLogsFolder)) {
|
||||
logFolder = userLogsFolder;
|
||||
}
|
||||
}
|
||||
|
||||
var logFileName = DefaultLoggingFileName;
|
||||
|
||||
// are we inside a .app?
|
||||
var ix = ourPath.IndexOf(".app/", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (ix <= 0) {
|
||||
logger.Warn($"Unable to locate .app root from '{ourPath}'");
|
||||
return;
|
||||
if (ix > 0) {
|
||||
var appPath = ourPath.Substring(0, ix + 4);
|
||||
var contentsDir = Path.Combine(appPath, "Contents");
|
||||
var macosDir = Path.Combine(contentsDir, "MacOS");
|
||||
var updateExe = Path.Combine(macosDir, "UpdateMac");
|
||||
var metadataPath = Path.Combine(macosDir, CoreUtil.SpecVersionFileName);
|
||||
|
||||
if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) {
|
||||
initLog.Info("Located valid manifest file at: " + metadataPath);
|
||||
AppId = manifest.Id;
|
||||
RootAppDir = appPath;
|
||||
UpdateExePath = updateExe;
|
||||
CurrentlyInstalledVersion = manifest.Version;
|
||||
Channel = manifest.Channel;
|
||||
logFileName = $"velopack_{manifest.Id}.log";
|
||||
}
|
||||
} else {
|
||||
initLog.Warn($"Unable to locate .app root from '{ourPath}'");
|
||||
}
|
||||
|
||||
var appPath = ourPath.Substring(0, ix + 4);
|
||||
var contentsDir = Path.Combine(appPath, "Contents");
|
||||
var macosDir = Path.Combine(contentsDir, "MacOS");
|
||||
var updateExe = Path.Combine(macosDir, "UpdateMac");
|
||||
var metadataPath = Path.Combine(macosDir, CoreUtil.SpecVersionFileName);
|
||||
try {
|
||||
var logFilePath = Path.Combine(logFolder, logFileName);
|
||||
var fileLog = new FileVelopackLogger(logFilePath, currentProcessId);
|
||||
combinedLog.Add(fileLog);
|
||||
} catch (Exception ex) {
|
||||
initLog.Error("Unable to create file logger: " + ex);
|
||||
}
|
||||
|
||||
if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) {
|
||||
logger.Info("Located valid manifest file at: " + metadataPath);
|
||||
AppId = manifest.Id;
|
||||
RootAppDir = appPath;
|
||||
UpdateExePath = updateExe;
|
||||
CurrentlyInstalledVersion = manifest.Version;
|
||||
Channel = manifest.Channel;
|
||||
if (AppId == null) {
|
||||
initLog.Warn($"Failed to initialise {nameof(OsxVelopackLocator)}. This could be because the program is not in a .app bundle.");
|
||||
} else {
|
||||
initLog.Info($"Initialised {nameof(OsxVelopackLocator)} for {AppId} v{CurrentlyInstalledVersion}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,11 @@ namespace Velopack.Locators
|
||||
public abstract class VelopackLocator : IVelopackLocator
|
||||
{
|
||||
private static IVelopackLocator? _current;
|
||||
|
||||
/// <summary>
|
||||
/// The default log file name for Velopack.
|
||||
/// </summary>
|
||||
protected const string DefaultLoggingFileName = "velopack.log";
|
||||
|
||||
/// <summary>
|
||||
/// Check if a VelopackLocator has been set for the current process.
|
||||
|
||||
@@ -49,14 +49,21 @@ namespace Velopack.Locators
|
||||
public override string ProcessExePath { get; }
|
||||
|
||||
/// <inheritdoc cref="WindowsVelopackLocator" />
|
||||
public WindowsVelopackLocator(string currentProcessPath, uint currentProcessId, IVelopackLogger? logger)
|
||||
public WindowsVelopackLocator(string currentProcessPath, uint currentProcessId, IVelopackLogger? customLog)
|
||||
{
|
||||
if (!VelopackRuntimeInfo.IsWindows)
|
||||
throw new NotSupportedException("Cannot instantiate WindowsLocator on a non-Windows system.");
|
||||
throw new NotSupportedException($"Cannot instantiate {nameof(WindowsVelopackLocator)} on a non-Windows system.");
|
||||
|
||||
ProcessId = currentProcessId;
|
||||
var ourPath = ProcessExePath = currentProcessPath;
|
||||
|
||||
var combinedLog = new CombinedVelopackLogger();
|
||||
combinedLog.Add(customLog);
|
||||
Log = combinedLog;
|
||||
|
||||
using var initLog = new CachedVelopackLogger(combinedLog);
|
||||
initLog.Info($"Initialising {nameof(WindowsVelopackLocator)}");
|
||||
|
||||
// We try various approaches here. Firstly, if Update.exe is in the parent directory,
|
||||
// we use that. If it's not present, we search for a parent "current" or "app-{ver}" directory,
|
||||
// which could designate that this executable is running in a nested sub-directory.
|
||||
@@ -73,11 +80,9 @@ namespace Velopack.Locators
|
||||
// we're running in a directory with an Update.exe in the parent directory
|
||||
var manifestFile = Path.Combine(myDirPath, CoreUtil.SpecVersionFileName);
|
||||
var rootDir = Path.GetDirectoryName(possibleUpdateExe)!;
|
||||
logger ??= new FileVelopackLogger(Path.Combine(rootDir, "velopack.log"), currentProcessId);
|
||||
Log = logger;
|
||||
if (PackageManifest.TryParseFromFile(manifestFile, out var manifest)) {
|
||||
// ideal, the info we need is in a manifest file.
|
||||
logger.Info($"{nameof(WindowsVelopackLocator)}: Update.exe in parent dir, Located valid manifest file at: " + manifestFile);
|
||||
initLog.Info($"{nameof(WindowsVelopackLocator)}: Update.exe in parent dir, Located valid manifest file at: " + manifestFile);
|
||||
AppId = manifest.Id;
|
||||
CurrentlyInstalledVersion = manifest.Version;
|
||||
RootAppDir = rootDir;
|
||||
@@ -86,14 +91,15 @@ namespace Velopack.Locators
|
||||
Channel = manifest.Channel;
|
||||
} else if (PathUtil.PathPartStartsWith(myDirName, "app-") && NuGetVersion.TryParse(myDirName.Substring(4), out var version)) {
|
||||
// this is a legacy case, where we're running in an 'root/app-*/' directory, and there is no manifest.
|
||||
logger.Warn("Update.exe in parent dir, Legacy app-* directory detected, sq.version not found. Using directory name for AppId and Version.");
|
||||
initLog.Warn(
|
||||
"Update.exe in parent dir, Legacy app-* directory detected, sq.version not found. Using directory name for AppId and Version.");
|
||||
AppId = Path.GetFileName(Path.GetDirectoryName(possibleUpdateExe));
|
||||
CurrentlyInstalledVersion = version;
|
||||
RootAppDir = rootDir;
|
||||
UpdateExePath = possibleUpdateExe;
|
||||
AppContentDir = myDirPath;
|
||||
} else {
|
||||
logger.Error("Update.exe in parent dir, but unable to locate a valid manifest file at: " + manifestFile);
|
||||
initLog.Error("Update.exe in parent dir, but unable to locate a valid manifest file at: " + manifestFile);
|
||||
}
|
||||
} else if (ixCurrent > 0) {
|
||||
// this is an attempt to handle the case where we are running in a nested current directory.
|
||||
@@ -103,10 +109,8 @@ namespace Velopack.Locators
|
||||
possibleUpdateExe = Path.GetFullPath(Path.Combine(rootDir, "Update.exe"));
|
||||
// we only support parsing a manifest when we're in a nested current directory. no legacy fallback.
|
||||
if (File.Exists(possibleUpdateExe) && PackageManifest.TryParseFromFile(manifestFile, out var manifest)) {
|
||||
logger ??= new FileVelopackLogger(Path.Combine(rootDir, "velopack.log"), currentProcessId);
|
||||
Log = logger;
|
||||
logger.Warn("Running in deeply nested directory. This is not an advised use-case.");
|
||||
logger.Info("Located valid manifest file at: " + manifestFile);
|
||||
initLog.Warn("Running in deeply nested directory. This is not an advised use-case.");
|
||||
initLog.Info("Located valid manifest file at: " + manifestFile);
|
||||
RootAppDir = Path.GetDirectoryName(possibleUpdateExe);
|
||||
UpdateExePath = possibleUpdateExe;
|
||||
AppId = manifest.Id;
|
||||
@@ -116,14 +120,36 @@ namespace Velopack.Locators
|
||||
}
|
||||
}
|
||||
|
||||
if (Log == null) {
|
||||
bool fileLogCreated = false;
|
||||
if (!String.IsNullOrEmpty(AppId) && !String.IsNullOrEmpty(RootAppDir)) {
|
||||
try {
|
||||
Log = new FileVelopackLogger(Path.Combine(AppContext.BaseDirectory, "velopack.log"), currentProcessId);
|
||||
} catch (Exception ex) {
|
||||
Debug.WriteLine("Error creating Velopack logger: " + ex);
|
||||
Log = new NullVelopackLogger();
|
||||
var logFilePath = Path.Combine(RootAppDir, DefaultLoggingFileName);
|
||||
var fileLog = new FileVelopackLogger(logFilePath, currentProcessId);
|
||||
combinedLog.Add(fileLog);
|
||||
fileLogCreated = true;
|
||||
} catch (Exception ex2) {
|
||||
initLog.Error("Unable to create default file logger: " + ex2);
|
||||
}
|
||||
}
|
||||
|
||||
// if the RootAppDir was unwritable, or we don't know the app id, we could try to write to the temp folder instead.
|
||||
if (!fileLogCreated) {
|
||||
try {
|
||||
var logFileName = String.IsNullOrEmpty(AppId) ? DefaultLoggingFileName : $"velopack_{AppId}.log";
|
||||
var logFilePath = Path.Combine(Path.GetTempPath(), logFileName);
|
||||
var fileLog = new FileVelopackLogger(logFilePath, currentProcessId);
|
||||
combinedLog.Add(fileLog);
|
||||
} catch (Exception ex2) {
|
||||
initLog.Error("Unable to create temp folder file logger: " + ex2);
|
||||
}
|
||||
}
|
||||
|
||||
if (AppId == null) {
|
||||
initLog.Warn(
|
||||
$"Failed to initialise {nameof(WindowsVelopackLocator)}. This could be because the program is not installed or packaged properly.");
|
||||
} else {
|
||||
initLog.Info($"Initialised {nameof(WindowsVelopackLocator)} for {AppId} v{CurrentlyInstalledVersion}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/lib-csharp/Logging/CachedVelopackLogger.cs
Normal file
50
src/lib-csharp/Logging/CachedVelopackLogger.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Velopack.Logging
|
||||
{
|
||||
internal class CachedVelopackLogger : IVelopackLogger, IDisposable
|
||||
{
|
||||
private readonly List<(VelopackLogLevel logLevel, string? message, Exception? exception)> _cache = new();
|
||||
private readonly IVelopackLogger _logger;
|
||||
private readonly object _lock = new();
|
||||
private bool _committed;
|
||||
|
||||
public CachedVelopackLogger(IVelopackLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Log(VelopackLogLevel logLevel, string? message, Exception? exception)
|
||||
{
|
||||
lock (_lock) {
|
||||
if (_committed) {
|
||||
_logger.Log(logLevel, message, exception);
|
||||
} else {
|
||||
_cache.Add((logLevel, message, exception));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Commit()
|
||||
{
|
||||
lock (_lock) {
|
||||
if (_committed) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (logLevel, message, exception) in _cache) {
|
||||
_logger.Log(logLevel, message, exception);
|
||||
}
|
||||
|
||||
_cache.Clear();
|
||||
_committed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/lib-csharp/Logging/CombinedVelopackLogger.cs
Normal file
44
src/lib-csharp/Logging/CombinedVelopackLogger.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Velopack.Logging
|
||||
{
|
||||
internal class CombinedVelopackLogger : IVelopackLogger, IDisposable
|
||||
{
|
||||
private readonly List<IVelopackLogger> _loggers = new();
|
||||
|
||||
public void Log(VelopackLogLevel logLevel, string? message, Exception? exception)
|
||||
{
|
||||
foreach (var logger in _loggers) {
|
||||
try {
|
||||
logger.Log(logLevel, message, exception);
|
||||
} catch (Exception ex) {
|
||||
Debug.WriteLine($"Error logging to {logger.GetType().Name} ({ex}) {Environment.NewLine} [{logLevel}] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(IVelopackLogger? logger)
|
||||
{
|
||||
if (logger != null) {
|
||||
_loggers.Add(logger);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var localLoggers = _loggers.ToArray();
|
||||
_loggers.Clear();
|
||||
|
||||
foreach (var logger in localLoggers) {
|
||||
try {
|
||||
if (logger is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
} catch (Exception ex) {
|
||||
Debug.WriteLine($"Error disposing {logger.GetType().Name} ({ex})");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Velopack.Logging
|
||||
{
|
||||
public class FileVelopackLogger : IVelopackLogger, IDisposable
|
||||
{
|
||||
public uint ProcessId { get; }
|
||||
private uint ProcessId { get; }
|
||||
private readonly object _lock = new();
|
||||
private readonly StreamWriter _writer;
|
||||
private readonly FileStream _fileStream;
|
||||
|
||||
Reference in New Issue
Block a user