diff --git a/osu.Framework.Desktop/Platform/DesktopGameHost.cs b/osu.Framework.Desktop/Platform/DesktopGameHost.cs index bf8b7da33c..4dbf9538c6 100644 --- a/osu.Framework.Desktop/Platform/DesktopGameHost.cs +++ b/osu.Framework.Desktop/Platform/DesktopGameHost.cs @@ -14,6 +14,7 @@ using osu.Framework.Desktop.Input.Handlers.Mouse; using osu.Framework.Input; using osu.Framework.Input.Handlers; +using osu.Framework.Logging; namespace osu.Framework.Desktop.Platform { @@ -22,6 +23,20 @@ public abstract class DesktopGameHost : GameHost private readonly TcpIpcProvider ipcProvider; private readonly Task ipcTask; + public override Storage Storage + { + get + { + return base.Storage; + } + + protected set + { + base.Storage = value; + Logger.Storage = value.GetStorageForDirectory("logs"); + } + } + protected DesktopGameHost(string gameName = @"", bool bindIPCPort = false) : base(gameName) { diff --git a/osu.Framework.Desktop/Platform/DesktopStorage.cs b/osu.Framework.Desktop/Platform/DesktopStorage.cs index bff98a9240..5fcd4d0992 100644 --- a/osu.Framework.Desktop/Platform/DesktopStorage.cs +++ b/osu.Framework.Desktop/Platform/DesktopStorage.cs @@ -9,7 +9,6 @@ using SQLite.Net.Interop; using SQLite.Net.Platform.Win32; using System.Diagnostics; -using osu.Framework.Logging; namespace osu.Framework.Desktop.Platform { @@ -18,19 +17,33 @@ public class DesktopStorage : Storage public DesktopStorage(string baseName) : base(baseName) { - //todo: this is obviously not the right way to do this. - Logger.LogDirectory = Path.Combine(BasePath, @"logs"); } - protected virtual string BasePath => @"./"; //use current directory by default + protected override string LocateBasePath() => @"./"; //use current directory by default - public override bool Exists(string path) => File.Exists(Path.Combine(BasePath, path)); + public override bool Exists(string path) => File.Exists(GetUsablePathFor(path)); - public override bool ExistsDirectory(string path) => Directory.Exists(Path.Combine(BasePath, path)); + public override bool ExistsDirectory(string path) => Directory.Exists(GetUsablePathFor(path)); - public override void DeleteDirectory(string path) => Directory.Delete(Path.Combine(BasePath, path), true); + public override void DeleteDirectory(string path) + { + path = GetUsablePathFor(path); + + // handles the case where the directory doesn't exist, which will throw a DirectoryNotFoundException. + if (Directory.Exists(path)) + Directory.Delete(path, true); + } + + public override void Delete(string path) + { + path = GetUsablePathFor(path); + + // handles the case where the containing directory doesn't exist, which will throw a DirectoryNotFoundException. + if (File.Exists(path)) + File.Delete(path); + } - public override void Delete(string path) => File.Delete(Path.Combine(BasePath, path)); + public override string[] GetDirectories(string path) => Directory.GetDirectories(GetUsablePathFor(path)); public override void OpenInNativeExplorer() { @@ -39,7 +52,7 @@ public override void OpenInNativeExplorer() public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) { - path = Path.Combine(BasePath, path); + path = GetUsablePathFor(path); if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); @@ -64,7 +77,7 @@ public override SQLiteConnection GetDatabase(string name) platform = new SQLitePlatformWin32(Architecture.NativeIncludePath); else platform = new SQLitePlatformGeneric(); - return new SQLiteConnection(platform, Path.Combine(BasePath, $@"{name}.db")); + return new SQLiteConnection(platform, GetUsablePathFor($@"{name}.db")); } public override void DeleteDatabase(string name) => Delete($@"{name}.db"); diff --git a/osu.Framework.Desktop/Platform/Linux/LinuxStorage.cs b/osu.Framework.Desktop/Platform/Linux/LinuxStorage.cs index a7a992c858..55b90a09a6 100644 --- a/osu.Framework.Desktop/Platform/Linux/LinuxStorage.cs +++ b/osu.Framework.Desktop/Platform/Linux/LinuxStorage.cs @@ -13,24 +13,23 @@ public LinuxStorage(string baseName) { } - protected override string BasePath + protected override string LocateBasePath() { - get + string home = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + string xdg = Environment.GetEnvironmentVariable("XDG_DATA_HOME"); + string[] paths = { - string home = Environment.GetFolderPath(Environment.SpecialFolder.Personal); - string xdg = Environment.GetEnvironmentVariable("XDG_DATA_HOME"); - string[] paths = - { - Path.Combine(xdg ?? Path.Combine(home, ".local", "share"), BaseName), - Path.Combine(home, "." + BaseName) - }; - foreach (string path in paths) - { - if (Directory.Exists(path)) - return path; - } - return paths[0]; + xdg ?? Path.Combine(home, ".local", "share"), + Path.Combine(home) + }; + + foreach (string path in paths) + { + if (Directory.Exists(path)) + return path; } + + return paths[0]; } } } diff --git a/osu.Framework.Desktop/Platform/Windows/WindowsStorage.cs b/osu.Framework.Desktop/Platform/Windows/WindowsStorage.cs index 45999e6d3c..2e8e99603c 100644 --- a/osu.Framework.Desktop/Platform/Windows/WindowsStorage.cs +++ b/osu.Framework.Desktop/Platform/Windows/WindowsStorage.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE using System; -using System.IO; namespace osu.Framework.Desktop.Platform.Windows { @@ -13,13 +12,6 @@ public WindowsStorage(string baseName) { } - protected override string BasePath - { - get - { - string appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - return Path.Combine(appdata, BaseName); - } - } + protected override string LocateBasePath() => Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); } } diff --git a/osu.Framework/Logging/Logger.cs b/osu.Framework/Logging/Logger.cs index bd64b112a9..b9c20f544b 100644 --- a/osu.Framework/Logging/Logger.cs +++ b/osu.Framework/Logging/Logger.cs @@ -6,26 +6,13 @@ using System.Diagnostics; using System.Globalization; using System.IO; -using osu.Framework.IO.File; +using osu.Framework.Platform; using osu.Framework.Threading; namespace osu.Framework.Logging { public class Logger { - /// - /// Directory to place all log files. - /// - public static string LogDirectory - { - get { return logDirectory; } - set - { - logDirectory = value; - hasLogDirectory = null; //we should check again whether the directory exists. - } - } - /// /// Global control over logging. /// @@ -46,6 +33,11 @@ public static string LogDirectory /// public static string VersionIdentifier = @"unknown"; + /// + /// The storage to place logs inside. + /// + public static Storage Storage; + /// /// Add a plain-text phrase which should always be filtered from logs. /// Useful for avoiding logging of credentials. @@ -130,7 +122,7 @@ public static Logger GetLogger(LoggingTarget target = LoggingTarget.Runtime, boo public LoggingTarget Target { get; } - public string Filename => logDirectory == null ? null : Path.Combine(logDirectory, $@"{Target.ToString().ToLower()}.log"); + public string Filename => $@"{Target.ToString().ToLower()}.log"; private Logger(LoggingTarget target = LoggingTarget.Runtime) { @@ -189,13 +181,15 @@ public void Add(string message = @"", LogLevel level = LogLevel.Verbose) background_scheduler.Add(delegate { - ensureLogDirectoryExists(); - if (hasLogDirectory.HasValue && !hasLogDirectory.Value) + if (Storage == null) return; try { - File.AppendAllLines(Filename, lines); + using (var stream = Storage.GetStream(Filename, FileAccess.Write, FileMode.Append)) + using (var writer = new StreamWriter(stream)) + foreach (var line in lines) + writer.WriteLine(line); } catch { @@ -208,20 +202,9 @@ public void Add(string message = @"", LogLevel level = LogLevel.Verbose) /// /// Deletes log file from disk. /// - /// If specified, creates a copy of the last log file with specified suffix. - public void Clear(string lastLogSuffix = null) + public void Clear() { - background_scheduler.Add(delegate - { - ensureLogDirectoryExists(); - if (Filename == null) return; - - if (!string.IsNullOrEmpty(lastLogSuffix)) - FileSafety.FileMove(Filename, Filename.Replace(@".log", $@"_{lastLogSuffix}.log")); - else - FileSafety.FileDelete(Filename); - }); - + background_scheduler.Add(() => Storage?.Delete(Filename)); addHeader(); } @@ -237,30 +220,6 @@ private void addHeader() private static readonly List filters = new List(); private static readonly Dictionary static_loggers = new Dictionary(); private static readonly ThreadedScheduler background_scheduler = new ThreadedScheduler(@"Logger"); - private static bool? hasLogDirectory; - private static string logDirectory; - - private void ensureLogDirectoryExists() - { - if (hasLogDirectory.HasValue) - return; - - if (logDirectory != null) - { - try - { - Directory.CreateDirectory(logDirectory); - hasLogDirectory = true; - } - catch - { - } - - return; - } - - hasLogDirectory = false; - } } public class LogEntry diff --git a/osu.Framework/Platform/Storage.cs b/osu.Framework/Platform/Storage.cs index f5c7431497..a980db4910 100644 --- a/osu.Framework/Platform/Storage.cs +++ b/osu.Framework/Platform/Storage.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +using System; using System.IO; using osu.Framework.IO.File; using SQLite.Net; @@ -11,11 +12,35 @@ public abstract class Storage { protected string BaseName { get; set; } + protected readonly string BasePath; + + /// + /// An optional path to be added after . + /// + protected string SubDirectory { get; set; } = string.Empty; + protected Storage(string baseName) { BaseName = FileSafety.FilenameStrip(baseName); + BasePath = LocateBasePath(); + if (BasePath == null) + throw new NullReferenceException(nameof(BasePath)); } + /// + /// Find the location which will be used as a root for this storage. + /// This should usually be a platform-specific implementation. + /// + /// + protected abstract string LocateBasePath(); + + /// + /// Get a Storage-usable path for the provided path. + /// + /// An incomplete path, usually provided as user input. + /// + protected string GetUsablePathFor(string path) => Path.Combine(BasePath, BaseName, SubDirectory, path); + /// /// Check whether a file exists at the specified path. /// @@ -42,6 +67,25 @@ protected Storage(string baseName) /// The path of the file to delete. public abstract void Delete(string path); + /// + /// Retrieve a list of directories at the specified path. + /// + /// The path to list. + /// A list of directories in the path, relative to the path. + public abstract string[] GetDirectories(string path); + + /// + /// Retrieve a for a contained directory. + /// + /// The subdirectory to use as a root. + /// A more specific storage. + public Storage GetStorageForDirectory(string path) + { + var clone = (Storage)MemberwiseClone(); + clone.SubDirectory = path; + return clone; + } + /// /// Retrieve a stream from an underlying file inside this storage. ///