mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
implements error log reporting (#464)
* implements error log reporting * changes api post variables, includes warnings
This commit is contained in:
committed by
GitHub
parent
dc7461746c
commit
1808c085e8
23
appmgr/Cargo.lock
generated
23
appmgr/Cargo.lock
generated
@@ -848,6 +848,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"simple-logging",
|
"simple-logging",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"stderrlog",
|
||||||
"tar",
|
"tar",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio 1.11.0",
|
"tokio 1.11.0",
|
||||||
@@ -2958,6 +2959,19 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stderrlog"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45a53e2eff3e94a019afa6265e8ee04cb05b9d33fe9f5078b14e4e391d155a38"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"chrono",
|
||||||
|
"log",
|
||||||
|
"termcolor",
|
||||||
|
"thread_local",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stdweb"
|
name = "stdweb"
|
||||||
version = "0.4.20"
|
version = "0.4.20"
|
||||||
@@ -3183,6 +3197,15 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ sqlx = { version = "0.5", features = [
|
|||||||
"runtime-tokio-rustls",
|
"runtime-tokio-rustls",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
] }
|
] }
|
||||||
|
stderrlog = "0.5.1"
|
||||||
tar = "0.4.35"
|
tar = "0.4.35"
|
||||||
thiserror = "1.0.24"
|
thiserror = "1.0.24"
|
||||||
tokio = { version = "1.11.0", features = ["full"] }
|
tokio = { version = "1.11.0", features = ["full"] }
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use embassy::status::{check_all, synchronize_all};
|
|||||||
use embassy::util::daemon;
|
use embassy::util::daemon;
|
||||||
use embassy::{Error, ErrorKind, ResultExt};
|
use embassy::{Error, ErrorKind, ResultExt};
|
||||||
use futures::{FutureExt, TryFutureExt};
|
use futures::{FutureExt, TryFutureExt};
|
||||||
|
use log::LevelFilter;
|
||||||
use patch_db::json_ptr::JsonPointer;
|
use patch_db::json_ptr::JsonPointer;
|
||||||
use reqwest::{Client, Proxy};
|
use reqwest::{Client, Proxy};
|
||||||
use rpc_toolkit::hyper::{Body, Response, Server, StatusCode};
|
use rpc_toolkit::hyper::{Body, Response, Server, StatusCode};
|
||||||
@@ -31,8 +32,11 @@ fn err_to_500(e: Error) -> Response<Body> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_main(cfg_path: Option<&str>) -> Result<Option<Shutdown>, Error> {
|
async fn inner_main(
|
||||||
let rpc_ctx = RpcContext::init(cfg_path).await?;
|
cfg_path: Option<&str>,
|
||||||
|
log_level: LevelFilter,
|
||||||
|
) -> Result<Option<Shutdown>, Error> {
|
||||||
|
let rpc_ctx = RpcContext::init(cfg_path, log_level).await?;
|
||||||
let mut shutdown_recv = rpc_ctx.shutdown.subscribe();
|
let mut shutdown_recv = rpc_ctx.shutdown.subscribe();
|
||||||
|
|
||||||
let sig_handler_ctx = rpc_ctx.clone();
|
let sig_handler_ctx = rpc_ctx.clone();
|
||||||
@@ -231,14 +235,15 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
simple_logging::log_to_stderr(match matches.occurrences_of("verbosity") {
|
// initializes the bootstrap logger, this will be replaced with the EmbassyLogger later
|
||||||
0 => log::LevelFilter::Off,
|
let filter = match matches.occurrences_of("verbosity") {
|
||||||
1 => log::LevelFilter::Error,
|
0 => log::LevelFilter::Error,
|
||||||
2 => log::LevelFilter::Warn,
|
1 => log::LevelFilter::Warn,
|
||||||
3 => log::LevelFilter::Info,
|
2 => log::LevelFilter::Info,
|
||||||
4 => log::LevelFilter::Debug,
|
3 => log::LevelFilter::Debug,
|
||||||
_ => log::LevelFilter::Trace,
|
_ => log::LevelFilter::Trace,
|
||||||
});
|
};
|
||||||
|
simple_logging::log_to_stderr(filter);
|
||||||
let cfg_path = matches.value_of("config");
|
let cfg_path = matches.value_of("config");
|
||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
@@ -246,7 +251,7 @@ fn main() {
|
|||||||
.enable_all()
|
.enable_all()
|
||||||
.build()
|
.build()
|
||||||
.expect("failed to initialize runtime");
|
.expect("failed to initialize runtime");
|
||||||
rt.block_on(inner_main(cfg_path))
|
rt.block_on(inner_main(cfg_path, filter))
|
||||||
};
|
};
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::sync::atomic::{AtomicU64, AtomicUsize};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
|
use log::LevelFilter;
|
||||||
use patch_db::json_ptr::JsonPointer;
|
use patch_db::json_ptr::JsonPointer;
|
||||||
use patch_db::{PatchDb, Revision};
|
use patch_db::{PatchDb, Revision};
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
@@ -25,6 +26,7 @@ use crate::manager::ManagerMap;
|
|||||||
use crate::net::tor::os_key;
|
use crate::net::tor::os_key;
|
||||||
use crate::net::NetController;
|
use crate::net::NetController;
|
||||||
use crate::shutdown::Shutdown;
|
use crate::shutdown::Shutdown;
|
||||||
|
use crate::util::logger::EmbassyLogger;
|
||||||
use crate::util::{from_toml_async_reader, AsyncFileExt};
|
use crate::util::{from_toml_async_reader, AsyncFileExt};
|
||||||
use crate::{Error, ResultExt};
|
use crate::{Error, ResultExt};
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ pub struct RpcContextConfig {
|
|||||||
pub revision_cache_size: Option<usize>,
|
pub revision_cache_size: Option<usize>,
|
||||||
pub zfs_pool_name: Option<String>,
|
pub zfs_pool_name: Option<String>,
|
||||||
pub datadir: Option<PathBuf>,
|
pub datadir: Option<PathBuf>,
|
||||||
|
pub log_server: Option<Url>,
|
||||||
}
|
}
|
||||||
impl RpcContextConfig {
|
impl RpcContextConfig {
|
||||||
pub async fn load<P: AsRef<Path>>(path: Option<P>) -> Result<Self, Error> {
|
pub async fn load<P: AsRef<Path>>(path: Option<P>) -> Result<Self, Error> {
|
||||||
@@ -116,17 +119,33 @@ pub struct RpcContextSeed {
|
|||||||
pub metrics_cache: RwLock<Option<crate::system::Metrics>>,
|
pub metrics_cache: RwLock<Option<crate::system::Metrics>>,
|
||||||
pub shutdown: Sender<Option<Shutdown>>,
|
pub shutdown: Sender<Option<Shutdown>>,
|
||||||
pub websocket_count: AtomicUsize,
|
pub websocket_count: AtomicUsize,
|
||||||
pub session_id: AtomicU64,
|
pub logger: EmbassyLogger,
|
||||||
|
pub log_epoch: Arc<AtomicU64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RpcContext(Arc<RpcContextSeed>);
|
pub struct RpcContext(Arc<RpcContextSeed>);
|
||||||
impl RpcContext {
|
impl RpcContext {
|
||||||
pub async fn init<P: AsRef<Path>>(cfg_path: Option<P>) -> Result<Self, Error> {
|
pub async fn init<P: AsRef<Path>>(
|
||||||
|
cfg_path: Option<P>,
|
||||||
|
log_level: LevelFilter,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
let base = RpcContextConfig::load(cfg_path).await?;
|
let base = RpcContextConfig::load(cfg_path).await?;
|
||||||
let (shutdown, _) = tokio::sync::broadcast::channel(1);
|
let (shutdown, _) = tokio::sync::broadcast::channel(1);
|
||||||
let secret_store = base.secret_store().await?;
|
let secret_store = base.secret_store().await?;
|
||||||
let db = base.db(&secret_store).await?;
|
let db = base.db(&secret_store).await?;
|
||||||
|
let share = crate::db::DatabaseModel::new()
|
||||||
|
.server_info()
|
||||||
|
.share_stats()
|
||||||
|
.get(&mut db.handle(), true)
|
||||||
|
.await?;
|
||||||
|
let log_epoch = Arc::new(AtomicU64::new(rand::random()));
|
||||||
|
let logger = EmbassyLogger::new(
|
||||||
|
log_level,
|
||||||
|
log_epoch.clone(),
|
||||||
|
base.log_server.clone(),
|
||||||
|
*share,
|
||||||
|
);
|
||||||
let docker = Docker::connect_with_unix_defaults()?;
|
let docker = Docker::connect_with_unix_defaults()?;
|
||||||
let net_controller = NetController::init(
|
let net_controller = NetController::init(
|
||||||
([127, 0, 0, 1], 80).into(),
|
([127, 0, 0, 1], 80).into(),
|
||||||
@@ -151,7 +170,8 @@ impl RpcContext {
|
|||||||
metrics_cache: RwLock::new(None),
|
metrics_cache: RwLock::new(None),
|
||||||
shutdown,
|
shutdown,
|
||||||
websocket_count: AtomicUsize::new(0),
|
websocket_count: AtomicUsize::new(0),
|
||||||
session_id: AtomicU64::new(rand::random()),
|
logger,
|
||||||
|
log_epoch,
|
||||||
});
|
});
|
||||||
let res = Self(seed);
|
let res = Self(seed);
|
||||||
res.managers
|
res.managers
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ async fn ws_handler<
|
|||||||
.websocket_count
|
.websocket_count
|
||||||
.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
|
.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
if new_count == 0 {
|
if new_count == 0 {
|
||||||
ctx.session_id
|
ctx.log_epoch
|
||||||
.store(rand::random(), std::sync::atomic::Ordering::SeqCst)
|
.store(rand::random(), std::sync::atomic::Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
()
|
()
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ impl Database {
|
|||||||
tor: Vec::new(),
|
tor: Vec::new(),
|
||||||
clearnet: Vec::new(),
|
clearnet: Vec::new(),
|
||||||
},
|
},
|
||||||
|
share_stats: false,
|
||||||
},
|
},
|
||||||
package_data: AllPackageData::default(),
|
package_data: AllPackageData::default(),
|
||||||
broken_packages: Vec::new(),
|
broken_packages: Vec::new(),
|
||||||
@@ -82,6 +83,7 @@ pub struct ServerInfo {
|
|||||||
unread_notification_count: u64,
|
unread_notification_count: u64,
|
||||||
specs: ServerSpecs,
|
specs: ServerSpecs,
|
||||||
connection_addresses: ConnectionAddresses,
|
connection_addresses: ConnectionAddresses,
|
||||||
|
share_stats: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
|||||||
@@ -68,7 +68,13 @@ pub fn main_api() -> Result<(), RpcError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command(subcommands(system::logs, system::metrics, shutdown::shutdown, shutdown::restart))]
|
#[command(subcommands(
|
||||||
|
system::config,
|
||||||
|
system::logs,
|
||||||
|
system::metrics,
|
||||||
|
shutdown::shutdown,
|
||||||
|
shutdown::restart
|
||||||
|
))]
|
||||||
pub fn server() -> Result<(), RpcError> {
|
pub fn server() -> Result<(), RpcError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -455,6 +455,22 @@ async fn get_disk_info() -> Result<MetricsDisk, Error> {
|
|||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command(subcommands(share_stats))]
|
||||||
|
pub async fn config() -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command(display(display_serializable))]
|
||||||
|
async fn share_stats(#[context] ctx: RpcContext, #[arg] value: bool) -> Result<(), Error> {
|
||||||
|
crate::db::DatabaseModel::new()
|
||||||
|
.server_info()
|
||||||
|
.share_stats()
|
||||||
|
.put(&mut ctx.db.handle(), &value)
|
||||||
|
.await?;
|
||||||
|
ctx.logger.set_sharing(value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
pub async fn test_get_temp() {
|
pub async fn test_get_temp() {
|
||||||
println!("{}", get_temp().await.unwrap())
|
println!("{}", get_temp().await.unwrap())
|
||||||
|
|||||||
85
appmgr/src/util/logger.rs
Normal file
85
appmgr/src/util/logger.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use log::{set_boxed_logger, LevelFilter, Metadata, Record};
|
||||||
|
use reqwest::{Client, Url};
|
||||||
|
use stderrlog::{StdErrLog, Timestamp};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EmbassyLogger {
|
||||||
|
log_level: log::LevelFilter,
|
||||||
|
log_epoch: Arc<AtomicU64>,
|
||||||
|
logger: StdErrLog,
|
||||||
|
sharing: Arc<AtomicBool>,
|
||||||
|
share_dest: Url,
|
||||||
|
}
|
||||||
|
impl EmbassyLogger {
|
||||||
|
pub fn new(
|
||||||
|
log_level: log::LevelFilter,
|
||||||
|
log_epoch: Arc<AtomicU64>,
|
||||||
|
share_dest: Option<Url>,
|
||||||
|
share_errors: bool,
|
||||||
|
) -> Self {
|
||||||
|
let share_dest = match share_dest {
|
||||||
|
None => Url::parse("https://beta-registry-0-3.start9labs.com/error-logs").unwrap(), // TODO
|
||||||
|
Some(a) => a,
|
||||||
|
};
|
||||||
|
let mut logger = stderrlog::new();
|
||||||
|
logger
|
||||||
|
.module(module_path!())
|
||||||
|
.timestamp(Timestamp::Millisecond);
|
||||||
|
match log_level {
|
||||||
|
LevelFilter::Off => logger.quiet(true),
|
||||||
|
LevelFilter::Error => logger.verbosity(0),
|
||||||
|
LevelFilter::Warn => logger.verbosity(1),
|
||||||
|
LevelFilter::Info => logger.verbosity(2),
|
||||||
|
LevelFilter::Debug => logger.verbosity(3),
|
||||||
|
LevelFilter::Trace => logger.verbosity(4),
|
||||||
|
};
|
||||||
|
let embassy_logger = EmbassyLogger {
|
||||||
|
log_level,
|
||||||
|
log_epoch,
|
||||||
|
logger,
|
||||||
|
sharing: Arc::new(AtomicBool::new(share_errors)),
|
||||||
|
share_dest: share_dest,
|
||||||
|
};
|
||||||
|
set_boxed_logger(Box::new(embassy_logger.clone())).unwrap();
|
||||||
|
embassy_logger
|
||||||
|
}
|
||||||
|
pub fn set_sharing(&self, sharing: bool) {
|
||||||
|
self.sharing.store(sharing, Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl log::Log for EmbassyLogger {
|
||||||
|
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||||
|
self.logger.enabled(metadata)
|
||||||
|
}
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
self.logger.log(record);
|
||||||
|
if self.sharing.load(Ordering::SeqCst) {
|
||||||
|
if record.level() <= log::Level::Warn {
|
||||||
|
let mut body = HashMap::new();
|
||||||
|
body.insert(
|
||||||
|
"log-epoch",
|
||||||
|
format!("{}", self.log_epoch.load(Ordering::SeqCst)),
|
||||||
|
);
|
||||||
|
body.insert("log-message", format!("{}", record.args()));
|
||||||
|
// we don't care about the result and need it to be fast
|
||||||
|
tokio::spawn(
|
||||||
|
Client::new()
|
||||||
|
.post(self.share_dest.clone())
|
||||||
|
.json(&body)
|
||||||
|
.send(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn order_level() {
|
||||||
|
assert!(log::Level::Warn > log::Level::Error)
|
||||||
|
}
|
||||||
@@ -21,6 +21,8 @@ use tokio::task::{JoinError, JoinHandle};
|
|||||||
use crate::shutdown::Shutdown;
|
use crate::shutdown::Shutdown;
|
||||||
use crate::{Error, ResultExt as _};
|
use crate::{Error, ResultExt as _};
|
||||||
|
|
||||||
|
pub mod logger;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Never {}
|
pub enum Never {}
|
||||||
impl Never {}
|
impl Never {}
|
||||||
|
|||||||
Reference in New Issue
Block a user