api and log refactor

This commit is contained in:
John Smith 2022-07-01 12:13:52 -04:00
parent f409c84778
commit c106d324c8
25 changed files with 662 additions and 501 deletions

1
Cargo.lock generated
View File

@ -5155,7 +5155,6 @@ dependencies = [
"ctrlc",
"daemonize",
"directories",
"failure",
"flume",
"futures-util",
"hostname",

View File

@ -3,6 +3,7 @@ use crate::tools::*;
use crate::veilid_client_capnp::*;
use capnp::capability::Promise;
use capnp_rpc::{pry, rpc_twoparty_capnp, twoparty, Disconnector, RpcSystem};
use serde::de::DeserializeOwned;
use std::cell::RefCell;
use std::net::SocketAddr;
use std::rc::Rc;
@ -28,6 +29,30 @@ macro_rules! pry_result {
};
}
fn map_to_internal_error<T: ToString>(e: T) -> VeilidAPIError {
VeilidAPIError::Internal {
message: e.to_string(),
}
}
fn decode_api_result<T: DeserializeOwned + fmt::Debug>(
reader: &api_result::Reader,
) -> Result<T, VeilidAPIError> {
match reader.which().map_err(map_to_internal_error)? {
api_result::Which::Ok(v) => {
let ok_val = v.map_err(map_to_internal_error)?;
let res: T = veilid_core::deserialize_json(ok_val).map_err(map_to_internal_error)?;
Ok(res)
}
api_result::Which::Err(e) => {
let err_val = e.map_err(map_to_internal_error)?;
let res: VeilidAPIError =
veilid_core::deserialize_json(err_val).map_err(map_to_internal_error)?;
Err(res)
}
}
}
struct VeilidClientImpl {
comproc: CommandProcessor,
}
@ -254,7 +279,13 @@ impl ClientApiConnection {
};
let request = server.borrow().attach_request();
let response = request.send().promise.await.map_err(map_to_string)?;
response.get().map(drop).map_err(map_to_string)
let reader = response
.get()
.map_err(map_to_string)?
.get_result()
.map_err(map_to_string)?;
let res: Result<(), VeilidAPIError> = decode_api_result(&reader);
res.map_err(map_to_string)
}
pub async fn server_detach(&mut self) -> Result<(), String> {
@ -269,7 +300,13 @@ impl ClientApiConnection {
};
let request = server.borrow().detach_request();
let response = request.send().promise.await.map_err(map_to_string)?;
response.get().map(drop).map_err(map_to_string)
let reader = response
.get()
.map_err(map_to_string)?
.get_result()
.map_err(map_to_string)?;
let res: Result<(), VeilidAPIError> = decode_api_result(&reader);
res.map_err(map_to_string)
}
pub async fn server_shutdown(&mut self) -> Result<(), String> {
@ -294,18 +331,47 @@ impl ClientApiConnection {
inner
.server
.as_ref()
.ok_or_else(|| "Not connected, ignoring attach request".to_owned())?
.ok_or_else(|| "Not connected, ignoring debug request".to_owned())?
.clone()
};
let mut request = server.borrow().debug_request();
request.get().set_what(&what);
request.get().set_command(&what);
let response = request.send().promise.await.map_err(map_to_string)?;
response
let reader = response
.get()
.map_err(map_to_string)?
.get_output()
.map(|o| o.to_owned())
.map_err(map_to_string)
.get_result()
.map_err(map_to_string)?;
let res: Result<String, VeilidAPIError> = decode_api_result(&reader);
res.map_err(map_to_string)
}
pub async fn server_change_log_level(
&mut self,
layer: String,
log_level: VeilidConfigLogLevel,
) -> Result<(), String> {
trace!("ClientApiConnection::change_log_level");
let server = {
let inner = self.inner.borrow();
inner
.server
.as_ref()
.ok_or_else(|| "Not connected, ignoring change_log_level request".to_owned())?
.clone()
};
let mut request = server.borrow().change_log_level_request();
request.get().set_layer(&layer);
let log_level_json = veilid_core::serialize_json(&log_level);
request.get().set_log_level(&log_level_json);
let response = request.send().promise.await.map_err(map_to_string)?;
let reader = response
.get()
.map_err(map_to_string)?
.get_result()
.map_err(map_to_string)?;
let res: Result<(), VeilidAPIError> = decode_api_result(&reader);
res.map_err(map_to_string)
}
// Start Client API connection

View File

