-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add support for host logging, deprecating old topics_logger in favor of host-managed topics logger. updates examples. #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
be0586b
157043d
8659de1
d5ab3b9
95a083f
d444fde
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| //! Host interfaces for working with host logging, allowing you to send | ||
| //! logs to different destinations | ||
| use momento_functions_wit::host::momento::host::logging; | ||
| use thiserror::Error; | ||
|
|
||
| /// Where do you want your logs to go? | ||
| pub enum LogDestination { | ||
| /// Momento topic within the same cache as your function | ||
| Topic { | ||
| /// Name of the topic | ||
| topic: String, | ||
| }, | ||
| } | ||
|
|
||
| /// A single configuration for a destination | ||
| pub struct ConfigureLoggingInput { | ||
| /// At what level would you like Momento's system logs to be filtered into this destination? | ||
| pub system_log_level: log::LevelFilter, | ||
| /// The specific destination | ||
| pub destination: LogDestination, | ||
| } | ||
|
|
||
| impl ConfigureLoggingInput { | ||
| /// Constructs a single logging input with a desired destination. System logs will be at default level (INFO). | ||
| pub fn new(destination: LogDestination) -> Self { | ||
| Self { | ||
| system_log_level: log::LevelFilter::Info, | ||
| destination, | ||
| } | ||
| } | ||
|
|
||
| /// Constructs a single logging input with a desired destination as well as a specified system logs filter. | ||
| pub fn new_with_system_log_level( | ||
| system_log_level: log::LevelFilter, | ||
| destination: LogDestination, | ||
| ) -> Self { | ||
| Self { | ||
| system_log_level, | ||
| destination, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<LogDestination> for logging::Destination { | ||
| fn from(value: LogDestination) -> Self { | ||
| match value { | ||
| LogDestination::Topic { topic } => { | ||
| momento_functions_wit::host::momento::host::logging::Destination::Topic( | ||
| logging::TopicDestination { topic_name: topic }, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<ConfigureLoggingInput> for logging::ConfigureLoggingInput { | ||
| fn from(value: ConfigureLoggingInput) -> Self { | ||
| Self { | ||
| system_logs_level: match value.system_log_level { | ||
| log::LevelFilter::Off => logging::LogLevel::Off, | ||
| log::LevelFilter::Error => logging::LogLevel::Error, | ||
| log::LevelFilter::Warn => logging::LogLevel::Warn, | ||
| log::LevelFilter::Info => logging::LogLevel::Info, | ||
| log::LevelFilter::Debug => logging::LogLevel::Debug, | ||
| // Momento does not publish Trace logs | ||
| log::LevelFilter::Trace => logging::LogLevel::Debug, | ||
| }, | ||
| destination: value.destination.into(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Captures any errors Momento has detected during log configuration | ||
| #[derive(Debug, Error)] | ||
| pub enum LogConfigurationError { | ||
| #[error("Invalid auth provided for configuration! {message}")] | ||
| /// Invalid auth was provided | ||
| Auth { | ||
| /// The error message bubbled back up | ||
| message: String, | ||
| }, | ||
| #[error("Something went wrong while trying to configure logs! {message}")] | ||
| /// Something went wrong | ||
| Unknown { | ||
| /// The error message bubbled back up | ||
| message: String, | ||
| }, | ||
| } | ||
|
|
||
| /// Configures logging via Momento host functions | ||
| pub fn configure_logging(inputs: Vec<ConfigureLoggingInput>) -> Result<(), LogConfigurationError> { | ||
tylerburdsall marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| let converted: Vec<logging::ConfigureLoggingInput> = | ||
| inputs.into_iter().map(Into::into).collect(); | ||
| logging::configure_logging(&converted).map_err(|e| LogConfigurationError::Auth { | ||
| message: e.to_string(), | ||
| }) | ||
| } | ||
|
|
||
| /// Logs a given string | ||
| pub fn log(input: &str) { | ||
| logging::log(input) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| use std::fmt::Write; | ||
| use std::sync::atomic::AtomicU32; | ||
|
|
||
| use log::{LevelFilter, Log, set_logger_racy, set_max_level}; | ||
| use momento_functions_host::logging::{ConfigureLoggingInput, LogConfigurationError}; | ||
| use time::format_description::well_known::Rfc3339; | ||
|
|
||
| pub struct HostLog { | ||
| level: LevelFilter, | ||
| dropped: AtomicU32, | ||
| } | ||
|
|
||
| impl HostLog { | ||
| pub fn init( | ||
| log_level: LevelFilter, | ||
| destinations: Vec<ConfigureLoggingInput>, | ||
| ) -> Result<(), LogConfigurationError> { | ||
| set_max_level(log_level); | ||
|
|
||
| static mut LOGGER: Option<HostLog> = None; | ||
| momento_functions_host::logging::configure_logging(destinations)?; | ||
| #[allow(static_mut_refs)] | ||
| #[allow(clippy::expect_used)] | ||
| unsafe { | ||
tylerburdsall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| LOGGER.replace(HostLog { | ||
| level: log_level, | ||
| dropped: AtomicU32::new(0), | ||
| }); | ||
| set_logger_racy(LOGGER.as_mut().expect("logger just set")).map_err(|e| { | ||
| LogConfigurationError::Unknown { | ||
| message: format!("Failed to configure logger! {e:?}"), | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Log for HostLog { | ||
| fn enabled(&self, metadata: &log::Metadata) -> bool { | ||
| metadata.level() <= self.level | ||
| } | ||
|
|
||
| fn log(&self, record: &log::Record) { | ||
| if self.enabled(record.metadata()) { | ||
| let mut buffer = String::with_capacity(128); | ||
|
||
| let utc_now = time::OffsetDateTime::now_utc(); | ||
| let timestamp = utc_now.format(&Rfc3339).unwrap_or("<unknown>".to_string()); | ||
| let level = record.level().as_str(); | ||
| let module = record.module_path().unwrap_or("<unknown>"); | ||
| let file = record.file().unwrap_or("<unknown>"); | ||
| let line = record.line().unwrap_or(0); | ||
| let log_message = record.args(); | ||
|
|
||
| let dropped = self.dropped.swap(0, std::sync::atomic::Ordering::Relaxed); | ||
| let dropped_clause = if 0 < dropped { | ||
| format!(" ({dropped} messages dropped)") | ||
| } else { | ||
| String::new() | ||
| }; | ||
tylerburdsall marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| let _ = write!( | ||
| &mut buffer, | ||
| "{level} {timestamp} {module} {file}:{line}{dropped_clause} {log_message}" | ||
| ); | ||
|
|
||
| momento_functions_host::logging::log(buffer.as_str()); | ||
| } | ||
| } | ||
|
|
||
| fn flush(&self) {} | ||
| } | ||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.