Skip to content

Commit

Permalink
Add --log-console
Browse files Browse the repository at this point in the history
  • Loading branch information
notoriaga committed Jan 19, 2022
1 parent b301aa4 commit 7e48208
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 95 deletions.
2 changes: 1 addition & 1 deletion console_backend/src/bin/headless-console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ Usage:
let (_server_send, server_recv) = channel::unbounded::<Vec<u8>>();
let client_send = ChannelSender::boxed(client_send_);
let shared_state = SharedState::new();
setup_logging(client_send.clone(), shared_state.clone());
let conn_manager = ConnectionManager::new(client_send.clone(), shared_state.clone());
handle_cli(opt, &conn_manager, shared_state.clone(), &client_send);
setup_logging(client_send.clone(), shared_state.clone());
refresh_connection_frontend(&client_send, &shared_state);
refresh_loggingbar(&client_send, &shared_state);
server_recv_thread(conn_manager, client_send, server_recv, shared_state);
Expand Down
14 changes: 10 additions & 4 deletions console_backend/src/cli_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use clap::Parser;
use log::{debug, error};
use strum::VariantNames;

use crate::errors::CONVERT_TO_STR_FAILURE;
use crate::log_panel::LogLevel;
use crate::output::CsvLogging;
use crate::shared_state::SharedState;
Expand All @@ -21,6 +20,7 @@ use crate::{
common_constants::{SbpLogging, Tabs},
connection::{Connection, ConnectionManager},
};
use crate::{constants::LOG_FILENAME, errors::CONVERT_TO_STR_FAILURE};

#[derive(Debug)]
pub struct CliLogLevel(LogLevel);
Expand Down Expand Up @@ -95,9 +95,9 @@ pub struct CliOptions {
#[clap(subcommand)]
pub input: Option<Input>,

/// Log messages to terminal.
/// Create a log file containing console debug information.
#[clap(long)]
pub log_stderr: bool,
pub log_console: bool,

/// Exit when connection closes.
#[clap(long)]
Expand Down Expand Up @@ -319,11 +319,17 @@ pub fn handle_cli(
}
}
}
if let Some(ref path) = opt.settings_yaml {
sbp_settings::setting::load_from_path(path).expect("failed to load settings");
}
if let Some(folder) = opt.log_dirname {
shared_state.set_logging_directory(PathBuf::from(folder));
}
shared_state.lock().logging_bar.csv_logging = CsvLogging::from(opt.csv_log);
shared_state.lock().log_to_std.set(opt.log_stderr);
if opt.log_console {
let filename = chrono::Local::now().format(LOG_FILENAME).to_string().into();
shared_state.set_log_filename(Some(filename));
}
if let Some(path) = opt.sbp_log_filename {
shared_state.set_sbp_logging_filename(Some(path));
}
Expand Down
1 change: 1 addition & 0 deletions console_backend/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub(crate) const APPLICATION_NAME: &str = "swift_navigation_console";
// Logging constants
#[allow(dead_code)]
pub(crate) const LOG_WRITER_BUFFER_MESSAGE_COUNT: usize = 50;
pub(crate) const LOG_FILENAME: &str = "swift-console-%Y%m%d-%H%M%S.log";

// Main Tab constants.
pub(crate) const WRITE_TO_DEVICE_SENDER_ID: u16 = 1337;
Expand Down
185 changes: 111 additions & 74 deletions console_backend/src/log_panel.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
use async_logger_log::Logger;
use sbp::messages::logging::MsgLog;
use std::{fs::File, io::Write};

use async_logger::Writer;
use async_logger_log::Logger;
use capnp::message::Builder;
use chrono::Local;
use log::{debug, error, info, warn, LevelFilter, Record};
use sbp::messages::logging::MsgLog;
use serde::{Deserialize, Serialize};

use crate::client_sender::BoxedClientSender;
use crate::common_constants as cc;
use crate::constants::LOG_WRITER_BUFFER_MESSAGE_COUNT;
use crate::errors::CONSOLE_LOG_JSON_TO_STRING_FAILURE;
use crate::shared_state::SharedState;
use crate::types::ArcBool;
use crate::utils::serialize_capnproto_builder;

use async_logger::Writer;
use chrono::Local;
use log::{debug, error, info, warn, LevelFilter, Record};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct ConsoleLogPacket {
level: String,
timestamp: String,
msg: String,
}

const DEVICE: &str = "DEVICE";
const CONSOLE: &str = "CONSOLE";

pub type LogLevel = cc::LogLevel;