@ -8,6 +8,19 @@ use std::net::SocketAddr;
use std::rc::Rc;
use std::time::{Duration, SystemTime};
use veilid_core::xx::{Eventual, EventualCommon};
use veilid_core::VeilidConfigLogLevel;
pub fn convert_loglevel(s: &str) -> Result<VeilidConfigLogLevel, String> {
match s.to_ascii_lowercase().as_str() {
"off" => Ok(VeilidConfigLogLevel::Off),
"error" => Ok(VeilidConfigLogLevel::Error),
"warn" => Ok(VeilidConfigLogLevel::Warn),
"info" => Ok(VeilidConfigLogLevel::Info),
"debug" => Ok(VeilidConfigLogLevel::Debug),
"trace" => Ok(VeilidConfigLogLevel::Trace),
_ => Err(format!("Invalid log level: {}", s)),
}
}
#[derive(PartialEq, Clone)]
pub enum ConnectionState {
@ -97,6 +110,7 @@ shutdown - shut the server down
attach - attach the server to the Veilid network
detach - detach the server from the Veilid network
debug - send a debugging command to the Veilid server
change_log_level - change the log level for a tracing layer
"#,
);
let ui = self.ui();
@ -131,7 +145,7 @@ debug - send a debugging command to the Veilid server
let ui = self.ui();
spawn_detached_local(async move {
if let Err(e) = capi.server_attach().await {
error!("Server command 'attach' failed to execute: {}", e);
error!("Server command 'attach' failed: {}", e);
}
ui.send_callback(callback);
});
@ -144,7 +158,7 @@ debug - send a debugging command to the Veilid server
let ui = self.ui();
spawn_detached_local(async move {
if let Err(e) = capi.server_detach().await {
error!("Server command 'detach' failed to execute: {}", e);
error!("Server command 'detach' failed: {}", e);
}
ui.send_callback(callback);
});
@ -170,7 +184,40 @@ debug - send a debugging command to the Veilid server
match capi.server_debug(rest.unwrap_or_default()).await {
Ok(output) => ui.display_string_dialog("Debug Output", output, callback),
Err(e) => {
error!("Server command 'debug' failed to execute: {}", e);
error!("Server command 'debug' failed: {}", e);
ui.send_callback(callback);
}
}
});
Ok(())
}
pub fn cmd_change_log_level(
&self,
rest: Option<String>,
callback: UICallback,
) -> Result<(), String> {
trace!("CommandProcessor::cmd_change_log_level");
let mut capi = self.capi();
let ui = self.ui();
spawn_detached_local(async move {
let (layer, rest) = Self::word_split(&rest.unwrap_or_default());
let log_level = match convert_loglevel(&rest.unwrap_or_default()) {
Ok(v) => v,
Err(e) => {
error!("failed to change log level: {}", e);
ui.send_callback(callback);
return;
}
};
match capi.server_change_log_level(layer, log_level).await {
Ok(()) => {
info!("Log level changed");
ui.send_callback(callback);
}
Err(e) => {
error!("Server command 'change_log_level' failed: {}", e);
ui.send_callback(callback);
}
}
@ -190,6 +237,7 @@ debug - send a debugging command to the Veilid server
"attach" => self.cmd_attach(callback),
"detach" => self.cmd_detach(callback),
"debug" => self.cmd_debug(rest, callback),
"change_log_level" => self.cmd_change_log_level(rest, callback),
_ => {
let ui = self.ui();
ui.send_callback(callback);

View File

@ -10,14 +10,10 @@ cfg_if! {
use send_wrapper::*;
struct ApiLoggerInner {
max_level: Option<VeilidLogLevel>,
filter_ignore: Cow<'static, [Cow<'static, str>]>,
update_callback: SendWrapper<UpdateCallback>,
}
} else {
struct ApiLoggerInner {
max_level: Option<VeilidLogLevel>,
filter_ignore: Cow<'static, [Cow<'static, str>]>,
update_callback: UpdateCallback,
}
}
@ -31,21 +27,14 @@ pub struct ApiTracingLayer {
static API_LOGGER: OnceCell<ApiTracingLayer> = OnceCell::new();
impl ApiTracingLayer {
fn new_inner(
max_level: Option<VeilidLogLevel>,
update_callback: UpdateCallback,
) -> ApiLoggerInner {
fn new_inner(update_callback: UpdateCallback) -> ApiLoggerInner {
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
ApiLoggerInner {
max_level,
filter_ignore: Default::default(),
update_callback: SendWrapper::new(update_callback),
}
} else {
ApiLoggerInner {
max_level,
filter_ignore: Default::default(),
update_callback,
}
}
@ -53,11 +42,11 @@ impl ApiTracingLayer {
}
#[instrument(level = "debug", skip(update_callback))]
pub async fn init(max_level: Option<VeilidLogLevel>, update_callback: UpdateCallback) {
pub async fn init(update_callback: UpdateCallback) {
let api_logger = API_LOGGER.get_or_init(|| ApiTracingLayer {
inner: Arc::new(Mutex::new(None)),
});
let apilogger_inner = Some(Self::new_inner(max_level, update_callback));
let apilogger_inner = Some(Self::new_inner(update_callback));
*api_logger.inner.lock() = apilogger_inner;
}
@ -76,52 +65,9 @@ impl ApiTracingLayer {
})
.clone()
}
#[instrument(level = "trace")]
pub fn change_api_log_level(max_level: Option<VeilidLogLevel>) {
if let Some(api_logger) = API_LOGGER.get() {
if let Some(inner) = &mut *api_logger.inner.lock() {
inner.max_level = max_level;
}
}
}
pub fn add_filter_ignore_str(filter_ignore: &'static str) {
if let Some(api_logger) = API_LOGGER.get() {
if let Some(inner) = &mut *api_logger.inner.lock() {
let mut list = Vec::from(&*inner.filter_ignore);
list.push(Cow::Borrowed(filter_ignore));
inner.filter_ignore = Cow::Owned(list);
}
}
}
}
impl<S: Subscriber + for<'a> registry::LookupSpan<'a>> Layer<S> for ApiTracingLayer {
fn enabled(&self, metadata: &tracing::Metadata<'_>, _: layer::Context<'_, S>) -> bool {
if let Some(inner) = &mut *self.inner.lock() {
// Skip things that are out of our level
if let Some(max_level) = inner.max_level {
if VeilidLogLevel::from_tracing_level(*metadata.level()) > max_level {
return false;
}
} else {
return false;
}
// Skip filtered targets
let skip = match (metadata.target(), &*inner.filter_ignore) {
(path, ignore) if !ignore.is_empty() => {
// Check that the module path does not match any ignore filters
ignore.iter().any(|v| path.starts_with(&**v))
}
_ => false,
};
!skip
} else {
false
}
}
fn on_new_span(
&self,
attrs: &tracing::span::Attributes<'_>,
@ -161,23 +107,16 @@ impl<S: Subscriber + for<'a> registry::LookupSpan<'a>> Layer<S> for ApiTracingLa
event.record(&mut recorder);
let meta = event.metadata();
let level = meta.level();
if let Some(max_level) = inner.max_level {
if VeilidLogLevel::from_tracing_level(*level) <= max_level {
let log_level = VeilidLogLevel::from_tracing_level(*level);
let log_level = VeilidLogLevel::from_tracing_level(*level);
let origin = meta
.file()
.and_then(|file| meta.line().map(|ln| format!("{}:{}", file, ln)))
.unwrap_or_default();
let origin = meta
.file()
.and_then(|file| meta.line().map(|ln| format!("{}:{}", file, ln)))
.unwrap_or_default();
let message = format!("{} {}", origin, recorder);
let message = format!("{} {}", origin, recorder);
(inner.update_callback)(VeilidUpdate::Log(VeilidStateLog {
log_level,
message,
}))
}
}
(inner.update_callback)(VeilidUpdate::Log(VeilidStateLog { log_level, message }))
}
}
}
@ -232,64 +171,3 @@ impl core::default::Default for StringRecorder {
StringRecorder::new()
}
}
impl log::Log for ApiTracingLayer {
fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
if let Some(inner) = &mut *self.inner.lock() {
if let Some(max_level) = inner.max_level {
return VeilidLogLevel::from_log_level(metadata.level()) <= max_level;
}
}
false
}
fn log(&self, record: &log::Record<'_>) {
if let Some(inner) = &mut *self.inner.lock() {
// Skip filtered targets
let skip = match (record.target(), &*inner.filter_ignore) {
(path, ignore) if !ignore.is_empty() => {
// Check that the module path does not match any ignore filters
ignore.iter().any(|v| path.starts_with(&**v))
}
_ => false,
};
if skip {
return;
}
let metadata = record.metadata();
let level = metadata.level();
let log_level = VeilidLogLevel::from_log_level(level);
if let Some(max_level) = inner.max_level {
if log_level <= max_level {
let file = record.file().unwrap_or("<unknown>");
let loc = if level >= log::Level::Debug {
if let Some(line) = record.line() {
format!("[{}:{}] ", file, line)
} else {
format!("[{}:<unknown>] ", file)
}
} else {
"".to_owned()
};
let tgt = if record.target().is_empty() {
"".to_owned()
} else {
format!("{}: ", record.target())
};
let message = format!("{}{}{}", tgt, loc, record.args());
(inner.update_callback)(VeilidUpdate::Log(VeilidStateLog {
log_level,
message,
}))
}
}
}
}
fn flush(&self) {
// always flushes
}
}

View File

