mirror of
https://github.com/IllusionMan1212/3dobs.git
synced 2026-10-24 10:12:35 +00:00
log: logging to file, console and stdout
This commit is contained in:
+6
-4
@@ -1,5 +1,7 @@
|
||||
use std::{io::{BufReader, BufRead}, collections::HashMap, path::PathBuf};
|
||||
|
||||
use log::{error, warn, trace};
|
||||
|
||||
use crate::{mesh::Vertex, aabb::AABB, importer::ObjMesh, importer::Object, importer::Material};
|
||||
|
||||
const BUF_CAP: usize = 1024 * 128; // 128 Kilobytes
|
||||
@@ -140,7 +142,7 @@ fn parse_mtl(path: &PathBuf) -> Result<HashMap<String, Material>, Box<dyn std::e
|
||||
Some(MtlToken::DiffuseTexture) => {
|
||||
// TODO: textures
|
||||
}
|
||||
_ => { println!("Unhandled material token: {}", token) },
|
||||
_ => { warn!("Unhandled material token: {}", token) },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,7 +329,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
||||
materials.extend(m);
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Failed to parse mtl file {:?}: {}", material_path, e);
|
||||
error!("Failed to parse mtl file {:?}: {}", material_path, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,12 +362,12 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
||||
Some(ObjToken::Line) | Some(ObjToken::Point) => {
|
||||
// we don't handle lines or points
|
||||
}
|
||||
_ => { println!("Unhandled obj token: {}", token) },
|
||||
_ => { warn!("Unhandled obj token: {}", token) },
|
||||
}
|
||||
}
|
||||
}
|
||||
let elapsed = now.elapsed();
|
||||
println!("Loaded in {}ms", elapsed.as_millis());
|
||||
trace!("Loaded in {}ms", elapsed.as_millis());
|
||||
|
||||
let mesh_name = {
|
||||
if current_mesh_name.is_empty() && !object_name.is_empty() {
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
use std::io::{Read, Seek, BufReader, BufRead};
|
||||
|
||||
use log::trace;
|
||||
|
||||
use crate::{importer::ObjMesh, importer::Object, importer::Material, aabb::AABB, mesh::Vertex};
|
||||
|
||||
const STL_HEADER_SIZE: u64 = 80;
|
||||
@@ -229,7 +231,7 @@ pub fn load_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::error::E
|
||||
parse_binary_stl(file)?
|
||||
};
|
||||
let elapsed = now.elapsed();
|
||||
println!("Loaded in {} ms", elapsed.as_millis());
|
||||
trace!("Loaded in {} ms", elapsed.as_millis());
|
||||
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
use std::{os::unix::net::{UnixListener, UnixStream}, path::PathBuf, io::{Write, Read}, fs::File, process::exit, thread, sync::mpsc::Receiver};
|
||||
|
||||
use fs4::FileExt;
|
||||
use log::error;
|
||||
|
||||
fn create_named_pipe(pipe_path: PathBuf) -> UnixListener {
|
||||
if pipe_path.exists() {
|
||||
@@ -58,7 +59,7 @@ pub fn init(lock_file: &File, args_paths: Vec<PathBuf>, one_instance: bool) -> O
|
||||
ipc_tx.send(paths).unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error: {:?}", err);
|
||||
error!("Error: {:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ pub mod utils;
|
||||
pub mod mesh;
|
||||
pub mod model;
|
||||
pub mod ui;
|
||||
pub mod log;
|
||||
pub mod logger;
|
||||
pub mod aabb;
|
||||
pub mod importer;
|
||||
pub mod ipc;
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum LogLevel {
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl From<LogLevel> for mint::Vector4<f32> {
|
||||
fn from(level: LogLevel) -> Self {
|
||||
match level {
|
||||
LogLevel::Debug => [0.5, 0.5, 1.0, 1.0].into(),
|
||||
LogLevel::Info => [0.5, 0.5, 0.5, 1.0].into(),
|
||||
LogLevel::Warning => [1.0, 0.64, 0.0, 1.0].into(),
|
||||
LogLevel::Error => [1.0, 0.0, 0.0, 1.0].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LogMessage {
|
||||
pub level: LogLevel,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl LogMessage {
|
||||
pub fn new(level: LogLevel, message: &str) -> Self {
|
||||
let severity = match level {
|
||||
LogLevel::Debug => "DEBUG",
|
||||
LogLevel::Info => "INFO",
|
||||
LogLevel::Warning => "WARNING",
|
||||
LogLevel::Error => "ERROR",
|
||||
};
|
||||
|
||||
Self {
|
||||
level,
|
||||
message: format!("[{}]: {}", severity, message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Log {
|
||||
pub history: Vec<LogMessage>,
|
||||
}
|
||||
|
||||
impl Log {
|
||||
pub fn clear(&mut self) {
|
||||
self.history.clear();
|
||||
}
|
||||
|
||||
pub fn log(&mut self, message: &str, level: LogLevel) {
|
||||
// TODO: also write to a log file
|
||||
self.history.push(LogMessage::new(level, message));
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Log {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
history: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
use std::{io::Write, sync::{Arc, RwLock}};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum LogLevel {
|
||||
Error,
|
||||
Warn,
|
||||
Info,
|
||||
Debug,
|
||||
}
|
||||
|
||||
impl From<LogLevel> for mint::Vector4<f32> {
|
||||
fn from(level: LogLevel) -> Self {
|
||||
match level {
|
||||
LogLevel::Debug => [0.5, 0.5, 1.0, 1.0].into(),
|
||||
LogLevel::Info => [0.5, 0.5, 0.5, 1.0].into(),
|
||||
LogLevel::Warn => [1.0, 0.64, 0.0, 1.0].into(),
|
||||
LogLevel::Error => [1.0, 0.0, 0.0, 1.0].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<log::LevelFilter> for LogLevel {
|
||||
fn from(level: log::LevelFilter) -> Self {
|
||||
match level {
|
||||
log::LevelFilter::Debug => LogLevel::Debug,
|
||||
log::LevelFilter::Info => LogLevel::Info,
|
||||
log::LevelFilter::Warn => LogLevel::Warn,
|
||||
log::LevelFilter::Error => LogLevel::Error,
|
||||
_ => LogLevel::Debug,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LogMessage {
|
||||
pub level: LogLevel,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl LogMessage {
|
||||
pub fn new(level: LogLevel, message: &str) -> Self {
|
||||
Self {
|
||||
level,
|
||||
message: format!("{}", message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Log {
|
||||
pub history: Vec<LogMessage>,
|
||||
}
|
||||
|
||||
impl Log {
|
||||
pub fn clear(&mut self) {
|
||||
self.history.clear();
|
||||
}
|
||||
|
||||
pub fn log(&mut self, message: &str, level: LogLevel) {
|
||||
self.history.push(LogMessage::new(level, message));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WritableLog {
|
||||
pub arc: Arc<RwLock<Log>>,
|
||||
buf: String
|
||||
}
|
||||
|
||||
impl Default for WritableLog {
|
||||
fn default() -> Self {
|
||||
Self{arc: Arc::new(RwLock::new(Log::default())), buf: String::with_capacity(1024)}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for WritableLog {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.buf.push_str(std::str::from_utf8(buf).unwrap());
|
||||
if buf.contains(&b'\n') {
|
||||
self.flush()?;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
// match the log level found at index 0 till space to LogLevel
|
||||
let level = match &self.buf[0..self.buf.find(' ').unwrap()] {
|
||||
"[ERROR]" => LogLevel::Error,
|
||||
"[WARN]" => LogLevel::Warn,
|
||||
"[INFO]" => LogLevel::Info,
|
||||
"[DEBUG]" => LogLevel::Debug,
|
||||
_ => LogLevel::Info,
|
||||
};
|
||||
|
||||
let mut logger = self.arc.write().unwrap();
|
||||
logger.log(&self.buf, level);
|
||||
self.buf.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+40
-3
@@ -1,13 +1,49 @@
|
||||
use glfw::{Action, Context, Key, Modifiers};
|
||||
use glad_gl::gl;
|
||||
use anyhow;
|
||||
use std::fs::File;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use glfw::{Action, Context, Key, Modifiers};
|
||||
use glad_gl::gl;
|
||||
use anyhow;
|
||||
use simplelog::*;
|
||||
|
||||
use threedobs::{shader, ui::ui, utils, ipc};
|
||||
|
||||
fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
|
||||
let logger = threedobs::logger::WritableLog::default();
|
||||
|
||||
let log_conf = ConfigBuilder::default()
|
||||
.set_target_level(LevelFilter::Error)
|
||||
.set_thread_level(LevelFilter::Off)
|
||||
.set_level_color(Level::Error, Some(Color::Red))
|
||||
.set_level_color(Level::Debug, Some(Color::Rgb(128, 128, 255)))
|
||||
.set_level_color(Level::Warn, Some(Color::Rgb(255, 163, 0)))
|
||||
.set_level_color(Level::Info, Some(Color::Rgb(128, 128, 128)))
|
||||
.build();
|
||||
let in_program_log_conf = ConfigBuilder::default()
|
||||
.set_target_level(LevelFilter::Off)
|
||||
.set_time_level(LevelFilter::Off)
|
||||
.set_thread_level(LevelFilter::Off)
|
||||
.set_level_color(Level::Error, Some(Color::Red))
|
||||
.set_level_color(Level::Debug, Some(Color::Rgb(128, 128, 255)))
|
||||
.set_level_color(Level::Warn, Some(Color::Rgb(255, 255, 0)))
|
||||
.set_level_color(Level::Info, Some(Color::Rgb(128, 128, 128)))
|
||||
.build();
|
||||
|
||||
let log_level = if cfg!(debug_assertions) {
|
||||
LevelFilter::Debug
|
||||
} else {
|
||||
LevelFilter::Info
|
||||
};
|
||||
|
||||
CombinedLogger::init(
|
||||
vec![
|
||||
TermLogger::new(log_level, log_conf.clone(), TerminalMode::Mixed, ColorChoice::Auto),
|
||||
WriteLogger::new(log_level, log_conf, File::create("3dobs.log")?),
|
||||
WriteLogger::new(log_level, in_program_log_conf, logger.clone())
|
||||
]
|
||||
).unwrap();
|
||||
|
||||
let settings: ui::Settings = confy::load("3dobs", "settings")?;
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
@@ -36,6 +72,7 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let mut state = ui::State{
|
||||
settings,
|
||||
logger,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
+5
-4
@@ -1,5 +1,6 @@
|
||||
use glad_gl::gl;
|
||||
use anyhow::Result;
|
||||
use log::{debug, error};
|
||||
|
||||
pub struct ShaderSource {
|
||||
pub name: String,
|
||||
@@ -26,7 +27,7 @@ impl Shader {
|
||||
let mut success1 = 0;
|
||||
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success1);
|
||||
#[cfg(debug_assertions)]
|
||||
println!("vertex shader {:?} compiled with status: {}",
|
||||
debug!("vertex shader {:?} compiled with status: {}",
|
||||
vertex_obj.name,
|
||||
success1
|
||||
);
|
||||
@@ -34,7 +35,7 @@ impl Shader {
|
||||
let info_buf = [0u8; 512];
|
||||
gl::GetShaderInfoLog(vertex_shader as u32, 512, std::ptr::null_mut(), info_buf.as_ptr() as *mut i8);
|
||||
#[cfg(debug_assertions)]
|
||||
println!("vertex shader info: {}", std::str::from_utf8(&info_buf).unwrap());
|
||||
error!("vertex shader info: {}", std::str::from_utf8(&info_buf).unwrap());
|
||||
}
|
||||
|
||||
let frag_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
|
||||
@@ -45,7 +46,7 @@ impl Shader {
|
||||
let mut success2 = 0;
|
||||
gl::GetShaderiv(frag_shader, gl::COMPILE_STATUS, &mut success2);
|
||||
#[cfg(debug_assertions)]
|
||||
println!("frag shader {:?} compiled with status: {}",
|
||||
debug!("frag shader {:?} compiled with status: {}",
|
||||
frag_obj.name,
|
||||
success2
|
||||
);
|
||||
@@ -53,7 +54,7 @@ impl Shader {
|
||||
let info_buf2 = [0u8; 512];
|
||||
gl::GetShaderInfoLog(frag_shader as u32, 512, std::ptr::null_mut(), info_buf2.as_ptr() as *mut i8);
|
||||
#[cfg(debug_assertions)]
|
||||
println!("frag shader info: {}", std::str::from_utf8(&info_buf2).unwrap());
|
||||
error!("frag shader info: {}", std::str::from_utf8(&info_buf2).unwrap());
|
||||
}
|
||||
|
||||
let shader_program = gl::CreateProgram();
|
||||
|
||||
+16
-24
@@ -1,9 +1,10 @@
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use glad_gl::gl;
|
||||
use log::{info, debug};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::{camera::Camera, model, imgui_glfw_support, imgui_opengl_renderer, mesh, ui, log, utils};
|
||||
use crate::{camera::Camera, model, imgui_glfw_support, imgui_opengl_renderer, mesh, ui, logger, utils};
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Settings {
|
||||
@@ -26,7 +27,7 @@ pub struct State {
|
||||
pub camera: Camera,
|
||||
pub objects: Vec<model::Model>,
|
||||
pub viewport_size: [f32; 2],
|
||||
pub logger: log::Log,
|
||||
pub logger: logger::WritableLog,
|
||||
pub settings: Settings,
|
||||
}
|
||||
|
||||
@@ -48,7 +49,7 @@ impl Default for State {
|
||||
camera: Camera::new(),
|
||||
objects: vec![],
|
||||
viewport_size: [0.0, 0.0],
|
||||
logger: log::Log::default(),
|
||||
logger: logger::WritableLog::default(),
|
||||
settings: Settings::default(),
|
||||
}
|
||||
}
|
||||
@@ -210,10 +211,7 @@ fn draw_object_hierarchy(ui: &imgui::Ui, state: &mut State, idx: usize) -> bool
|
||||
|
||||
ui.table_next_column();
|
||||
if ui.small_button(format!("X###{}-{}", object.name.as_str(), idx)) {
|
||||
let output = format!("Removing object {}", object.name);
|
||||
println!("{}", output);
|
||||
|
||||
state.logger.log(&output, log::LogLevel::Info);
|
||||
info!("Removing object {}", object.name);
|
||||
return true;
|
||||
}
|
||||
if ui.checkbox("Selected", &mut (Some(state.objects[idx].id) == state.active_model)) {
|
||||
@@ -256,8 +254,9 @@ fn draw_console(ui: &imgui::Ui, state: &mut State) {
|
||||
ui.child_window("###ConsoleHistory")
|
||||
.size([0.0, -27.0])
|
||||
.build(|| {
|
||||
for line in state.logger.history.iter() {
|
||||
let style = ui.push_style_color(imgui::StyleColor::Text, line.level.clone());
|
||||
for line in state.logger.arc.read().unwrap().history.iter() {
|
||||
let style = ui.push_style_color(imgui::StyleColor::Text, line.level);
|
||||
|
||||
ui.text_wrapped(line.message.clone());
|
||||
style.pop();
|
||||
}
|
||||
@@ -268,7 +267,8 @@ fn draw_console(ui: &imgui::Ui, state: &mut State) {
|
||||
|
||||
ui.separator();
|
||||
if ui.button("Clear") {
|
||||
state.logger.clear();
|
||||
let mut logger = state.logger.arc.write().unwrap();
|
||||
logger.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -369,21 +369,13 @@ fn draw_viewport(ui: &imgui::Ui, state: &mut State, texture: u32) {
|
||||
let _ = capture.save(save_path);
|
||||
let elapsed = now.elapsed();
|
||||
|
||||
state.logger.log(
|
||||
format!("Scene capture saved to: {} successfully",
|
||||
save_path
|
||||
.canonicalize()
|
||||
.expect("Capture path to be canonicalized")
|
||||
.to_str()
|
||||
.expect("Capture path to be valid unicode"))
|
||||
.as_str(),
|
||||
log::LogLevel::Info);
|
||||
info!("Scene capture saved to: {} successfully", save_path
|
||||
.canonicalize()
|
||||
.expect("Capture path to be canonicalized")
|
||||
.to_str()
|
||||
.expect("Capture path to be valid unicode"));
|
||||
|
||||
state.logger.log(
|
||||
format!("Scene capture took: {}ms",
|
||||
elapsed.as_millis())
|
||||
.as_str(),
|
||||
log::LogLevel::Debug);
|
||||
debug!("Scene capture took: {}ms", elapsed.as_millis());
|
||||
|
||||
unsafe {
|
||||
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
|
||||
|
||||
+7
-11
@@ -3,8 +3,9 @@ use std::path::PathBuf;
|
||||
use glad_gl::gl;
|
||||
use glm;
|
||||
use anyhow::{Result, Context};
|
||||
use ::log::{info, error};
|
||||
|
||||
use crate::{log, model, ui, importer};
|
||||
use crate::{model, ui, importer};
|
||||
|
||||
pub enum SupportedFileExtensions {
|
||||
OBJ,
|
||||
@@ -66,18 +67,18 @@ pub fn import_models_from_paths(paths: &Vec<PathBuf>, state: &mut ui::ui::State)
|
||||
for model_path in paths {
|
||||
let filename = model_path.file_name();
|
||||
if model_path.is_dir() {
|
||||
state.logger.log(&format!("Skipping directory \"{}\"", filename.unwrap().to_str().unwrap()), log::LogLevel::Info);
|
||||
info!("Skipping directory \"{}\"", filename.unwrap().to_str().unwrap());
|
||||
continue;
|
||||
}
|
||||
match model_path.extension() {
|
||||
Some(ext) => {
|
||||
if SupportedFileExtensions::from_str(ext.to_str().unwrap()).is_none() {
|
||||
state.logger.log(&format!("Skipping file \"{}\" because it is not an OBJ or STL file", filename.unwrap().to_str().unwrap()), log::LogLevel::Info);
|
||||
info!("Skipping file \"{}\" because it is not an OBJ or STL file", filename.unwrap().to_str().unwrap());
|
||||
continue;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
state.logger.log(&format!("Skipping file \"{}\" because it has no extension", filename.unwrap().to_str().unwrap()), log::LogLevel::Info);
|
||||
info!("Skipping file \"{}\" because it has no extension", filename.unwrap().to_str().unwrap());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -88,19 +89,14 @@ pub fn import_models_from_paths(paths: &Vec<PathBuf>, state: &mut ui::ui::State)
|
||||
|
||||
state.active_model = Some(m.id);
|
||||
if let Some(model_name) = filename {
|
||||
let msg = format!("Loaded model \"{}\"", model_name.to_str().unwrap());
|
||||
println!("{}", msg);
|
||||
state.logger.log(&msg, log::LogLevel::Info);
|
||||
info!("Loaded model \"{}\"", model_name.to_str().unwrap());
|
||||
m.name = model_name.to_str().unwrap().to_string();
|
||||
}
|
||||
state.objects.push(m);
|
||||
state.camera.focus_on_selected_model(state.active_model, &state.objects);
|
||||
},
|
||||
Err(e) => {
|
||||
let error = format!("Error loading model \"{}\": {}", model_path.to_str().unwrap(), e);
|
||||
println!("{}", error);
|
||||
|
||||
state.logger.log(&error, log::LogLevel::Error);
|
||||
error!("Error loading model \"{}\": {}", model_path.to_str().unwrap(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user