tracing-cloudwatch 0.4.1

tracing-subscriber layer that sends your application's tracing events(logs) to AWS CloudWatch Logs
Documentation
use async_trait::async_trait;
use aws_sdk_cloudwatchlogs::{
    Client as SdkClient,
    error::{BuildError, SdkError},
    operation::put_log_events::PutLogEventsError,
    types::InputLogEvent,
};

use crate::{
    client::{CloudWatchClient, LogDestination, PutLogsError},
    dispatch::LogEvent,
};

#[async_trait]
impl CloudWatchClient for SdkClient {
    async fn put_logs(
        &self,
        dest: LogDestination,
        logs: Vec<LogEvent>,
    ) -> Result<(), PutLogsError> {
        let log_events = logs
            .into_iter()
            .map(TryFrom::try_from)
            .collect::<Result<Vec<_>, BuildError>>()
            .map_err(|err| PutLogsError::Other(err.into()))?;

        match self
            .put_log_events()
            .set_log_events(Some(log_events))
            .log_group_name(dest.log_group_name)
            .log_stream_name(dest.log_stream_name)
            .send()
            .await
        {
            Ok(output) => {
                if let Some(rejected_info) = output.rejected_log_events_info() {
                    eprintln!("[tracing-cloudwatch] Put logs rejected: {rejected_info:?}");
                }
                Ok(())
            }
            Err(SdkError::ServiceError(service_err)) => match service_err.into_err() {
                PutLogEventsError::ResourceNotFoundException(err) => {
                    Err(PutLogsError::LogDestinationNotFound {
                        message: err.message().unwrap_or_default().to_string(),
                    })
                }
                err => Err(anyhow::Error::from(err).into()),
            },
            Err(err) => Err(anyhow::Error::from(err).into()),
        }
    }
}

impl TryFrom<LogEvent> for InputLogEvent {
    type Error = BuildError;

    fn try_from(value: LogEvent) -> Result<Self, Self::Error> {
        InputLogEvent::builder()
            .timestamp(value.timestamp.timestamp_millis())
            .message(value.message)
            .build()
    }
}