@ -59,21 +59,11 @@ impl ServicesContext {
#[instrument(err, skip_all)]
pub async fn startup(&mut self) -> Result<(), VeilidAPIError> {
let api_log_level: VeilidConfigLogLevel = self.config.get().api_log_level;
if api_log_level != VeilidConfigLogLevel::Off {
ApiTracingLayer::init(
api_log_level.to_veilid_log_level(),
self.update_callback.clone(),
)
.await;
for ig in crate::DEFAULT_LOG_IGNORE_LIST {
ApiTracingLayer::add_filter_ignore_str(ig);
}
info!("Veilid API logging initialized");
}
info!("Veilid API starting up");
info!("init api tracing");
ApiTracingLayer::init(self.update_callback.clone()).await;
// Set up protected store
trace!("init protected store");
let protected_store = ProtectedStore::new(self.config.clone());

View File

@ -30,6 +30,7 @@ mod routing_table;
mod rpc_processor;
mod veilid_api;
mod veilid_config;
mod veilid_layer_filter;
mod veilid_rng;
#[macro_use]
@ -40,6 +41,7 @@ pub use self::attachment_manager::AttachmentState;
pub use self::core_context::{api_startup, api_startup_json, UpdateCallback};
pub use self::veilid_api::*;
pub use self::veilid_config::*;
pub use self::veilid_layer_filter::*;
pub mod veilid_capnp {
include!(concat!(env!("OUT_DIR"), "/proto/veilid_capnp.rs"));

View File

@ -169,7 +169,6 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
match key.as_str() {
"program_name" => Ok(Box::new(String::from("Veilid"))),
"namespace" => Ok(Box::new(String::from(""))),
"api_log_level" => Ok(Box::new(VeilidConfigLogLevel::Off)),
"capabilities.protocol_udp" => Ok(Box::new(true)),
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)),
"capabilities.protocol_accept_tcp" => Ok(Box::new(true)),

View File

@ -1861,12 +1861,6 @@ impl VeilidAPI {
.map_err(|e| VeilidAPIError::Internal { message: e })
}
// Change api logging level if it is enabled
#[instrument(skip(self))]
pub async fn change_api_log_level(&self, log_level: VeilidConfigLogLevel) {
ApiTracingLayer::change_api_log_level(log_level.to_veilid_log_level());
}
////////////////////////////////////////////////////////////////
// Direct Node Access (pretty much for testing only)

View File

@ -223,6 +223,16 @@ impl VeilidConfigLogLevel {
Some(VeilidLogLevel::Trace) => Self::Trace,
}
}
pub fn from_tracing_level_filter(level: level_filters::LevelFilter) -> Self {
match level {
level_filters::LevelFilter::OFF => Self::Off,
level_filters::LevelFilter::ERROR => Self::Error,
level_filters::LevelFilter::WARN => Self::Warn,
level_filters::LevelFilter::INFO => Self::Info,
level_filters::LevelFilter::DEBUG => Self::Debug,
level_filters::LevelFilter::TRACE => Self::Trace,
}
}
}
impl Default for VeilidConfigLogLevel {
fn default() -> Self {

View File

@ -0,0 +1,91 @@
use super::*;
use crate::xx::*;
use tracing::level_filters::LevelFilter;
use tracing::subscriber;
use tracing_subscriber::layer;
struct VeilidLayerFilterInner {
max_level: LevelFilter,
ignore_list: Vec<String>,
}
#[derive(Clone)]
pub struct VeilidLayerFilter {
inner: Arc<RwLock<VeilidLayerFilterInner>>,
}
impl VeilidLayerFilter {
pub fn new(
max_level: VeilidConfigLogLevel,
ignore_list: Option<Vec<String>>,
) -> VeilidLayerFilter {
Self {
inner: Arc::new(RwLock::new(VeilidLayerFilterInner {
max_level: max_level.to_tracing_level_filter(),
ignore_list: ignore_list
.unwrap_or_else(|| DEFAULT_LOG_IGNORE_LIST.map(|x| x.to_owned()).to_vec()),
})),
}
}
pub fn max_level(&self) -> VeilidConfigLogLevel {
let inner = self.inner.read();
VeilidConfigLogLevel::from_tracing_level_filter(inner.max_level)
}
pub fn ignore_list(&self) -> Vec<String> {
let inner = self.inner.read();
inner.ignore_list.clone()
}
pub fn set_max_level(&self, level: VeilidConfigLogLevel) {
{
let mut inner = self.inner.write();
inner.max_level = level.to_tracing_level_filter();
}
callsite::rebuild_interest_cache();
}
pub fn set_ignore_list(&self, ignore_list: Option<Vec<String>>) {
{
let mut inner = self.inner.write();
inner.ignore_list = ignore_list
.unwrap_or_else(|| DEFAULT_LOG_IGNORE_LIST.map(|x| x.to_owned()).to_vec());
}
callsite::rebuild_interest_cache();
}
}
impl<S: tracing::Subscriber> layer::Filter<S> for VeilidLayerFilter {
fn enabled(&self, metadata: &tracing::Metadata<'_>, _: &layer::Context<'_, S>) -> bool {
let inner = self.inner.read();
if *metadata.level() > inner.max_level {
false
} else {
true
}
}
fn callsite_enabled(
&self,
metadata: &'static tracing::Metadata<'static>,
) -> subscriber::Interest {
let inner = self.inner.read();
let skip = inner
.ignore_list
.iter()
.any(|v| metadata.target().starts_with(&**v));
if skip {
subscriber::Interest::never()
} else if *metadata.level() > inner.max_level {
subscriber::Interest::never()
} else {
subscriber::Interest::always()
}
}
fn max_level_hint(&self) -> Option<LevelFilter> {
let inner = self.inner.read();
Some(inner.max_level)
}
}

View File

@ -7,7 +7,6 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
return VeilidConfig(
programName: "Veilid Plugin Test",
namespace: "",
apiLogLevel: VeilidConfigLogLevel.info,
capabilities: VeilidConfigCapabilities(
protocolUDP: !kIsWeb,
protocolConnectTCP: !kIsWeb,

View File

@ -59,7 +59,7 @@ VeilidConfigLogLevel convertToVeilidConfigLogLevel(LogLevel? level) {
void setRootLogLevel(LogLevel? level) {
Loggy('').level = getLogOptions(level);
Veilid.instance.changeApiLogLevel(convertToVeilidConfigLogLevel(level));
Veilid.instance.changeLogLevel("all", convertToVeilidConfigLogLevel(level));
}
void initLoggy() {
@ -81,22 +81,26 @@ void main() {
logging: VeilidWASMConfigLogging(
performance: VeilidWASMConfigLoggingPerformance(
enabled: true,
level: VeilidLogLevel.trace,
level: VeilidConfigLogLevel.debug,
logsInTimings: true,
logsInConsole: false)));
logsInConsole: true),
api: VeilidWASMConfigLoggingApi(
enabled: true, level: VeilidConfigLogLevel.info)));
Veilid.instance.configureVeilidPlatform(platformConfig.json);
} else {
var platformConfig = VeilidFFIConfig(
logging: VeilidFFIConfigLogging(
terminal: VeilidFFIConfigLoggingTerminal(
enabled: false,
level: VeilidLogLevel.trace,
level: VeilidConfigLogLevel.debug,
),
otlp: VeilidFFIConfigLoggingOtlp(
enabled: false,
level: VeilidLogLevel.trace,
level: VeilidConfigLogLevel.trace,
grpcEndpoint: "localhost:4317",
serviceName: "VeilidExample")));
serviceName: "VeilidExample"),
api: VeilidFFIConfigLoggingApi(
enabled: true, level: VeilidConfigLogLevel.info)));
Veilid.instance.configureVeilidPlatform(platformConfig.json);
}

View File

