Added paths to log files in the "About" dialog for easier support (#304)

This commit is contained in:
Thorsten Sommer 2025-02-27 20:17:51 +01:00 committed by GitHub
parent 277dc73468
commit 9d2b63bbaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 123 additions and 11 deletions

View File

@ -26,6 +26,38 @@
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.EventNote" HeaderText="Changelog">
<Changelog/>
</ExpansionPanel>
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Book" HeaderText="Logbook">
<MudText Typo="Typo.h4">
Explanation
</MudText>
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
AI Studio creates a log file at startup, in which events during startup are recorded. After startup,
another log file is created that records all events that occur during the use of the app. This
includes any errors that may occur. Depending on when an error occurs (at startup or during use),
the contents of these log files can be helpful for troubleshooting. Sensitive information such as
passwords is not included in the log files.
</MudJustifiedText>
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
By clicking on the respective path, the path is copied to the clipboard. You might open these files
with a text editor to view their contents.
</MudJustifiedText>
<MudText Typo="Typo.h4">
Startup log file
</MudText>
<MudList T="string" Class="mb-3">
<MudListItem T="string" Icon="@Icons.Material.Outlined.Folder" Text="@this.logPaths.LogStartupPath" OnClick="() => this.CopyStartupLogPath()"/>
</MudList>
<MudText Typo="Typo.h4">
Usage log file
</MudText>
<MudList T="string" Class="mb-3">
<MudListItem T="string" Icon="@Icons.Material.Outlined.Folder" Text="@this.logPaths.LogAppPath" OnClick="() => this.CopyAppLogPath()"/>
</MudList>
</ExpansionPanel>
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Engineering" HeaderText="Motivation">
<Motivation/>

View File

@ -1,5 +1,8 @@
using System.Reflection;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Pages;
@ -9,9 +12,15 @@ public partial class About : ComponentBase
[Inject]
private MessageBus MessageBus { get; init; } = null!;
[Inject]
private RustService RustService { get; init; } = null!;
[Inject]
private ISnackbar Snackbar { get; init; } = null!;
private static readonly Assembly ASSEMBLY = Assembly.GetExecutingAssembly();
private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute<MetaDataAttribute>()!;
private static string VersionDotnetRuntime => $"Used .NET runtime: v{META_DATA.DotnetVersion}";
private static string VersionDotnetSdk => $"Used .NET SDK: v{META_DATA.DotnetSdkVersion}";
@ -26,6 +35,28 @@ public partial class About : ComponentBase
private static string TauriVersion => $"Tauri: v{META_DATA.TauriVersion}";
private GetLogPathsResponse logPaths;
#region Overrides of ComponentBase
private async Task CopyStartupLogPath()
{
await this.RustService.CopyText2Clipboard(this.Snackbar, this.logPaths.LogStartupPath);
}
private async Task CopyAppLogPath()
{
await this.RustService.CopyText2Clipboard(this.Snackbar, this.logPaths.LogAppPath);
}
protected override async Task OnInitializedAsync()
{
this.logPaths = await this.RustService.GetLogPaths();
await base.OnInitializedAsync();
}
#endregion
private const string LICENSE = """
# Functional Source License, Version 1.1, MIT Future License

View File

@ -0,0 +1,3 @@
namespace AIStudio.Tools.Rust;
public readonly record struct GetLogPathsResponse(string LogStartupPath, string LogAppPath);

View File

@ -0,0 +1,15 @@
using AIStudio.Tools.Rust;
namespace AIStudio.Tools.Services;
public sealed partial class RustService
{
/// <summary>
/// Get the paths of the log files.
/// </summary>
/// <returns>The paths of the log files.</returns>
public async Task<GetLogPathsResponse> GetLogPaths()
{
return await this.http.GetFromJsonAsync<GetLogPathsResponse>("/log/paths", this.jsonRustSerializerOptions);
}
}

View File

@ -2,3 +2,4 @@
- Added Helmholtz (aka "Blablador") as provider. This provider is available to all researchers and employees of the 18 Helmholtz Centers as well as all eduGAIN organizations worldwide.
- Added GWDG SAIA as provider. This provider is available to all researchers and employees of the GWDG, the Max Planck Society, the 18 Helmholtz Centers, and most German universities.
- Added DeepSeek as provider.
- Added paths to log files in the "About" dialog for easier troubleshooting and support.

View File

@ -7,10 +7,18 @@ use flexi_logger::{DeferredNow, Duplicate, FileSpec, Logger, LoggerHandle};
use flexi_logger::writers::FileLogWriter;
use log::kv;
use log::kv::{Key, Value, VisitSource};
use rocket::get;
use rocket::serde::json::Json;
use rocket::serde::Serialize;
use crate::api_token::APIToken;
use crate::environment::is_dev;
static LOGGER: OnceLock<RuntimeLoggerHandle> = OnceLock::new();
static LOG_STARTUP_PATH: OnceLock<String> = OnceLock::new();
static LOG_APP_PATH: OnceLock<String> = OnceLock::new();
/// Initialize the logging system.
pub fn init_logging() {
@ -42,11 +50,16 @@ pub fn init_logging() {
false => "AI Studio Events",
};
let log_path = FileSpec::default()
.basename(log_basename)
.suppress_timestamp()
.suffix("log");
// Store the startup log path:
LOG_STARTUP_PATH.set(log_path.as_pathbuf(None).canonicalize().unwrap().to_str().unwrap().to_string()).expect("Cannot store the startup log path");
let runtime_logger = Logger::try_with_str(log_config).expect("Cannot create logging")
.log_to_file(FileSpec::default()
.basename(log_basename)
.suppress_timestamp()
.suffix("log"))
.log_to_file(log_path)
.duplicate_to_stdout(Duplicate::All)
.use_utc()
.format_for_files(file_logger_format)
@ -63,12 +76,13 @@ pub fn init_logging() {
/// Switch the logging system to a file-based output.
pub fn switch_to_file_logging(logger_path: PathBuf) -> Result<(), Box<dyn Error>>{
LOGGER.get().expect("No LOGGER was set").handle.reset_flw(&FileLogWriter::builder(
FileSpec::default()
.directory(logger_path)
.basename("events")
.suppress_timestamp()
.suffix("log")))?;
let log_path = FileSpec::default()
.directory(logger_path)
.basename("events")
.suppress_timestamp()
.suffix("log");
LOG_APP_PATH.set(log_path.as_pathbuf(None).to_str().unwrap().to_string()).expect("Cannot store the app log path");
LOGGER.get().expect("No LOGGER was set").handle.reset_flw(&FileLogWriter::builder(log_path))?;
Ok(())
}
@ -160,4 +174,19 @@ fn file_logger_format(
// Write the log message:
write!(w, "{}", &record.args())
}
#[get("/log/paths")]
pub async fn get_log_paths(_token: APIToken) -> Json<LogPathsResponse> {
Json(LogPathsResponse {
log_startup_path: LOG_STARTUP_PATH.get().expect("No startup log path was set").clone(),
log_app_path: LOG_APP_PATH.get().expect("No app log path was set").clone(),
})
}
/// The response the get log paths request.
#[derive(Serialize)]
pub struct LogPathsResponse {
log_startup_path: String,
log_app_path: String,
}

View File

@ -77,6 +77,7 @@ pub fn start_runtime_api() {
crate::secret::delete_secret,
crate::environment::get_data_directory,
crate::environment::get_config_directory,
crate::log::get_log_paths,
])
.ignite().await.unwrap()
.launch().await.unwrap();