impl LogLevel {
pub fn level_filter(&self) -> LevelFilter {
match self {
Expand All @@ -38,52 +31,6 @@ impl LogLevel {
}
}

// Custom formatting of `log::Record` to account for SbpLog values
pub fn splitable_log_formatter(record: &Record) -> String {
let level = if record.target() != DEVICE {
CONSOLE
} else {
record.level().as_str()
};
let timestamp = Local::now().format("%b %d %Y %H:%M:%S");
let mut msg = record.args().to_string();
msg.retain(|c| c != '\0');
let msg_packet = ConsoleLogPacket {
level: level.to_string(),
timestamp: timestamp.to_string(),
msg,
};
serde_json::to_string(&msg_packet).expect(CONSOLE_LOG_JSON_TO_STRING_FAILURE)
}

enum SbpMsgLevel {
Emergency = 0,
Alert = 1,
Critical = 2,
Error = 3,
Warn = 4,
Notice = 5,
Info = 6,
Debug = 7,
Other,
}

impl From<u8> for SbpMsgLevel {
fn from(orig: u8) -> Self {
match orig {
0 => SbpMsgLevel::Emergency,
1 => SbpMsgLevel::Alert,
2 => SbpMsgLevel::Critical,
3 => SbpMsgLevel::Error,
4 => SbpMsgLevel::Warn,
5 => SbpMsgLevel::Notice,
6 => SbpMsgLevel::Info,
7 => SbpMsgLevel::Debug,
_ => SbpMsgLevel::Other,
}
}
}

pub fn handle_log_msg(msg: MsgLog) {
let text = msg.text.to_string();
let level: SbpMsgLevel = SbpMsgLevel::from(msg.level);
Expand Down Expand Up @@ -112,17 +59,17 @@ pub fn setup_logging(client_sender: BoxedClientSender, shared_state: SharedState
}

#[derive(Debug)]
pub struct LogPanelWriter {
pub client_sender: BoxedClientSender,
struct LogPanelWriter {
client_sender: BoxedClientSender,
shared_state: SharedState,
pub log_to_std: ArcBool,
log_file: Option<File>,
}

impl LogPanelWriter {
pub fn new(client_sender: BoxedClientSender, shared_state: SharedState) -> LogPanelWriter {
LogPanelWriter {
log_file: init_log_file(&shared_state),
client_sender,
log_to_std: shared_state.log_to_std(),
shared_state,
}
}
Expand All @@ -139,22 +86,112 @@ impl Writer<Box<String>> for LogPanelWriter {

let mut log_update = msg.init_log_append();
log_update.set_log_level(&self.shared_state.log_level().to_string());
let mut entries = log_update.init_entries(slice.len() as u32);

let mut entries = log_update.init_entries(slice.len() as u32);
for (idx, item) in slice.iter().enumerate() {
let packet: ConsoleLogPacket =
serde_json::from_str(item).expect(CONSOLE_LOG_JSON_TO_STRING_FAILURE);
if self.log_to_std.get() {
eprintln!("{}\t{}\t{}", packet.timestamp, packet.level, packet.msg);
}
let mut entry = entries.reborrow().get(idx as u32);

entry.set_line(item);
}

self.client_sender
.send_data(serialize_capnproto_builder(builder));

if let Some(ref mut f) = self.log_file {
for item in slice {
let packet = serde_json::from_str(item).expect(CONSOLE_LOG_JSON_TO_STRING_FAILURE);
write_packet(packet, f);
}
}
}

fn flush(&mut self) {}
fn flush(&mut self) {
if let Some(ref mut f) = self.log_file {
let _ = f.flush();
}
}
}

#[derive(Serialize, Deserialize)]
struct ConsoleLogPacket<'a> {
level: &'a str,
timestamp: &'a str,
msg: &'a str,
}

enum SbpMsgLevel {
Emergency = 0,
Alert = 1,
Critical = 2,
Error = 3,
Warn = 4,
Notice = 5,
Info = 6,
Debug = 7,
Other,
}

impl From<u8> for SbpMsgLevel {
fn from(orig: u8) -> Self {
match orig {
0 => SbpMsgLevel::Emergency,
1 => SbpMsgLevel::Alert,
2 => SbpMsgLevel::Critical,
3 => SbpMsgLevel::Error,
4 => SbpMsgLevel::Warn,
5 => SbpMsgLevel::Notice,
6 => SbpMsgLevel::Info,
7 => SbpMsgLevel::Debug,
_ => SbpMsgLevel::Other,
}
}
}

fn init_log_file(shared_state: &SharedState) -> Option<File> {
let filepath = shared_state
.log_filename()
.map(|f| shared_state.logging_directory().join(f));
filepath.and_then(|p| match File::create(&p) {
Ok(f) => Some(f),
Err(e) => {
error!(
"issue creating console log file, {}, error, {}",
p.display(),
e
);
None
}
})
}

fn write_packet(packet: ConsoleLogPacket, f: &mut File) {
// Min one space plus the longest log level
const MIN_SPACES: usize = "CONSOLE".len() + 1;

let spaces = " ".repeat(MIN_SPACES - packet.level.len());
if let Err(e) = writeln!(
f,
"{timestamp} {level}{spaces}{msg}",
timestamp = packet.timestamp,
level = packet.level,
msg = packet.msg,
) {
error!("error writing console logs to file: {e}");
}
}

// Custom formatting of `log::Record` to account for SbpLog values
fn splitable_log_formatter(record: &Record) -> String {
let level = if record.target() != DEVICE {
CONSOLE
} else {
record.level().as_str()
};
let timestamp = Local::now().format("%b %d %Y %H:%M:%S").to_string();
let mut msg = record.args().to_string();
msg.retain(|c| c != '\0');
let msg_packet = ConsoleLogPacket {
level,
timestamp: &timestamp,
msg: &msg,
};
serde_json::to_string(&msg_packet).expect(CONSOLE_LOG_JSON_TO_STRING_FAILURE)
}
5 changes: 1 addition & 4 deletions console_backend/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,11 @@ impl Server {
server_send: Some(server_send),
};
let shared_state = SharedState::new();
setup_logging(client_send.clone(), shared_state.clone());
let opt = CliOptions::from_filtered_cli();
if let Some(ref path) = opt.settings_yaml {
sbp_settings::setting::load_from_path(path).expect("failed to load settings");
}
let conn_manager = ConnectionManager::new(client_send.clone(), shared_state.clone());
// Handle CLI Opts.
handle_cli(opt, &conn_manager, shared_state.clone(), &client_send);
setup_logging(client_send.clone(), shared_state.clone());
refresh_connection_frontend(&client_send, &shared_state);
refresh_loggingbar(&client_send, &shared_state);
server_recv_thread(conn_manager, client_send, server_recv, shared_state);
Expand Down
27 changes: 15 additions & 12 deletions console_backend/src/shared_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ use crate::output::{CsvLogging, CsvSerializer};
use crate::process_messages::StopToken;
use crate::settings_tab;
use crate::solution_tab::LatLonUnits;
use crate::types::ArcBool;
use crate::update_tab::UpdateTabUpdate;
use crate::utils::send_conn_state;
use crate::watch::{WatchReceiver, Watched};
Expand Down Expand Up @@ -84,12 +83,19 @@ impl SharedState {
LOG_DIRECTORY.path()
}
}
pub fn log_level(&self) -> LogLevel {
self.lock().log_panel.level.clone()
}
pub fn set_log_level(&self, log_level: LogLevel) {
self.lock().log_panel.log_level = log_level.clone();
log::set_max_level(log_level.level_filter());
let filter = log_level.level_filter();
self.lock().log_panel.level = log_level;
log::set_max_level(filter);
}
pub fn log_level(&self) -> LogLevel {
self.lock().log_panel.log_level.clone()
pub fn log_filename(&self) -> Option<PathBuf> {
self.lock().log_panel.filename.clone()
}
pub fn set_log_filename(&self, filename: Option<PathBuf>) {
self.lock().log_panel.filename = filename;
}
pub fn reset_logging(&self) {
let mut guard = self.lock();
Expand Down Expand Up @@ -319,9 +325,6 @@ impl SharedState {
guard.auto_survey_data.alt = Some(alt);
guard.auto_survey_data.requested = false;
}
pub fn log_to_std(&self) -> ArcBool {
self.lock().log_to_std.clone()
}
pub fn heartbeat_data(&self) -> Heartbeat {
self.lock().heartbeat_data.clone()
}
Expand Down Expand Up @@ -366,7 +369,6 @@ pub struct SharedStateInner {
pub(crate) reset_device: bool,
pub(crate) advanced_networking_update: Option<AdvancedNetworkingState>,
pub(crate) auto_survey_data: AutoSurveyData,
pub(crate) log_to_std: ArcBool,
pub(crate) heartbeat_data: Heartbeat,
}
impl SharedStateInner {
Expand All @@ -393,7 +395,6 @@ impl SharedStateInner {
reset_device: false,
advanced_networking_update: None,
auto_survey_data: AutoSurveyData::new(),
log_to_std: ArcBool::new_with(true),
heartbeat_data,
}
}
Expand Down Expand Up @@ -455,13 +456,15 @@ impl LoggingBarState {

#[derive(Debug)]
pub struct LogPanelState {
pub log_level: LogLevel,
pub level: LogLevel,
pub filename: Option<PathBuf>,
}

impl LogPanelState {
fn new() -> LogPanelState {
LogPanelState {
log_level: LogLevel::WARNING,
level: LogLevel::WARNING,
filename: None,
}
}
}
Expand Down

0 comments on commit 7e48208

Please sign in to comment.