use reqwest::Method;
use serde::Serialize;
use crate::client::Client;
use crate::error::{Error, Result};
use crate::types::LogLevel;
pub(crate) const MAX_BATCH_ENTRIES: usize = 100;
#[derive(Debug, Clone, Serialize)]
pub struct LogEntryInput {
pub level: LogLevel,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<String>,
}
impl LogEntryInput {
pub fn new(
level: LogLevel,
message: impl Into<String>,
context: Option<serde_json::Value>,
) -> Self {
Self {
level,
message: message.into(),
context,
timestamp: None,
}
}
#[must_use]
pub fn timestamp(mut self, timestamp: impl Into<String>) -> Self {
self.timestamp = Some(timestamp.into());
self
}
}
#[derive(Clone)]
pub struct Logs {
client: Client,
}
macro_rules! level_method {
($name:ident, $level:expr) => {
pub async fn $name(
&self,
message: impl Into<String>,
context: Option<serde_json::Value>,
) -> Result<String> {
self.write($level, message, context).await
}
};
}
impl Logs {
pub(crate) fn new(client: Client) -> Self {
Self { client }
}
pub async fn write(
&self,
level: LogLevel,
message: impl Into<String>,
context: Option<serde_json::Value>,
) -> Result<String> {
#[derive(Serialize)]
struct Body {
level: LogLevel,
message: String,
#[serde(skip_serializing_if = "Option::is_none")]
context: Option<serde_json::Value>,
}
#[derive(serde::Deserialize)]
struct Resp {
id: String,
}
let env_id = &self.client.inner.env_id;
let body = Body {
level,
message: message.into(),
context,
};
let resp: Resp = self
.client
.inner
.http
.request(Method::POST, &format!("/c/{env_id}/log"), Some(&body))
.await?;
Ok(resp.id)
}
pub async fn write_batch(&self, entries: &[LogEntryInput]) -> Result<Vec<String>> {
if entries.is_empty() {
return Ok(Vec::new());
}
if entries.len() > MAX_BATCH_ENTRIES {
return Err(Error::Api {
status: 0,
message: format!(
"write_batch accepts at most {MAX_BATCH_ENTRIES} entries per request, got {}",
entries.len()
),
body: None,
});
}
#[derive(serde::Deserialize)]
struct Resp {
ids: Vec<String>,
}
let env_id = &self.client.inner.env_id;
let body = serde_json::json!({ "entries": entries });
let resp: Resp = self
.client
.inner
.http
.request(Method::POST, &format!("/c/{env_id}/logs"), Some(&body))
.await?;
Ok(resp.ids)
}
level_method!(debug, LogLevel::Debug);
level_method!(info, LogLevel::Info);
level_method!(notice, LogLevel::Notice);
level_method!(warning, LogLevel::Warning);
level_method!(error, LogLevel::Error);
level_method!(critical, LogLevel::Critical);
level_method!(alert, LogLevel::Alert);
level_method!(emergency, LogLevel::Emergency);
}