@ -13,7 +13,7 @@ import 'veilid_stub.dart'
class VeilidFFIConfigLoggingTerminal {
bool enabled;
VeilidLogLevel level;
VeilidConfigLogLevel level;
VeilidFFIConfigLoggingTerminal({
required this.enabled,
@ -29,12 +29,12 @@ class VeilidFFIConfigLoggingTerminal {
VeilidFFIConfigLoggingTerminal.fromJson(Map<String, dynamic> json)
: enabled = json['enabled'],
level = veilidLogLevelFromJson(json['level']);
level = veilidConfigLogLevelFromJson(json['level']);
}
class VeilidFFIConfigLoggingOtlp {
bool enabled;
VeilidLogLevel level;
VeilidConfigLogLevel level;
String grpcEndpoint;
String serviceName;
@ -56,27 +56,52 @@ class VeilidFFIConfigLoggingOtlp {
VeilidFFIConfigLoggingOtlp.fromJson(Map<String, dynamic> json)
: enabled = json['enabled'],
level = veilidLogLevelFromJson(json['level']),
level = veilidConfigLogLevelFromJson(json['level']),
grpcEndpoint = json['grpc_endpoint'],
serviceName = json['service_name'];
}
class VeilidFFIConfigLoggingApi {
bool enabled;
VeilidConfigLogLevel level;
VeilidFFIConfigLoggingApi({
required this.enabled,
required this.level,
});
Map<String, dynamic> get json {
return {
'enabled': enabled,
'level': level.json,
};
}
VeilidFFIConfigLoggingApi.fromJson(Map<String, dynamic> json)
: enabled = json['enabled'],
level = veilidConfigLogLevelFromJson(json['level']);
}
class VeilidFFIConfigLogging {
VeilidFFIConfigLoggingTerminal terminal;
VeilidFFIConfigLoggingOtlp otlp;
VeilidFFIConfigLoggingApi api;
VeilidFFIConfigLogging({required this.terminal, required this.otlp});
VeilidFFIConfigLogging(
{required this.terminal, required this.otlp, required this.api});
Map<String, dynamic> get json {
return {
'terminal': terminal.json,
'otlp': otlp.json,
'api': api.json,
};
}
VeilidFFIConfigLogging.fromJson(Map<String, dynamic> json)
: terminal = VeilidFFIConfigLoggingTerminal.fromJson(json['terminal']),
otlp = VeilidFFIConfigLoggingOtlp.fromJson(json['otlp']);
otlp = VeilidFFIConfigLoggingOtlp.fromJson(json['otlp']),
api = VeilidFFIConfigLoggingApi.fromJson(json['api']);
}
class VeilidFFIConfig {
@ -101,7 +126,7 @@ class VeilidFFIConfig {
class VeilidWASMConfigLoggingPerformance {
bool enabled;
VeilidLogLevel level;
VeilidConfigLogLevel level;
bool logsInTimings;
bool logsInConsole;
@ -123,25 +148,49 @@ class VeilidWASMConfigLoggingPerformance {
VeilidWASMConfigLoggingPerformance.fromJson(Map<String, dynamic> json)
: enabled = json['enabled'],
level = veilidLogLevelFromJson(json['level']),
level = veilidConfigLogLevelFromJson(json['level']),
logsInTimings = json['logs_in_timings'],
logsInConsole = json['logs_in_console'];
}
class VeilidWASMConfigLoggingApi {
bool enabled;
VeilidConfigLogLevel level;
VeilidWASMConfigLoggingApi({
required this.enabled,
required this.level,
});
Map<String, dynamic> get json {
return {
'enabled': enabled,
'level': level.json,
};
}
VeilidWASMConfigLoggingApi.fromJson(Map<String, dynamic> json)
: enabled = json['enabled'],
level = veilidConfigLogLevelFromJson(json['level']);
}
class VeilidWASMConfigLogging {
VeilidWASMConfigLoggingPerformance performance;
VeilidWASMConfigLoggingApi api;
VeilidWASMConfigLogging({required this.performance});
VeilidWASMConfigLogging({required this.performance, required this.api});
Map<String, dynamic> get json {
return {
'performance': performance.json,
'api': api.json,
};
}
VeilidWASMConfigLogging.fromJson(Map<String, dynamic> json)
: performance =
VeilidWASMConfigLoggingPerformance.fromJson(json['performance']);
VeilidWASMConfigLoggingPerformance.fromJson(json['performance']),
api = VeilidWASMConfigLoggingApi.fromJson(json['api']);
}
class VeilidWASMConfig {
@ -899,7 +948,6 @@ class VeilidConfigCapabilities {
class VeilidConfig {
String programName;
String namespace;
VeilidConfigLogLevel apiLogLevel;
VeilidConfigCapabilities capabilities;
VeilidConfigProtectedStore protectedStore;
VeilidConfigTableStore tableStore;
@ -909,7 +957,6 @@ class VeilidConfig {
VeilidConfig({
required this.programName,
required this.namespace,
required this.apiLogLevel,
required this.capabilities,
required this.protectedStore,
required this.tableStore,
@ -921,7 +968,6 @@ class VeilidConfig {
return {
'program_name': programName,
'namespace': namespace,
'api_log_level': apiLogLevel.json,
'capabilities': capabilities.json,
'protected_store': protectedStore.json,
'table_store': tableStore.json,
@ -933,7 +979,6 @@ class VeilidConfig {
VeilidConfig.fromJson(Map<String, dynamic> json)
: programName = json['program_name'],
namespace = json['namespace'],
apiLogLevel = json['api_log_level'],
capabilities = VeilidConfigCapabilities.fromJson(json['capabilities']),
protectedStore =
VeilidConfigProtectedStore.fromJson(json['protected_store']),
@ -1253,9 +1298,9 @@ abstract class Veilid {
static late Veilid instance = getVeilid();
void configureVeilidPlatform(Map<String, dynamic> platformConfigJson);
void changeLogLevel(String layer, VeilidConfigLogLevel logLevel);
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config);
Future<VeilidState> getVeilidState();
Future<void> changeApiLogLevel(VeilidConfigLogLevel logLevel);
Future<void> shutdownVeilidCore();
Future<String> debug(String command);
String veilidVersionString();

View File

@ -32,15 +32,15 @@ typedef _InitializeVeilidFlutterDart = void Function(Pointer<_DartPostCObject>);
// fn configure_veilid_platform(platform_config: FfiStr)
typedef _ConfigureVeilidPlatformC = Void Function(Pointer<Utf8>);
typedef _ConfigureVeilidPlatformDart = void Function(Pointer<Utf8>);
// fn change_log_level(layer: FfiStr, log_level: FfiStr)
typedef _ChangeLogLevelC = Void Function(Pointer<Utf8>, Pointer<Utf8>);
typedef _ChangeLogLevelDart = void Function(Pointer<Utf8>, Pointer<Utf8>);
// fn startup_veilid_core(port: i64, config: FfiStr)
typedef _StartupVeilidCoreC = Void Function(Int64, Pointer<Utf8>);
typedef _StartupVeilidCoreDart = void Function(int, Pointer<Utf8>);
// fn get_veilid_state(port: i64)
typedef _GetVeilidStateC = Void Function(Int64);
typedef _GetVeilidStateDart = void Function(int);
// fn change_api_log_level(port: i64, log_level: FfiStr)
typedef _ChangeApiLogLevelC = Void Function(Int64, Pointer<Utf8>);
typedef _ChangeApiLogLevelDart = void Function(int, Pointer<Utf8>);
// fn debug(port: i64, log_level: FfiStr)
typedef _DebugC = Void Function(Int64, Pointer<Utf8>);
typedef _DebugDart = void Function(int, Pointer<Utf8>);
@ -246,9 +246,9 @@ class VeilidFFI implements Veilid {
// Shared library functions
final _FreeStringDart _freeString;
final _ConfigureVeilidPlatformDart _configureVeilidPlatform;
final _ChangeLogLevelDart _changeLogLevel;
final _StartupVeilidCoreDart _startupVeilidCore;
final _GetVeilidStateDart _getVeilidState;
final _ChangeApiLogLevelDart _changeApiLogLevel;
final _ShutdownVeilidCoreDart _shutdownVeilidCore;
final _DebugDart _debug;
final _VeilidVersionStringDart _veilidVersionString;
@ -261,15 +261,15 @@ class VeilidFFI implements Veilid {
_configureVeilidPlatform = dylib.lookupFunction<
_ConfigureVeilidPlatformC,
_ConfigureVeilidPlatformDart>('configure_veilid_platform'),
_changeLogLevel =
dylib.lookupFunction<_ChangeLogLevelC, _ChangeLogLevelDart>(
'change_log_level'),
_startupVeilidCore =
dylib.lookupFunction<_StartupVeilidCoreC, _StartupVeilidCoreDart>(
'startup_veilid_core'),
_getVeilidState =
dylib.lookupFunction<_GetVeilidStateC, _GetVeilidStateDart>(
'get_veilid_state'),
_changeApiLogLevel =
dylib.lookupFunction<_ChangeApiLogLevelC, _ChangeApiLogLevelDart>(
'change_api_log_level'),
_shutdownVeilidCore =
dylib.lookupFunction<_ShutdownVeilidCoreC, _ShutdownVeilidCoreDart>(
'shutdown_veilid_core'),
@ -297,6 +297,17 @@ class VeilidFFI implements Veilid {
malloc.free(nativePlatformConfig);
}
@override
void changeLogLevel(String layer, VeilidConfigLogLevel logLevel) {
var nativeLogLevel =
jsonEncode(logLevel.json, toEncodable: veilidApiToEncodable)
.toNativeUtf8();
var nativeLayer = layer.toNativeUtf8();
_changeLogLevel(nativeLayer, nativeLogLevel);
malloc.free(nativeLayer);
malloc.free(nativeLogLevel);
}
@override
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config) {
var nativeConfig =
@ -317,18 +328,6 @@ class VeilidFFI implements Veilid {
return processFutureJson(VeilidState.fromJson, recvPort.first);
}
@override
Future<void> changeApiLogLevel(VeilidConfigLogLevel logLevel) async {
var nativeLogLevel =
jsonEncode(logLevel.json, toEncodable: veilidApiToEncodable)
.toNativeUtf8();
final recvPort = ReceivePort("change_api_log_level");
final sendPort = recvPort.sendPort;
_changeApiLogLevel(sendPort.nativePort, nativeLogLevel);
malloc.free(nativeLogLevel);
return processFutureVoid(recvPort.first);
}
@override
Future<void> shutdownVeilidCore() async {
final recvPort = ReceivePort("shutdown_veilid_core");

View File

@ -27,6 +27,13 @@ class VeilidJS implements Veilid {
wasm, "configure_veilid_platform", [platformConfigJsonString]);
}
@override
void changeLogLevel(String layer, VeilidConfigLogLevel logLevel) {
var logLevelJsonString =
jsonEncode(logLevel.json, toEncodable: veilidApiToEncodable);
js_util.callMethod(wasm, "change_log_level", [layer, logLevelJsonString]);
}
@override
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config) async* {
var streamController = StreamController<VeilidUpdate>();
@ -53,12 +60,6 @@ class VeilidJS implements Veilid {
js_util.callMethod(wasm, "get_veilid_state", []))));
}
@override
Future<void> changeApiLogLevel(VeilidConfigLogLevel logLevel) {
return _wrapApiPromise(js_util.callMethod(wasm, "change_api_log_level",
[jsonEncode(logLevel.json, toEncodable: veilidApiToEncodable)]));
}
@override
Future<void> shutdownVeilidCore() {
return _wrapApiPromise(

View File

@ -7,7 +7,9 @@ use lazy_static::*;
use opentelemetry::sdk::*;
use opentelemetry::*;
use opentelemetry_otlp::WithExportConfig;
use parking_lot::Mutex;
use serde::*;
use std::collections::BTreeMap;
use std::os::raw::c_char;
use std::sync::Arc;
use tracing::*;
@ -15,9 +17,10 @@ use tracing_subscriber::prelude::*;
use tracing_subscriber::*;
// Globals
lazy_static! {
static ref VEILID_API: AsyncMutex<Option<veilid_core::VeilidAPI>> = AsyncMutex::new(None);
static ref FILTERS: Mutex<BTreeMap<&'static str, veilid_core::VeilidLayerFilter>> =
Mutex::new(BTreeMap::new());
}
async fn get_veilid_api() -> Result<veilid_core::VeilidAPI, veilid_core::VeilidAPIError> {
@ -35,17 +38,6 @@ async fn take_veilid_api() -> Result<veilid_core::VeilidAPI, veilid_core::Veilid
.ok_or(veilid_core::VeilidAPIError::NotInitialized)
}
fn logfilter<T: AsRef<str>, V: AsRef<[T]>>(metadata: &Metadata, ignore_list: V) -> bool {
// Skip filtered targets
!match (metadata.target(), ignore_list.as_ref()) {
(path, ignore) if !ignore.is_empty() => {
// Check that the module path does not match any ignore filters
ignore.iter().any(|v| path.starts_with(v.as_ref()))
}
_ => false,
}
}
/////////////////////////////////////////
// FFI Helpers
@ -75,21 +67,28 @@ macro_rules! check_err_json {
#[derive(Debug, Deserialize, Serialize)]
pub struct VeilidFFIConfigLoggingTerminal {
pub enabled: bool,
pub level: veilid_core::VeilidLogLevel,
pub level: veilid_core::VeilidConfigLogLevel,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct VeilidFFIConfigLoggingOtlp {
pub enabled: bool,
pub level: veilid_core::VeilidLogLevel,
pub level: veilid_core::VeilidConfigLogLevel,
pub grpc_endpoint: String,
pub service_name: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct VeilidFFIConfigLoggingApi {
pub enabled: bool,
pub level: veilid_core::VeilidConfigLogLevel,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct VeilidFFIConfigLogging {
pub terminal: VeilidFFIConfigLoggingTerminal,
pub otlp: VeilidFFIConfigLoggingOtlp,
pub api: VeilidFFIConfigLoggingApi,
}
#[derive(Debug, Deserialize, Serialize)]
@ -149,40 +148,24 @@ pub extern "C" fn configure_veilid_platform(platform_config: FfiStr) {
.expect("failed to deserialize plaform config json");
// Set up subscriber and layers
let mut ignore_list = Vec::<String>::new();
for ig in veilid_core::DEFAULT_LOG_IGNORE_LIST {
ignore_list.push(ig.to_owned());
}
let subscriber = Registry::default();
let mut layers = Vec::new();
let mut filters = (*FILTERS).lock();
// Terminal logger
let subscriber = subscriber.with(if platform_config.logging.terminal.enabled {
let terminal_max_log_level: level_filters::LevelFilter = platform_config
.logging
.terminal
.level
.to_tracing_level()
.into();
let ignore_list = ignore_list.clone();
Some(
fmt::Layer::new()
.compact()
.with_writer(std::io::stdout)
.with_filter(terminal_max_log_level)
.with_filter(filter::FilterFn::new(move |metadata| {
logfilter(metadata, &ignore_list)
})),
)
} else {
None
});
if platform_config.logging.terminal.enabled {
let filter =
veilid_core::VeilidLayerFilter::new(platform_config.logging.terminal.level, None);
let layer = fmt::Layer::new()
.compact()
.with_writer(std::io::stdout)
.with_filter(filter.clone());
filters.insert("terminal", filter);
layers.push(layer.boxed());
};
// OpenTelemetry logger
let subscriber = subscriber.with(if platform_config.logging.otlp.enabled {
let otlp_max_log_level: level_filters::LevelFilter =
platform_config.logging.otlp.level.to_tracing_level().into();
if platform_config.logging.otlp.enabled {
let grpc_endpoint = platform_config.logging.otlp.grpc_endpoint.clone();
cfg_if! {
@ -218,21 +201,23 @@ pub extern "C" fn configure_veilid_platform(platform_config: FfiStr) {
.map_err(|e| format!("failed to install OpenTelemetry tracer: {}", e))
.unwrap();
let ignore_list = ignore_list.clone();
Some(
tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_filter(otlp_max_log_level)
.with_filter(filter::FilterFn::new(move |metadata| {
logfilter(metadata, &ignore_list)
})),
)
} else {
None
});
let filter = veilid_core::VeilidLayerFilter::new(platform_config.logging.otlp.level, None);
let layer = tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_filter(filter.clone());
filters.insert("otlp", filter);
layers.push(layer.boxed());
}
// API logger (always add layer, startup will init this if it is enabled in settings)
let subscriber = subscriber.with(veilid_core::ApiTracingLayer::get());
// API logger
if platform_config.logging.api.enabled {
let filter = veilid_core::VeilidLayerFilter::new(platform_config.logging.api.level, None);
let layer = veilid_core::ApiTracingLayer::get().with_filter(filter.clone());
filters.insert("api", filter);
layers.push(layer.boxed());
}
let subscriber = subscriber.with(layers);
subscriber
.try_init()
@ -240,6 +225,32 @@ pub extern "C" fn configure_veilid_platform(platform_config: FfiStr) {
.expect("failed to initalize ffi platform");
}
#[no_mangle]
#[instrument(level = "debug")]
pub extern "C" fn change_log_level(layer: FfiStr, log_level: FfiStr) {
// get layer to change level on
let layer = layer.into_opt_string().unwrap_or("all".to_owned());
let layer = if layer == "all" { "".to_owned() } else { layer };
// get log level to change layer to
let log_level = log_level.into_opt_string();
let log_level: veilid_core::VeilidConfigLogLevel =
veilid_core::deserialize_opt_json(log_level).unwrap();
// change log level on appropriate layer
let filters = (*FILTERS).lock();
if layer.is_empty() {
// Change all layers
for f in filters.values() {
f.set_max_level(log_level);
}
} else {
// Change a specific layer
let f = filters.get(layer.as_str()).unwrap();
f.set_max_level(log_level);
}
}
#[no_mangle]
#[instrument]
pub extern "C" fn startup_veilid_core(port: i64, config: FfiStr) {
@ -291,20 +302,6 @@ pub extern "C" fn get_veilid_state(port: i64) {
});
}
#[no_mangle]
#[instrument(level = "debug")]
pub extern "C" fn change_api_log_level(port: i64, log_level: FfiStr) {
let log_level = log_level.into_opt_string();
DartIsolateWrapper::new(port).spawn_result_json(async move {
let log_level: veilid_core::VeilidConfigLogLevel =
veilid_core::deserialize_opt_json(log_level)?;
//let veilid_api = get_veilid_api().await?;
//veilid_api.change_api_log_level(log_level).await;
veilid_core::ApiTracingLayer::change_api_log_level(log_level.to_veilid_log_level());
APIRESULT_VOID
});
}
#[no_mangle]
#[instrument]
pub extern "C" fn shutdown_veilid_core(port: i64) {

View File

@ -36,7 +36,6 @@ capnp = "^0"
parking_lot = "^0"
capnp-rpc = "^0"
config = { version = "^0", features = ["yaml"] }
failure = "^0"
cfg-if = "^1"
serde = "^1"
serde_derive = "^1"

View File

@ -1,20 +1,24 @@
@0xd29582d26b2fb073;
struct ApiResult {
union {
ok @0 :Text;
err @1 :Text;
}
}
interface Registration {}
interface VeilidServer {
register @0 (veilidClient: VeilidClient) -> (registration: Registration, state: Text);
debug @1 (what: Text) -> (output: Text);
attach @2 ();
detach @3 ();
debug @1 (command: Text) -> (result :ApiResult);
attach @2 () -> (result :ApiResult);
detach @3 () -> (result :ApiResult);
shutdown @4 ();
getState @5 () -> (state: Text);
getState @5 () -> (result :ApiResult);
changeLogLevel @6 (layer: Text, logLevel: Text) -> (result :ApiResult);
}
interface VeilidClient {
update @0 (veilidUpdate: Text);
}

View File

@ -1,12 +1,14 @@
use crate::tools::*;
use crate::veilid_client_capnp::*;
use crate::veilid_logs::VeilidLogs;
use capnp::capability::Promise;
use capnp_rpc::{pry, rpc_twoparty_capnp, twoparty, RpcSystem};
use cfg_if::*;
use failure::*;
use futures_util::{future::try_join_all, FutureExt as FuturesFutureExt, StreamExt};
use serde::*;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::net::SocketAddr;
use std::rc::Rc;
use stop_token::future::FutureExt;
@ -14,9 +16,20 @@ use stop_token::*;
use tracing::*;
use veilid_core::*;
#[derive(Fail, Debug)]
#[fail(display = "Client API error: {}", _0)]
pub struct ClientAPIError(String);
// Encoding for ApiResult
fn encode_api_result<T: Serialize + fmt::Debug>(
result: &Result<T, VeilidAPIError>,
builder: &mut api_result::Builder,
) {
match result {
Ok(v) => {
builder.set_ok(&serialize_json(v));
}
Err(e) => {
builder.set_err(&serialize_json(e));
}
}
}
// --- interface Registration ---------------------------------
@ -67,17 +80,19 @@ impl registration::Server for RegistrationImpl {}
struct VeilidServerImpl {
veilid_api: veilid_core::VeilidAPI,
veilid_logs: VeilidLogs,
next_id: u64,
pub registration_map: Rc<RefCell<RegistrationMap>>,
}
impl VeilidServerImpl {
#[instrument(level = "trace", skip_all)]
pub fn new(veilid_api: veilid_core::VeilidAPI) -> Self {
pub fn new(veilid_api: veilid_core::VeilidAPI, veilid_logs: VeilidLogs) -> Self {
Self {
next_id: 0,
registration_map: Rc::new(RefCell::new(RegistrationMap::new())),
veilid_api,
veilid_logs,
}
}
}
@ -115,13 +130,7 @@ impl veilid_server::Server for VeilidServerImpl {
let mut res = results.get();
res.set_registration(registration);
let mut rpc_state = res.init_state(
state
.len()
.try_into()
.map_err(|e| ::capnp::Error::failed(format!("{:?}", e)))?,
);
rpc_state.push_str(&state);
res.set_state(&state);
Ok(())
})
@ -135,14 +144,11 @@ impl veilid_server::Server for VeilidServerImpl {
) -> Promise<(), ::capnp::Error> {
trace!("VeilidServerImpl::debug");
let veilid_api = self.veilid_api.clone();
let what = pry!(pry!(params.get()).get_what()).to_owned();
let command = pry!(pry!(params.get()).get_command()).to_owned();
Promise::from_future(async move {
let output = veilid_api
.debug(what)
.await
.map_err(|e| ::capnp::Error::failed(format!("{:?}", e)))?;
results.get().set_output(output.as_str());
let result = veilid_api.debug(command).await;
encode_api_result(&result, &mut results.get().init_result());
Ok(())
})
}
@ -151,15 +157,14 @@ impl veilid_server::Server for VeilidServerImpl {
fn attach(
&mut self,
_params: veilid_server::AttachParams,
mut _results: veilid_server::AttachResults,
mut results: veilid_server::AttachResults,
) -> Promise<(), ::capnp::Error> {
trace!("VeilidServerImpl::attach");
let veilid_api = self.veilid_api.clone();
Promise::from_future(async move {
veilid_api
.attach()
.await
.map_err(|e| ::capnp::Error::failed(format!("{:?}", e)))
let result = veilid_api.attach().await;
encode_api_result(&result, &mut results.get().init_result());
Ok(())
})
}
@ -167,15 +172,14 @@ impl veilid_server::Server for VeilidServerImpl {
fn detach(
&mut self,
_params: veilid_server::DetachParams,
mut _results: veilid_server::DetachResults,
mut results: veilid_server::DetachResults,
) -> Promise<(), ::capnp::Error> {
trace!("VeilidServerImpl::detach");
let veilid_api = self.veilid_api.clone();
Promise::from_future(async move {
veilid_api
.detach()
.await
.map_err(|e| ::capnp::Error::failed(format!("{:?}", e)))
let result = veilid_api.detach().await;
encode_api_result(&result, &mut results.get().init_result());
Ok(())
})
}
@ -208,24 +212,30 @@ impl veilid_server::Server for VeilidServerImpl {
trace!("VeilidServerImpl::get_state");
let veilid_api = self.veilid_api.clone();
Promise::from_future(async move {
let state = veilid_api
.get_state()
.await
.map_err(|e| ::capnp::Error::failed(format!("{:?}", e)))?;
let state = serialize_json(state);
let res = results.get();
let mut rpc_state = res.init_state(
state
.len()
.try_into()
.map_err(|e| ::capnp::Error::failed(format!("{:?}", e)))?,
);
rpc_state.push_str(&state);
let result = veilid_api.get_state().await;
encode_api_result(&result, &mut results.get().init_result());
Ok(())
})
}
#[instrument(level = "trace", skip_all)]
fn change_log_level(
&mut self,
params: veilid_server::ChangeLogLevelParams,
mut results: veilid_server::ChangeLogLevelResults,
) -> Promise<(), ::capnp::Error> {
trace!("VeilidServerImpl::change_log_level");
let layer = pry!(pry!(params.get()).get_layer()).to_owned();
let log_level_json = pry!(pry!(params.get()).get_log_level()).to_owned();
let log_level: veilid_core::VeilidConfigLogLevel =
pry!(veilid_core::deserialize_json(&log_level_json)
.map_err(|e| ::capnp::Error::failed(format!("{:?}", e))));
let result = self.veilid_logs.change_log_level(layer, log_level);
encode_api_result(&result, &mut results.get().init_result());
Promise::ok(())
}
}
// --- Client API Server-Side ---------------------------------
@ -235,6 +245,7 @@ type ClientApiAllFuturesJoinHandle =
struct ClientApiInner {
veilid_api: veilid_core::VeilidAPI,
veilid_logs: VeilidLogs,
registration_map: Rc<RefCell<RegistrationMap>>,
stop: Option<StopSource>,
join_handle: Option<ClientApiAllFuturesJoinHandle>,
@ -246,10 +257,11 @@ pub struct ClientApi {
impl ClientApi {
#[instrument(level = "trace", skip_all)]
pub fn new(veilid_api: veilid_core::VeilidAPI) -> Rc<Self> {
pub fn new(veilid_api: veilid_core::VeilidAPI, veilid_logs: VeilidLogs) -> Rc<Self> {
Rc::new(Self {
inner: RefCell::new(ClientApiInner {
veilid_api,
veilid_logs,
registration_map: Rc::new(RefCell::new(RegistrationMap::new())),
stop: Some(StopSource::new()),
join_handle: None,
@ -393,7 +405,10 @@ impl ClientApi {
#[instrument(level = "trace", skip(self))]
pub fn run(self: Rc<Self>, bind_addrs: Vec<SocketAddr>) {
// Create client api VeilidServer
let veilid_server_impl = VeilidServerImpl::new(self.inner.borrow().veilid_api.clone());
let veilid_server_impl = VeilidServerImpl::new(
self.inner.borrow().veilid_api.clone(),
self.inner.borrow().veilid_logs.clone(),
);
self.inner.borrow_mut().registration_map = veilid_server_impl.registration_map.clone();
// Make a client object for the server to send to each rpc client

View File

@ -62,9 +62,9 @@ fn main() -> Result<(), String> {
// run the server to set the node id and quit
return block_on(async {
// Init combined console/file logger
let _logs = VeilidLogs::setup(settings.clone())?;
let veilid_logs = VeilidLogs::setup(settings.clone())?;
run_veilid_server(settings, server_mode).await
run_veilid_server(settings, server_mode, veilid_logs).await
})
.map(|v| {
println!("{}", success);
@ -96,8 +96,8 @@ fn main() -> Result<(), String> {
// Run the server loop
block_on(async {
// Init combined console/file logger
let _logs = VeilidLogs::setup(settings.clone())?;
let veilid_logs = VeilidLogs::setup(settings.clone())?;
run_veilid_server(settings, server_mode).await
run_veilid_server(settings, server_mode, veilid_logs).await
})
}

View File

@ -1,6 +1,7 @@
use crate::client_api;
use crate::settings::*;
use crate::tools::*;
use crate::veilid_logs::*;
use flume::{unbounded, Receiver, Sender};
use lazy_static::*;
use parking_lot::Mutex;
@ -29,14 +30,19 @@ pub fn shutdown() {
}
}
pub async fn run_veilid_server(settings: Settings, server_mode: ServerMode) -> Result<(), String> {
run_veilid_server_internal(settings, server_mode).await
pub async fn run_veilid_server(
settings: Settings,
server_mode: ServerMode,
veilid_logs: VeilidLogs,
) -> Result<(), String> {
run_veilid_server_internal(settings, server_mode, veilid_logs).await
}
#[instrument(err, skip_all)]
pub async fn run_veilid_server_internal(
settings: Settings,
server_mode: ServerMode,
veilid_logs: VeilidLogs,
) -> Result<(), String> {
trace!(?settings, ?server_mode);
@ -63,7 +69,7 @@ pub async fn run_veilid_server_internal(
// Start client api if one is requested
let mut capi = if settingsr.client_api.enabled && matches!(server_mode, ServerMode::Normal) {
let some_capi = client_api::ClientApi::new(veilid_api.clone());
let some_capi = client_api::ClientApi::new(veilid_api.clone(), veilid_logs.clone());
some_capi
.clone()
.run(settingsr.client_api.listen_address.addrs.clone());
@ -156,5 +162,9 @@ pub async fn run_veilid_server_internal(
// Wait for update receiver to exit
let _ = update_receiver_jh.await;
// Finally, drop logs
// this is explicit to ensure we don't accidentally drop them too soon via a move
drop(veilid_logs);
out
}

View File

@ -192,6 +192,7 @@ pub fn load_config(
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum LogLevel {
Off,
Error,
Warn,
Info,
@ -205,6 +206,7 @@ impl<'de> serde::Deserialize<'de> for LogLevel {
{
let s = String::deserialize(deserializer)?;
match s.to_ascii_lowercase().as_str() {
"off" => Ok(LogLevel::Off),
"error" => Ok(LogLevel::Error),
"warn" => Ok(LogLevel::Warn),
"info" => Ok(LogLevel::Info),
@ -223,6 +225,7 @@ impl serde::Serialize for LogLevel {
S: serde::Serializer,
{
let s = match self {
LogLevel::Off => "off",
LogLevel::Error => "error",
LogLevel::Warn => "warn",
LogLevel::Info => "info",
@ -233,13 +236,14 @@ impl serde::Serialize for LogLevel {
}
}
pub fn convert_loglevel(log_level: LogLevel) -> veilid_core::VeilidLogLevel {
pub fn convert_loglevel(log_level: LogLevel) -> veilid_core::VeilidConfigLogLevel {
match log_level {
LogLevel::Error => veilid_core::VeilidLogLevel::Error,
LogLevel::Warn => veilid_core::VeilidLogLevel::Warn,
LogLevel::Info => veilid_core::VeilidLogLevel::Info,
LogLevel::Debug => veilid_core::VeilidLogLevel::Debug,
LogLevel::Trace => veilid_core::VeilidLogLevel::Trace,
LogLevel::Off => veilid_core::VeilidConfigLogLevel::Off,
LogLevel::Error => veilid_core::VeilidConfigLogLevel::Error,
LogLevel::Warn => veilid_core::VeilidConfigLogLevel::Warn,
LogLevel::Info => veilid_core::VeilidConfigLogLevel::Info,
LogLevel::Debug => veilid_core::VeilidConfigLogLevel::Debug,
LogLevel::Trace => veilid_core::VeilidConfigLogLevel::Trace,
}
}
@ -1016,13 +1020,6 @@ impl Settings {
} else {
format!("subnode{}", inner.testing.subnode_index)
})),
"api_log_level" => Ok(Box::new(if inner.logging.api.enabled {
veilid_core::VeilidConfigLogLevel::from_veilid_log_level(Some(
convert_loglevel(inner.logging.api.level),
))
} else {
veilid_core::VeilidConfigLogLevel::Off
})),
"capabilities.protocol_udp" => Ok(Box::new(true)),
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)),
"capabilities.protocol_accept_tcp" => Ok(Box::new(true)),

View File

@ -98,7 +98,7 @@ pub fn run_daemon(settings: Settings, _matches: ArgMatches) -> Result<(), String
// Now, run the server
block_on(async {
// Init combined console/file logger
let _logs = VeilidLogs::setup(settings.clone())?;
let veilid_logs = VeilidLogs::setup(settings.clone())?;
// Daemonize
daemon
@ -112,7 +112,7 @@ pub fn run_daemon(settings: Settings, _matches: ArgMatches) -> Result<(), String
let signals_task = spawn(handle_signals(signals));
let res = run_veilid_server(settings, ServerMode::Normal).await;
let res = run_veilid_server(settings, ServerMode::Normal, veilid_logs).await;
// Terminate the signal stream.
handle.close();

View File

@ -3,25 +3,22 @@ use cfg_if::*;
use opentelemetry::sdk::*;
use opentelemetry::*;
use opentelemetry_otlp::WithExportConfig;
use parking_lot::*;
use std::collections::BTreeMap;
use std::path::*;
use tracing::*;
use std::sync::Arc;
use tracing_appender::*;
use tracing_subscriber::prelude::*;
use tracing_subscriber::*;
pub struct VeilidLogs {
pub guard: Option<non_blocking::WorkerGuard>,
struct VeilidLogsInner {
_guard: Option<non_blocking::WorkerGuard>,
filters: BTreeMap<&'static str, veilid_core::VeilidLayerFilter>,
}
fn logfilter<T: AsRef<str>, V: AsRef<[T]>>(metadata: &Metadata, ignore_list: V) -> bool {
// Skip filtered targets
!match (metadata.target(), ignore_list.as_ref()) {
(path, ignore) if !ignore.is_empty() => {
// Check that the module path does not match any ignore filters
ignore.iter().any(|v| path.starts_with(v.as_ref()))
}
_ => false,
}
#[derive(Clone)]
pub struct VeilidLogs {
inner: Arc<Mutex<VeilidLogsInner>>,
}
impl VeilidLogs {
@ -29,39 +26,26 @@ impl VeilidLogs {
let settingsr = settings.read();
// Set up subscriber and layers
let mut ignore_list = Vec::<String>::new();
for ig in veilid_core::DEFAULT_LOG_IGNORE_LIST {
ignore_list.push(ig.to_owned());
}
let subscriber = Registry::default();
let mut layers = Vec::new();
let mut filters = BTreeMap::new();
// Terminal logger
let subscriber = subscriber.with(if settingsr.logging.terminal.enabled {
let terminal_max_log_level: level_filters::LevelFilter =
convert_loglevel(settingsr.logging.terminal.level)
.to_tracing_level()
.into();
let ignore_list = ignore_list.clone();
Some(
fmt::Layer::new()
.compact()
.with_writer(std::io::stdout)
.with_filter(terminal_max_log_level)
.with_filter(filter::FilterFn::new(move |metadata| {
logfilter(metadata, &ignore_list)
})),
)
} else {
None
});
if settingsr.logging.terminal.enabled {
let filter = veilid_core::VeilidLayerFilter::new(
convert_loglevel(settingsr.logging.terminal.level),
None,
);
let layer = fmt::Layer::new()
.compact()
.with_writer(std::io::stdout)
.with_filter(filter.clone());
filters.insert("terminal", filter);
layers.push(layer.boxed());
}
// OpenTelemetry logger
let subscriber = subscriber.with(if settingsr.logging.otlp.enabled {
let otlp_max_log_level: level_filters::LevelFilter =
convert_loglevel(settingsr.logging.otlp.level)
.to_tracing_level()
.into();
if settingsr.logging.otlp.enabled {
let grpc_endpoint = settingsr.logging.otlp.grpc_endpoint.name.clone();
cfg_if! {
@ -95,27 +79,20 @@ impl VeilidLogs {
.install_batch(batch)
.map_err(|e| format!("failed to install OpenTelemetry tracer: {}", e))?;
let ignore_list = ignore_list.clone();
Some(
tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_filter(otlp_max_log_level)
.with_filter(filter::FilterFn::new(move |metadata| {
logfilter(metadata, &ignore_list)
})),
)
} else {
None
});
let filter = veilid_core::VeilidLayerFilter::new(
convert_loglevel(settingsr.logging.otlp.level),
None,
);
let layer = tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_filter(filter.clone());
filters.insert("otlp", filter);
layers.push(layer.boxed());
}
// File logger
let mut guard = None;
let subscriber = subscriber.with(if settingsr.logging.file.enabled {
let file_max_log_level: level_filters::LevelFilter =
convert_loglevel(settingsr.logging.file.level)
.to_tracing_level()
.into();
if settingsr.logging.file.enabled {
let log_path = Path::new(&settingsr.logging.file.path);
let full_path = std::env::current_dir()
.unwrap_or(PathBuf::from(MAIN_SEPARATOR.to_string()))
@ -140,50 +117,88 @@ impl VeilidLogs {
tracing_appender::non_blocking(appender);
guard = Some(non_blocking_guard);
let ignore_list = ignore_list.clone();
Some(
fmt::Layer::new()
.compact()
.with_writer(non_blocking_appender)
.with_filter(file_max_log_level)
.with_filter(filter::FilterFn::new(move |metadata| {
logfilter(metadata, &ignore_list)
})),
)
} else {
None
});
let filter = veilid_core::VeilidLayerFilter::new(
convert_loglevel(settingsr.logging.file.level),
None,
);
let layer = fmt::Layer::new()
.compact()
.with_writer(non_blocking_appender)
.with_filter(filter.clone());
filters.insert("file", filter);
layers.push(layer.boxed());
}
// API logger
let subscriber = subscriber.with(if settingsr.logging.api.enabled {
// Get layer from veilid core, filtering is done by ApiTracingLayer automatically
Some(veilid_core::ApiTracingLayer::get())
} else {
None
});
if settingsr.logging.api.enabled {
let filter = veilid_core::VeilidLayerFilter::new(
convert_loglevel(settingsr.logging.api.level),
None,
);
let layer = veilid_core::ApiTracingLayer::get().with_filter(filter.clone());
filters.insert("api", filter);
layers.push(layer.boxed());
}
// Systemd Journal logger
cfg_if! {
if #[cfg(target_os = "linux")] {
let subscriber = subscriber.with(if settingsr.logging.system.enabled {
let ignore_list = ignore_list.clone();
let system_max_log_level: level_filters::LevelFilter = convert_loglevel(settingsr.logging.system.level).to_tracing_level().into();
Some(tracing_journald::layer().map_err(|e| format!("failed to set up journald logging: {}", e))?
.with_filter(system_max_log_level)
.with_filter(filter::FilterFn::new(move |metadata| {
logfilter(metadata, &ignore_list)
}))
)
} else {
None
});
if settingsr.logging.system.enabled {
let filter = veilid_core::VeilidLayerFilter::new(
convert_loglevel(settingsr.logging.system.level),
None,
);
let layer =tracing_journald::layer().map_err(|e| format!("failed to set up journald logging: {}", e))?
.with_filter(filter.clone());
filters.insert("system", filter);
layers.push(layer.boxed());
}
}
}
let subscriber = subscriber.with(layers);
subscriber
.try_init()
.map_err(|e| format!("failed to initialize logging: {}", e))?;
Ok(VeilidLogs { guard })
Ok(VeilidLogs {
inner: Arc::new(Mutex::new(VeilidLogsInner {
_guard: guard,
filters,
})),
})
}
pub fn change_log_level(
&self,
layer: String,
log_level: veilid_core::VeilidConfigLogLevel,
) -> Result<(), veilid_core::VeilidAPIError> {
// get layer to change level on
let layer = if layer == "all" { "".to_owned() } else { layer };
// change log level on appropriate layer
let inner = self.inner.lock();
if layer.is_empty() {
// Change all layers
for f in inner.filters.values() {
f.set_max_level(log_level);
}
} else {
// Change a specific layer
let f = match inner.filters.get(layer.as_str()) {
Some(f) => f,
None => {
return Err(veilid_core::VeilidAPIError::InvalidArgument {
context: "change_log_level".to_owned(),
argument: "layer".to_owned(),
value: layer,
});
}
};
f.set_max_level(log_level);
}
Ok(())
}
}

View File

@ -32,22 +32,12 @@ pub fn setup() -> () {
SETUP_ONCE.call_once(|| {});
}
// Log filtering
fn logfilter<T: AsRef<str>, V: AsRef<[T]>>(metadata: &Metadata, ignore_list: V) -> bool {
// Skip filtered targets
!match (metadata.target(), ignore_list.as_ref()) {
(path, ignore) if !ignore.is_empty() => {
// Check that the module path does not match any ignore filters
ignore.iter().any(|v| path.starts_with(v.as_ref()))
}
_ => false,
}
}
// API Singleton
lazy_static! {
static ref VEILID_API: SendWrapper<RefCell<Option<veilid_core::VeilidAPI>>> =
SendWrapper::new(RefCell::new(None));
static ref FILTERS: SendWrapper<RefCell<BTreeMap<&'static str, veilid_core::VeilidLayerFilter>>> =
SendWrapper::new(RefCell::new(BTreeMap::new()));
}
fn get_veilid_api() -> Result<veilid_core::VeilidAPI, veilid_core::VeilidAPIError> {
@ -123,9 +113,16 @@ pub struct VeilidWASMConfigLoggingPerformance {
pub logs_in_console: bool,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct VeilidWASMConfigLoggingAPI {
pub enabled: bool,
pub level: veilid_core::VeilidLogLevel,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct VeilidWASMConfigLogging {
pub performance: VeilidWASMConfigLoggingPerformance,
pub api: VeilidWASMConfigLoggingAPI,
}
#[derive(Debug, Deserialize, Serialize)]
@ -146,48 +143,61 @@ pub fn configure_veilid_platform(platform_config: String) {
.expect("failed to deserialize plaform config json");
// Set up subscriber and layers
let mut ignore_list = Vec::<String>::new();
for ig in veilid_core::DEFAULT_LOG_IGNORE_LIST {
ignore_list.push(ig.to_owned());
}
let subscriber = Registry::default();
let mut layers = Vec::new();
let handles = (*FILTER_RELOAD_HANDLES).borrow_mut();
// Performance logger
let subscriber = subscriber.with(if platform_config.logging.performance.enabled {
let performance_max_log_level =
platform_config.logging.performance.level.to_tracing_level();
let ignore_list = ignore_list.clone();
Some(
WASMLayer::new(
WASMLayerConfigBuilder::new()
.set_report_logs_in_timings(platform_config.logging.performance.logs_in_timings)
.set_console_config(if platform_config.logging.performance.logs_in_console {
ConsoleConfig::ReportWithConsoleColor
} else {
ConsoleConfig::NoReporting
})
.set_max_level(performance_max_log_level)
.build(),
)
.with_filter(filter::FilterFn::new(move |metadata| {
logfilter(metadata, &ignore_list)
})),
if platform_config.logging.performance.enabled {
let filter =
veilid_core::VeilidLayerFilter::new(platform_config.logging.performance.level, None);
let layer = WASMLayer::new(
WASMLayerConfigBuilder::new()
.set_report_logs_in_timings(platform_config.logging.performance.logs_in_timings)
.set_console_config(if platform_config.logging.performance.logs_in_console {
ConsoleConfig::ReportWithConsoleColor
} else {
ConsoleConfig::NoReporting
})
.build(),
)
} else {
None
});
.with_filter(filter.clone());
handles.insert("performance", filter);
layers.push(layer.boxed());
};
// API logger (always add layer, startup will init this if it is enabled in settings)
let subscriber = subscriber.with(veilid_core::ApiTracingLayer::get());
// API logger
if platform_config.logging.api.enabled {
let filter = veilid_core::VeilidLayerFilter::new(platform_config.logging.api.level, None);
let layer = veilid_core::ApiTracingLayer::get().with_filter(filter.clone());
handles.insert("api", filter);
layers.push(layer.boxed());
}
let subscriber = subscriber.with(layers);
subscriber
.try_init()
.map_err(|e| format!("failed to initialize logging: {}", e))
.expect("failed to initalize WASM platform");
}
#[wasm_bindgen()]
pub fn change_log_level(layer: String, log_level: String) {
let layer = if layer == "all" { "".to_owned() } else { layer };
let log_level: veilid_core::VeilidConfigLogLevel = deserialize_json(&log_level).unwrap();
let filters = (*FILTERS).borrow();
if layer.is_empty() {
// Change all layers
for f in filters.values() {
f.set_max_level(log_level);
}
} else {
// Change a specific layer
let f = filters.get(layer.as_str()).unwrap();
f.set_max_level(log_level);
}
}
#[wasm_bindgen()]
pub fn startup_veilid_core(update_callback: Function, json_config: String) -> Promise {
wrap_api_future(async move {
@ -221,17 +231,6 @@ pub fn get_veilid_state() -> Promise {
})
}
#[wasm_bindgen()]
pub fn change_api_log_level(log_level: String) -> Promise {
wrap_api_future(async move {
let log_level: veilid_core::VeilidConfigLogLevel = deserialize_json(&log_level)?;
//let veilid_api = get_veilid_api()?;
//veilid_api.change_api_log_level(log_level).await;
veilid_core::ApiTracingLayer::change_api_log_level(log_level.to_veilid_log_level());
APIRESULT_UNDEFINED
})
}
#[wasm_bindgen()]
pub fn shutdown_veilid_core() -> Promise {
wrap_api_future(async move {