1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4 parse::{Parse, ParseStream},
5 parse_macro_input, Expr, Ident, LitStr, Token,
6};
7
8struct LogInput {
9 kvs: Vec<(Ident, Expr)>,
10 message: Option<LitStr>,
11}
12
13impl Parse for LogInput {
14 fn parse(input: ParseStream) -> syn::Result<Self> {
15 let mut kvs = Vec::new();
16 let mut message = None;
17
18 while !input.is_empty() {
19 if input.peek(LitStr) {
20 if message.is_some() {
21 return Err(input.error("multiple message strings not allowed"));
22 }
23 message = Some(input.parse()?);
24 } else {
25 let key: Ident = input.parse()?;
26 input.parse::<Token![=]>()?;
27 let value: Expr = input.parse()?;
28 kvs.push((key, value));
29 }
30
31 if input.peek(Token![,]) {
32 input.parse::<Token![,]>()?;
33 }
34 }
35
36 Ok(LogInput { kvs, message })
37 }
38}
39
40fn generate_log_call(level: u8, parsed: LogInput) -> TokenStream {
41 let thread_id_expr = quote! {
42 ttlog::utils::current_thread_id_u32() as u8
43 };
44
45 let common_constants = quote! {
46 const LEVEL: u8 = #level;
47 const MODULE: &str = module_path!();
48 const FILE: &str = file!();
49 const POSITION: (u32, u32) = (line!(), column!());
50 };
51
52 let common_statics = quote! {
53 static TARGET_ID: std::sync::OnceLock<u16> = std::sync::OnceLock::new();
54 static FILE_ID: std::sync::OnceLock<u16> = std::sync::OnceLock::new();
55 };
56
57 match (parsed.message, parsed.kvs.is_empty()) {
59 (Some(message), true) => quote! {
61 {
62 #common_constants
63 const MESSAGE: &str = #message;
64
65 #common_statics
66 static MESSAGE_ID: std::sync::OnceLock<u16> = std::sync::OnceLock::new();
67
68 if let Some(logger) = ttlog::trace::GLOBAL_LOGGER.get() {
69 if LEVEL >= logger.level.load(std::sync::atomic::Ordering::Relaxed) {
70 let target_id = *TARGET_ID.get_or_init(|| logger.interner.intern_target(MODULE));
71 let message_id = *MESSAGE_ID.get_or_init(|| logger.interner.intern_message(MESSAGE));
72 let file_id = *FILE_ID.get_or_init(|| logger.interner.intern_file(FILE));
73
74 logger.send_event_fast(
75 LEVEL,
76 target_id,
77 std::num::NonZeroU16::new(message_id),
78 #thread_id_expr,
79 file_id,
80 POSITION,
81 None,
82 );
83 }
84 }
85 }
86 },
87
88 (Some(message), false) => {
90 let kv_keys: Vec<_> = parsed.kvs.iter().map(|(k, _)| k).collect();
91 let kv_values = parsed.kvs.iter().map(|(_, v)| v);
92 let num_kvs = parsed.kvs.len();
93
94 quote! {
95 {
96 #common_constants
97 const MESSAGE: &str = #message;
98 const NUM_VALUES: usize = #num_kvs;
99
100 #common_statics
101 static MESSAGE_ID: std::sync::OnceLock<u16> = std::sync::OnceLock::new();
102
103 if let Some(logger) = ttlog::trace::GLOBAL_LOGGER.get() {
104 if LEVEL >= logger.level.load(std::sync::atomic::Ordering::Relaxed) {
105 let mut buf = ttlog::kv::KvTransformer::with_capacity(128);
106 {
107 use serde::ser::{SerializeMap, Serializer};
108 let mut ser = serde_json::Serializer::new(&mut buf);
109 let mut map = ser.serialize_map(Some(NUM_VALUES)).unwrap();
110 #({
111 let wrapper = ttlog::kv::IntOrSer(&#kv_values);
112 map.serialize_entry(stringify!(#kv_keys), &wrapper).unwrap();
113 })*
114 map.end().unwrap();
115 }
116
117 let target_id = *TARGET_ID.get_or_init(|| logger.interner.intern_target(MODULE));
118 let file_id = *FILE_ID.get_or_init(|| logger.interner.intern_file(FILE));
119 let message_id = *MESSAGE_ID.get_or_init(|| logger.interner.intern_message(MESSAGE));
120 let kv_id = logger.interner.intern_kv(buf.into_inner());
121
122 logger.send_event_fast(
123 LEVEL,
124 target_id,
125 std::num::NonZeroU16::new(message_id),
126 #thread_id_expr,
127 file_id,
128 POSITION,
129 std::num::NonZeroU16::new(kv_id),
130 );
131 }
132 }
133 }
134 }
135 },
136
137 (None, false) => {
139 let kv_keys: Vec<_> = parsed.kvs.iter().map(|(k, _)| k).collect();
140 let kv_values = parsed.kvs.iter().map(|(_, v)| v);
141 let num_kvs = parsed.kvs.len();
142
143 quote! {
144 {
145 #common_constants
146 const NUM_VALUES: usize = #num_kvs;
147
148 #common_statics
149
150 if let Some(logger) = ttlog::trace::GLOBAL_LOGGER.get() {
151 if LEVEL >= logger.level.load(std::sync::atomic::Ordering::Relaxed) {
152 let mut buf = ttlog::kv::KvTransformer::with_capacity(128);
153 {
154 use serde::ser::{SerializeMap, Serializer};
155 let mut ser = serde_json::Serializer::new(&mut buf);
156 let mut map = ser.serialize_map(Some(NUM_VALUES)).unwrap();
157 #({
158 let wrapper = ttlog::kv::IntOrSer(&#kv_values);
159 map.serialize_entry(stringify!(#kv_keys), &wrapper).unwrap();
160 })*
161 map.end().unwrap();
162 }
163
164 let kv_id = logger.interner.intern_kv(buf.into_inner());
165 let target_id = *TARGET_ID.get_or_init(|| logger.interner.intern_target(MODULE));
166 let file_id = *FILE_ID.get_or_init(|| logger.interner.intern_file(FILE));
167
168 logger.send_event_fast(
169 LEVEL,
170 target_id,
171 None,
172 #thread_id_expr,
173 file_id,
174 POSITION,
175 std::num::NonZeroU16::new(kv_id),
176 );
177 }
178 }
179 }
180 }
181 },
182
183 (None, true) => quote! {
185 {
186 #common_constants
187 #common_statics
188
189 if let Some(logger) = ttlog::trace::GLOBAL_LOGGER.get() {
190 if LEVEL >= logger.level.load(std::sync::atomic::Ordering::Relaxed) {
191 let target_id = *TARGET_ID.get_or_init(|| logger.interner.intern_target(MODULE));
192 let file_id = *FILE_ID.get_or_init(|| logger.interner.intern_file(FILE));
193
194 logger.send_event_fast(
195 LEVEL,
196 target_id,
197 None,
198 #thread_id_expr,
199 file_id,
200 POSITION,
201 None,
202 );
203 }
204 }
205 }
206 },
207 }
208 .into()
209}
210
211#[proc_macro]
212pub fn trace(input: TokenStream) -> TokenStream {
213 generate_log_call(0, parse_macro_input!(input as LogInput))
214}
215#[proc_macro]
216pub fn debug(input: TokenStream) -> TokenStream {
217 generate_log_call(1, parse_macro_input!(input as LogInput))
218}
219#[proc_macro]
220pub fn info(input: TokenStream) -> TokenStream {
221 generate_log_call(2, parse_macro_input!(input as LogInput))
222}
223#[proc_macro]
224pub fn warn(input: TokenStream) -> TokenStream {
225 generate_log_call(3, parse_macro_input!(input as LogInput))
226}
227#[proc_macro]
228pub fn error(input: TokenStream) -> TokenStream {
229 generate_log_call(4, parse_macro_input!(input as LogInput))
230}
231#[proc_macro]
232pub fn fatal(input: TokenStream) -> TokenStream {
233 generate_log_call(5, parse_macro_input!(input as LogInput))
234}