-
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
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
be0586b
feat: add support for host logging, deprecating old topics_logger in …
tylerburdsall 157043d
use log::LevelFilter instead. add timestamp to logs.
tylerburdsall 8659de1
PR comments
tylerburdsall d5ab3b9
feat: add support for shipping logs to CloudWatch
tylerburdsall 95a083f
fmt
tylerburdsall d444fde
feat: support application log level by destination
tylerburdsall File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| //! 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, | ||
| }, | ||
| /// AWS CloudWatch Log Group for your function's logs | ||
| CloudWatch { | ||
| /// ARN of the IAM role for Momento to assume | ||
| iam_role_arn: String, | ||
| /// ARN of the CloudWatch Log Group for Momento to publish your | ||
| /// function logs to | ||
| log_group_name: String, | ||
| }, | ||
| } | ||
|
|
||
| impl LogDestination { | ||
| /// Creates a Topic destination | ||
| pub fn topic(name: impl Into<String>) -> Self { | ||
| Self::Topic { topic: name.into() } | ||
| } | ||
| /// Creates a CloudWatch destination. | ||
| /// Reach out to us at `[email protected]` for details on how to properly | ||
| /// set up your log configuration. | ||
| pub fn cloudwatch(iam_role_arn: impl Into<String>, log_group_name: impl Into<String>) -> Self { | ||
| Self::CloudWatch { | ||
| iam_role_arn: iam_role_arn.into(), | ||
| log_group_name: log_group_name.into(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A single configuration for a destination | ||
| pub struct LogConfiguration { | ||
| /// At what level would you like your function's logs to be filtered into this destination? | ||
| log_level: log::LevelFilter, | ||
| /// At what level would you like Momento's system logs to be filtered into this destination? | ||
| system_log_level: log::LevelFilter, | ||
| /// The specific destination | ||
| destination: LogDestination, | ||
| } | ||
|
|
||
| impl LogConfiguration { | ||
| /// Constructs a single logging input with a desired destination. System logs will be at default level (INFO). | ||
| pub fn new(destination: LogDestination) -> Self { | ||
| Self { | ||
| log_level: log::LevelFilter::Info, | ||
| system_log_level: log::LevelFilter::Info, | ||
| destination, | ||
| } | ||
| } | ||
|
|
||
| /// Constructs a single logging input with a desired destination as well as a specified logs filter. | ||
| pub fn with_log_level(mut self, log_level: log::LevelFilter) -> Self { | ||
| self.log_level = log_level; | ||
| self | ||
| } | ||
|
|
||
| /// Constructs a single logging input with a desired destination as well as a specified system logs filter. | ||
| pub fn with_system_log_level(mut self, system_log_level: log::LevelFilter) -> Self { | ||
| self.system_log_level = system_log_level; | ||
| self | ||
| } | ||
| } | ||
|
|
||
| impl From<LogDestination> for LogConfiguration { | ||
| fn from(value: LogDestination) -> Self { | ||
| match value { | ||
| LogDestination::Topic { topic } => Self::new(LogDestination::topic(topic)), | ||
| LogDestination::CloudWatch { | ||
| iam_role_arn, | ||
| log_group_name, | ||
| } => Self::new(LogDestination::cloudwatch(iam_role_arn, log_group_name)), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Create a single `LogConfiguration` given a `LogDestination`. | ||
| pub fn log_configuration(destination: LogDestination) -> LogConfiguration { | ||
| LogConfiguration::new(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 }, | ||
| ) | ||
| } | ||
| LogDestination::CloudWatch { | ||
| iam_role_arn, | ||
| log_group_name, | ||
| } => momento_functions_wit::host::momento::host::logging::Destination::Cloudwatch( | ||
| logging::CloudwatchDestination { | ||
| iam_role_arn, | ||
| log_group_name, | ||
| }, | ||
| ), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<LogConfiguration> for logging::ConfigureLoggingInput { | ||
| fn from(value: LogConfiguration) -> Self { | ||
| Self { | ||
| log_level: match value.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, | ||
| }, | ||
| 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, | ||
| }, | ||
| } | ||
|
|
||
| impl From<logging::LogConfigurationError> for LogConfigurationError { | ||
| fn from(value: logging::LogConfigurationError) -> Self { | ||
| match value { | ||
| logging::LogConfigurationError::Auth(e) => Self::Auth { message: e }, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Configures logging via Momento host functions | ||
| pub fn configure_host_logging( | ||
| configurations: impl IntoIterator<Item = LogConfiguration>, | ||
| ) -> Result<(), LogConfigurationError> { | ||
| let configurations = configurations | ||
| .into_iter() | ||
| .map(|configuration| configuration.into()) | ||
| .collect::<Vec<logging::ConfigureLoggingInput>>(); | ||
| Ok(logging::configure_logging(&configurations)?) | ||
| } | ||
|
|
||
| /// Logs a given string | ||
| pub fn log(input: &str, level: log::Level) { | ||
| logging::log( | ||
| input, | ||
| match level { | ||
| log::Level::Error => logging::LogLevel::Error, | ||
| log::Level::Warn => logging::LogLevel::Warn, | ||
| log::Level::Info => logging::LogLevel::Info, | ||
| log::Level::Debug => logging::LogLevel::Debug, | ||
| // Momento does not publish Trace logs | ||
| log::Level::Trace => logging::LogLevel::Debug, | ||
| }, | ||
| ) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| use std::fmt::Write; | ||
|
|
||
| use log::{Log, set_logger_racy, set_max_level}; | ||
| use momento_functions_host::logging::{LogConfiguration, LogConfigurationError}; | ||
| use time::format_description::well_known::Rfc3339; | ||
|
|
||
| pub struct HostLog {} | ||
|
|
||
| impl HostLog { | ||
| pub fn init( | ||
| configurations: impl IntoIterator<Item = LogConfiguration>, | ||
| ) -> Result<(), LogConfigurationError> { | ||
| static mut LOGGER: Option<HostLog> = None; | ||
| // We're setting this to DEBUG so all logs are captured and sent to the host serving | ||
| // the function. The host will determine whether to log the mesage. | ||
| set_max_level(log::LevelFilter::Debug); | ||
| momento_functions_host::logging::configure_host_logging(configurations)?; | ||
| #[allow(static_mut_refs)] | ||
| #[allow(clippy::expect_used)] | ||
| // SAFETY: concurrency requirement is satisfied by the single threaded nature | ||
| // of the Function environment. | ||
| unsafe { | ||
| LOGGER.replace(HostLog {}); | ||
| 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 { | ||
| // Host logging will filter out logs based on level | ||
| true | ||
| } | ||
|
|
||
| fn log(&self, record: &log::Record) { | ||
| 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 record_level = record.level(); | ||
| 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 _ = write!( | ||
| &mut buffer, | ||
| "{level} {timestamp} {module} {file}:{line} {log_message}" | ||
| ); | ||
|
|
||
| momento_functions_host::logging::log(buffer.as_str(), record_level); | ||
| } | ||
|
|
||
| fn flush(&self) {} | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.