Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ fn main() -> Result<()> {
logfire::info!(
"total size of {cwd} is {size} bytes",
cwd = cwd.display().to_string(),
size = total_size as i64
size = total_size
);

shutdown_handler.shutdown()?;
Expand Down
2 changes: 1 addition & 1 deletion src/internal/exporters/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ mod tests {
let _ = crate::span!(level: Level::DEBUG, "debug span");
let _ =
crate::span!(parent: &root, level: Level::DEBUG, "debug span with explicit parent");
crate::info!("log with values", foo = 42i64, bar = 33i64);
crate::info!("log with values", foo = 42, bar = 33);
crate::info!("hello world log");
panic!("oh no!");
}))
Expand Down
92 changes: 62 additions & 30 deletions src/macros/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,64 @@ impl LogfireValue {
}
}

// Helper structure which converts arguments which might be optional into
// otel arguments. Otel arguments are not allowed to be optional, so we
// have to lift this a layer.
/// Helper type which provides overrides for values which are either:
/// - Not handled by `Into<Value>` in OpenTelemetry
/// - Need special handling for type inference (e.g. `i32` integers)
/// - Are `Option<T>`, as opentelemetry does not support `Option` directly
pub struct LogfireConverter<T>(ConvertValue<T>);

pub struct FallbackToConvertValue<T>(PhantomData<T>);

pub struct TryConvertOption<T>(FallbackToConvertValue<T>);

pub struct LogfireConverter<T>(TryConvertOption<T>);
/// Implements default conversion logic
/// - Uses `Into<Value>` for most types
/// - Also provides the base case for any type inference / option handling
pub struct ConvertValue<T>(PhantomData<T>);

#[must_use]
pub fn converter<T>(_: &T) -> LogfireConverter<T> {
LogfireConverter(TryConvertOption(FallbackToConvertValue(PhantomData)))
LogfireConverter(ConvertValue(PhantomData))
}

impl<T> LogfireConverter<Option<T>> {
/// Flattens out options, returning a None branch to stop processing if the option is None.
#[inline]
#[must_use]
pub fn unpack_option(&self, value: Option<T>) -> Option<(T, LogfireConverter<T>)> {
value.map(|v| (v, LogfireConverter(ConvertValue(PhantomData))))
}
}

impl LogfireConverter<i32> {
/// Cause type inference to pick the i32 specialization for in info!("value: {value:?}", value = 12);
#[inline]
#[must_use]
pub fn handle_type_inference(&self) -> LogfireConverter<i32> {
LogfireConverter(ConvertValue(PhantomData))
}
}

impl LogfireConverter<Option<i32>> {
/// Cause type inference to pick the i32 specialization for in info!("value: {value:?}", value = Some(12));
#[inline]
#[must_use]
pub fn handle_type_inference(&self) -> LogfireConverter<Option<i32>> {
LogfireConverter(ConvertValue(PhantomData))
}
}

impl<T> ConvertValue<T> {
/// Base case for types where we don't care about type inference.
#[inline]
#[must_use]
pub fn handle_type_inference(&self) -> LogfireConverter<T> {
LogfireConverter(ConvertValue(PhantomData))
}

/// Base case for non-option values, just wrap it up. This will get removed again by
/// inlining and optimization.
#[inline]
#[must_use]
pub fn unpack_option(&self, value: T) -> Option<(T, LogfireConverter<T>)> {
Some((value, LogfireConverter(ConvertValue(PhantomData))))
}
}

/// Convenience to take ownership of borrow on String
Expand Down Expand Up @@ -146,32 +191,14 @@ impl LogfireConverter<char> {
}

impl<T> Deref for LogfireConverter<T> {
type Target = TryConvertOption<T>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T> TryConvertOption<Option<T>> {
#[inline]
pub fn convert_value(&self, value: Option<T>) -> Option<Value>
where
T: Into<Value>,
{
value.map(Into::into)
}
}

impl<T> Deref for TryConvertOption<T> {
type Target = FallbackToConvertValue<T>;
type Target = ConvertValue<T>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T> FallbackToConvertValue<T> {
impl<T> ConvertValue<T> {
#[inline]
pub fn convert_value(&self, value: T) -> Option<Value>
where
Expand Down Expand Up @@ -319,7 +346,12 @@ macro_rules! __log {
let arg_value = $crate::__evaluate_arg!($($path).+ $(= $value)?);
$crate::__macros_impl::LogfireValue::new(
stringify!($($path).+),
$crate::__macros_impl::converter(&arg_value).convert_value(arg_value)
$crate::__macros_impl::converter(&arg_value)
.handle_type_inference()
.unpack_option(arg_value)
.and_then(|(value, converter)| {
converter.convert_value(value)
})
)
}),*
]
Expand Down
Loading