use std::sync::Arc;
use tokio::{
io::{AsyncWrite, AsyncWriteExt},
sync::Mutex,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OutputFormat {
Standard,
TelemetryLogFd,
}
pub struct Sink(Arc<Mutex<Box<dyn AsyncWrite + Send + Unpin>>>, OutputFormat);
impl Clone for Sink {
fn clone(&self) -> Self {
Sink(Arc::clone(&self.0), self.1.clone())
}
}
impl Sink {
pub fn new(s: impl AsyncWrite + Send + Unpin + 'static) -> Self {
Sink(Arc::new(Mutex::new(Box::new(s))), OutputFormat::Standard)
}
pub fn stdout() -> Self {
Sink::new(tokio::io::stdout())
}
pub fn stderr() -> Self {
Sink::new(tokio::io::stderr())
}
pub fn format(mut self, kind: OutputFormat) -> Self {
self.1 = kind;
self
}
#[cfg(target_os = "linux")]
pub fn lambda_telemetry_log_fd() -> Result<Self, Error> {
std::env::var("_LAMBDA_TELEMETRY_LOG_FD")
.map_err(|e| Error::VarError(e))
.and_then(|fd| {
let fd = fd.parse().map_err(|e| Error::ParseIntError(e))?;
Ok(
Sink::new(unsafe { <tokio::fs::File as std::os::fd::FromRawFd>::from_raw_fd(fd) })
.format(OutputFormat::TelemetryLogFd),
)
})
}
pub async fn write_line(&self, s: String) {
let mut f = self.0.lock().await;
match self.1 {
OutputFormat::Standard => {
f.write_all(s.as_bytes()).await.unwrap();
f.write_all(b"\n").await.unwrap();
}
OutputFormat::TelemetryLogFd => {
let mut buf = [0; 16];
buf[0..4].copy_from_slice(&0xa55a0003u32.to_be_bytes());
let len = s.len() as u32 + 1; buf[4..8].copy_from_slice(&len.to_be_bytes());
let timestamp = chrono::Utc::now().timestamp_micros();
buf[8..16].copy_from_slice(×tamp.to_be_bytes());
f.write_all(&buf).await.unwrap();
f.write_all(s.as_bytes()).await.unwrap();
f.write_all(b"\n").await.unwrap();
}
}
}
pub async fn flush(&self) {
self.0.lock().await.flush().await.unwrap()
}
}
#[derive(Debug)]
pub enum Error {
VarError(std::env::VarError),
ParseIntError(std::num::ParseIntError),
}