1use alloc::{format, string::String, vec::Vec};
2use core::{fmt, str};
3use smallvec::SmallVec;
4use tracing_core::{
5 Event, Level,
6 field::{Field, Visit},
7 span,
8};
9
10#[derive(Clone, Default, PartialEq, Eq)]
12pub struct InlineString(SmallVec<[u8; 24]>);
13
14impl InlineString {
15 pub fn as_str(&self) -> &str {
17 str::from_utf8(&self.0).expect("inline string must always contain valid utf-8")
18 }
19}
20
21impl From<&str> for InlineString {
22 fn from(value: &str) -> Self {
23 Self(SmallVec::from_slice(value.as_bytes()))
24 }
25}
26
27impl From<String> for InlineString {
28 fn from(value: String) -> Self {
29 Self(SmallVec::from_slice(value.as_bytes()))
30 }
31}
32
33impl fmt::Debug for InlineString {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 fmt::Debug::fmt(self.as_str(), f)
36 }
37}
38
39impl fmt::Display for InlineString {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 f.write_str(self.as_str())
42 }
43}
44
45#[derive(Clone, Debug, PartialEq)]
47pub enum FieldValue {
48 Bool(bool),
49 I64(i64),
50 U64(u64),
51 I128(i128),
52 U128(u128),
53 F64(f64),
54 Str(InlineString),
55 Bytes(Vec<u8>),
56 Debug(InlineString),
57}
58
59#[derive(Clone, Debug, PartialEq)]
61pub struct OwnedField {
62 pub name: &'static str,
63 pub value: FieldValue,
64}
65
66pub type OwnedFields = SmallVec<[OwnedField; 8]>;
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct Timestamp {
72 pub unix_seconds: u64,
74 pub subsec_nanos: u32,
76}
77
78impl Timestamp {
79 pub const fn new(unix_seconds: u64, subsec_nanos: u32) -> Self {
81 Self {
82 unix_seconds,
83 subsec_nanos,
84 }
85 }
86
87 pub const fn unix_nanos(&self) -> u128 {
89 (self.unix_seconds as u128) * 1_000_000_000 + self.subsec_nanos as u128
90 }
91}
92
93#[derive(Clone, Debug, PartialEq)]
95pub struct SpanSnapshot {
96 pub id: u64,
97 pub metadata_id: u32,
98 pub name: &'static str,
99 pub target: &'static str,
100 pub level: Level,
101 pub fields: OwnedFields,
102}
103
104#[derive(Clone, Debug, PartialEq)]
106pub struct OwnedRecord {
107 pub timestamp: Timestamp,
108 pub metadata_id: u32,
109 pub name: &'static str,
110 pub target: &'static str,
111 pub level: Level,
112 pub fields: OwnedFields,
113 pub current_span: Option<SpanSnapshot>,
114 pub spans: SmallVec<[SpanSnapshot; 4]>,
115}
116
117impl OwnedRecord {
118 pub fn from_event(
120 timestamp: Timestamp,
121 metadata_id: u32,
122 event: &Event<'_>,
123 current_span: Option<SpanSnapshot>,
124 spans: SmallVec<[SpanSnapshot; 4]>,
125 ) -> Self {
126 let mut capture = FieldCapture::default();
127 event.record(&mut capture);
128 let metadata = event.metadata();
129 Self {
130 timestamp,
131 metadata_id,
132 name: metadata.name(),
133 target: metadata.target(),
134 level: *metadata.level(),
135 fields: capture.finish(),
136 current_span,
137 spans,
138 }
139 }
140}
141
142#[derive(Default)]
144pub struct FieldCapture {
145 fields: OwnedFields,
146}
147
148impl FieldCapture {
149 pub fn finish(self) -> OwnedFields {
151 self.fields
152 }
153
154 fn push(&mut self, field: &Field, value: FieldValue) {
155 self.fields.push(OwnedField {
156 name: field.name(),
157 value,
158 });
159 }
160}
161
162impl Visit for FieldCapture {
163 fn record_f64(&mut self, field: &Field, value: f64) {
164 self.push(field, FieldValue::F64(value));
165 }
166
167 fn record_i64(&mut self, field: &Field, value: i64) {
168 self.push(field, FieldValue::I64(value));
169 }
170
171 fn record_u64(&mut self, field: &Field, value: u64) {
172 self.push(field, FieldValue::U64(value));
173 }
174
175 fn record_i128(&mut self, field: &Field, value: i128) {
176 self.push(field, FieldValue::I128(value));
177 }
178
179 fn record_u128(&mut self, field: &Field, value: u128) {
180 self.push(field, FieldValue::U128(value));
181 }
182
183 fn record_bool(&mut self, field: &Field, value: bool) {
184 self.push(field, FieldValue::Bool(value));
185 }
186
187 fn record_str(&mut self, field: &Field, value: &str) {
188 self.push(field, FieldValue::Str(value.into()));
189 }
190
191 fn record_bytes(&mut self, field: &Field, value: &[u8]) {
192 self.push(field, FieldValue::Bytes(value.to_vec()));
193 }
194
195 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
196 self.push(field, FieldValue::Debug(format!("{value:?}").into()));
197 }
198}
199
200pub fn capture_span_fields(attributes: &span::Attributes<'_>) -> OwnedFields {
202 let mut capture = FieldCapture::default();
203 attributes.record(&mut capture);
204 capture.finish()
205}
206
207pub fn capture_record_fields(values: &span::Record<'_>) -> OwnedFields {
209 let mut capture = FieldCapture::default();
210 values.record(&mut capture);
211 capture.finish()
212}
213
214pub fn merge_fields(existing: &mut OwnedFields, updates: OwnedFields) {
216 for update in updates {
217 if let Some(slot) = existing.iter_mut().find(|field| field.name == update.name) {
218 slot.value = update.value;
219 } else {
220 existing.push(update);
221 }
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use smallvec::smallvec;
229
230 #[test]
231 fn merge_fields_overwrites_existing_keys() {
232 let mut fields = smallvec![OwnedField {
233 name: "message",
234 value: FieldValue::Str("before".into()),
235 }];
236 merge_fields(
237 &mut fields,
238 smallvec![OwnedField {
239 name: "message",
240 value: FieldValue::Str("after".into()),
241 }],
242 );
243
244 assert_eq!(fields.len(), 1);
245 assert_eq!(fields[0].value, FieldValue::Str("after".into()));
246 }
247}