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.
///