diff --git a/backend/scss/main.scss b/backend/scss/main.scss
index 062d3ff..2c65450 100644
--- a/backend/scss/main.scss
+++ b/backend/scss/main.scss
@@ -187,6 +187,12 @@ body {
}
}
+ #dev-panel {
+ .log-line-odd {
+ background-color: consts.$color-1;
+ }
+ }
+
form {
display: grid;
grid-template-columns: auto 1fr;
diff --git a/backend/src/html_templates.rs b/backend/src/html_templates.rs
index 922d776..d5fd3fd 100644
--- a/backend/src/html_templates.rs
+++ b/backend/src/html_templates.rs
@@ -3,6 +3,7 @@ use askama::Template;
use crate::{
Context,
data::{db, model},
+ log::Log,
translation::{self, Sentence, Tr},
};
@@ -58,6 +59,8 @@ pub struct HomeTemplate {
pub struct DevPanelTemplate {
pub context: Context,
pub recipes: Recipes,
+ pub log: Log,
+ pub current_log_file: String,
}
#[derive(Template)]
diff --git a/backend/src/log.rs b/backend/src/log.rs
index 45ab1b9..8842aa7 100644
--- a/backend/src/log.rs
+++ b/backend/src/log.rs
@@ -1,5 +1,12 @@
-use std::{fs, path::Path};
+use std::{
+ fs::{self, File},
+ io::{BufRead, BufReader},
+ path::{Path, PathBuf},
+ sync::Arc,
+};
+use itertools::Itertools;
+use tracing::{Level, event};
use tracing_appender::{
non_blocking::WorkerGuard,
rolling::{RollingFileAppender, Rotation},
@@ -22,38 +29,82 @@ const TRACING_LEVEL: tracing::Level = tracing::Level::INFO;
#[cfg(not(debug_assertions))]
const TRACING_DISPLAY_THREAD: bool = false;
-pub fn init
(directory: P) -> WorkerGuard
-where
- P: AsRef,
-{
- if !directory.as_ref().exists() {
- fs::DirBuilder::new().create(&directory).unwrap();
+#[derive(Clone)]
+pub struct Log {
+ guard: Arc,
+ directory: PathBuf,
+}
+
+// TODO: Remove all 'unwrap'.
+impl Log {
+ pub fn new(directory: P) -> Self
+ where
+ P: AsRef,
+ {
+ if !directory.as_ref().exists() {
+ fs::DirBuilder::new().create(&directory).unwrap();
+ }
+
+ let file_appender = RollingFileAppender::builder()
+ .rotation(Rotation::DAILY)
+ .filename_prefix(consts::BASE_LOG_FILENAME)
+ .filename_suffix("log")
+ .build(&directory)
+ .expect("Initializing rolling file appender failed");
+
+ let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
+
+ let layer_file = tracing_subscriber::fmt::layer()
+ .with_writer(non_blocking.with_max_level(TRACING_LEVEL))
+ .with_ansi(false)
+ .with_thread_ids(TRACING_DISPLAY_THREAD)
+ .with_thread_names(TRACING_DISPLAY_THREAD);
+
+ let layer_stdout = tracing_subscriber::fmt::layer()
+ .with_writer(std::io::stdout.with_max_level(TRACING_LEVEL))
+ .with_thread_ids(TRACING_DISPLAY_THREAD)
+ .with_thread_names(TRACING_DISPLAY_THREAD);
+
+ tracing_subscriber::Registry::default()
+ .with(layer_file)
+ .with(layer_stdout)
+ .init();
+
+ Log {
+ guard: Arc::new(guard),
+ directory: directory.as_ref().to_path_buf(),
+ }
}
- let file_appender = RollingFileAppender::builder()
- .rotation(Rotation::DAILY)
- .filename_prefix(consts::BASE_LOG_FILENAME)
- .filename_suffix("log")
- .build(directory)
- .expect("Initializing rolling file appender failed");
+ pub fn file_names(&self) -> std::io::Result> {
+ Ok(self
+ .directory
+ .read_dir()?
+ .map(|entry| {
+ entry
+ .unwrap()
+ .path()
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string()
+ })
+ .sorted()
+ .collect())
+ }
- let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
-
- let layer_file = tracing_subscriber::fmt::layer()
- .with_writer(non_blocking.with_max_level(TRACING_LEVEL))
- .with_ansi(false)
- .with_thread_ids(TRACING_DISPLAY_THREAD)
- .with_thread_names(TRACING_DISPLAY_THREAD);
-
- let layer_stdout = tracing_subscriber::fmt::layer()
- .with_writer(std::io::stdout.with_max_level(TRACING_LEVEL))
- .with_thread_ids(TRACING_DISPLAY_THREAD)
- .with_thread_names(TRACING_DISPLAY_THREAD);
-
- tracing_subscriber::Registry::default()
- .with(layer_file)
- .with(layer_stdout)
- .init();
-
- guard
+ pub fn read_content(&self, filename: &str) -> std::io::Result> {
+ let filepath = self.directory.join(filename);
+ if filepath.is_file() {
+ let file = File::open(filepath)?;
+ // tracing::event!(Level::ERROR, "file: {:?}", file);
+ Ok(BufReader::new(file)
+ .lines()
+ .map(|l| l.unwrap_or_default())
+ .collect())
+ } else {
+ Ok(vec![])
+ }
+ }
}
diff --git a/backend/src/main.rs b/backend/src/main.rs
index d668b64..6cf9d2e 100644
--- a/backend/src/main.rs
+++ b/backend/src/main.rs
@@ -26,6 +26,7 @@ use tower_http::{
use tracing::{Level, event};
use data::{backup, db, model};
+use log::Log;
use translation::Tr;
mod config;
@@ -45,6 +46,7 @@ mod utils;
struct AppState {
config: Config,
db_connection: db::Connection,
+ log: Log,
}
impl FromRef for Config {
@@ -59,6 +61,12 @@ impl FromRef for db::Connection {
}
}
+impl FromRef for Log {
+ fn from_ref(app_state: &AppState) -> Log {
+ app_state.log.clone()
+ }
+}
+
impl axum::response::IntoResponse for db::DBError {
fn into_response(self) -> Response {
ron_utils::ron_error(StatusCode::INTERNAL_SERVER_ERROR, &self.to_string()).into_response()
@@ -103,7 +111,7 @@ impl Context {
#[tokio::main]
async fn main() {
let config = config::load();
- let _guard = log::init(&config.logs_directory);
+ let log = Log::new(&config.logs_directory);
event!(Level::INFO, "Configuration: {:?}", config);
@@ -133,6 +141,7 @@ async fn main() {
let state = AppState {
config,
db_connection,
+ log,
};
let ron_api_routes = Router::new()
diff --git a/backend/src/services/mod.rs b/backend/src/services/mod.rs
index 37122e5..3483809 100644
--- a/backend/src/services/mod.rs
+++ b/backend/src/services/mod.rs
@@ -1,13 +1,14 @@
use askama::Template;
use axum::{
body, debug_handler,
- extract::{Extension, Request, State},
+ extract::{Extension, Query, Request, State},
http::{StatusCode, header},
middleware::Next,
response::{Html, IntoResponse, Response},
};
+use serde::Deserialize;
-use crate::{Context, Result, consts, data::db, html_templates::*, ron_utils};
+use crate::{AppState, Context, Result, consts, data::db, html_templates::*, log::Log, ron_utils};
pub mod fragments;
pub mod recipe;
@@ -62,10 +63,18 @@ pub async fn home_page(
///// DEV_PANEL /////
-#[debug_handler]
+#[derive(Deserialize)]
+pub struct LogFile {
+ #[serde(default)]
+ pub log_file: String,
+}
+
+#[debug_handler(state = AppState)]
pub async fn dev_panel(
State(connection): State,
+ State(log): State,
Extension(context): Extension,
+ log_file: Query,
) -> Result {
if context.user.is_some() && context.user.as_ref().unwrap().is_admin {
Ok(Html(
@@ -73,6 +82,8 @@ pub async fn dev_panel(
recipes: Recipes::new(connection, &context.user, context.tr.current_lang_code())
.await?,
context,
+ log,
+ current_log_file: log_file.log_file.clone(),
}
.render()?,
)
diff --git a/backend/templates/dev_panel.html b/backend/templates/dev_panel.html
index 89a90d1..18a5ccf 100644
--- a/backend/templates/dev_panel.html
+++ b/backend/templates/dev_panel.html
@@ -2,13 +2,36 @@
{% block content %}
-
+
+
+
+
+ {% for f in log.file_names().unwrap() %}
+ - {{ f }}
+ {% endfor %}
+
+
+ {% match log.read_content(current_log_file) %}
+ {% when Ok(lines) %}
+ {% for l in lines %}
+
{{ l }}
+ {% endfor %}
+ {% when Err(err) %}
+ Error reading log: {{ err }}
+ {% endmatch %}
+
+