1use std::time::{Duration, Instant};
6use tracing::{span, Level, Span};
7use tracing_subscriber::{
8 fmt::{self, format::FmtSpan},
9 layer::SubscriberExt,
10 util::SubscriberInitExt,
11 EnvFilter, Registry,
12};
13
14#[cfg(feature = "otel")]
15use opentelemetry::trace::TracerProvider;
16#[cfg(feature = "otel")]
17use opentelemetry_otlp::WithExportConfig;
18#[cfg(feature = "otel")]
19use opentelemetry_sdk::{runtime, Resource};
20#[cfg(feature = "otel")]
21use tracing_opentelemetry::OpenTelemetryLayer;
22
23pub struct OperationTracker {
27 span: Span,
28 start_time: Instant,
29 #[allow(dead_code)]
30 operation_name: String,
31}
32
33impl OperationTracker {
34 pub fn start(service_name: &str, operation_name: &str) -> Self {
36 let span = span!(
37 Level::INFO,
38 "service_operation",
39 service = service_name,
40 operation = operation_name,
41 duration_ms = tracing::field::Empty,
42 status = tracing::field::Empty,
43 );
44
45 {
46 let _enter = span.enter();
47 tracing::debug!("Starting operation");
48 }
49
50 Self {
51 span,
52 start_time: Instant::now(),
53 operation_name: operation_name.to_string(),
54 }
55 }
56
57 pub fn success(self) {
59 let duration = self.start_time.elapsed();
60 let duration_ms = duration.as_millis() as u64;
61
62 self.span.record("duration_ms", duration_ms);
63 self.span.record("status", "success");
64
65 let _enter = self.span.enter();
66 tracing::info!("Operation completed successfully");
67 }
68
69 pub fn error(self, error: &str) {
71 let duration = self.start_time.elapsed();
72 let duration_ms = duration.as_millis() as u64;
73
74 self.span.record("duration_ms", duration_ms);
75 self.span.record("status", "error");
76
77 let _enter = self.span.enter();
78 tracing::error!(error = error, "Operation failed");
79 }
80
81 pub fn span(&self) -> &Span {
83 &self.span
84 }
85
86 pub fn elapsed(&self) -> Duration {
88 self.start_time.elapsed()
89 }
90}
91
92pub fn init_tracing() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
94 init_tracing_with_filter("info")
95}
96
97pub fn init_tracing_with_filter(
99 filter: &str,
100) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
101 let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(filter));
102
103 let fmt_layer = fmt::layer()
104 .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE)
105 .with_target(true)
106 .with_thread_ids(true)
107 .with_thread_names(true);
108
109 Registry::default()
110 .with(env_filter)
111 .with(fmt_layer)
112 .try_init()?;
113
114 Ok(())
115}
116
117pub fn init_structured_tracing() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
119 let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
120
121 let json_layer = fmt::layer()
122 .json()
123 .with_current_span(true)
124 .with_span_list(true);
125
126 Registry::default()
127 .with(env_filter)
128 .with(json_layer)
129 .try_init()?;
130
131 Ok(())
132}
133
134#[cfg(feature = "otel")]
136#[derive(Debug, Clone)]
137pub struct OtelConfig {
138 pub endpoint: String,
140 pub service_name: String,
142 pub service_version: String,
144 pub environment: String,
146}
147
148#[cfg(feature = "otel")]
149impl Default for OtelConfig {
150 fn default() -> Self {
151 Self {
152 endpoint: "http://localhost:4317".to_string(),
153 service_name: "open-lark-sdk".to_string(),
154 service_version: env!("CARGO_PKG_VERSION").to_string(),
155 environment: "development".to_string(),
156 }
157 }
158}
159
160#[cfg(feature = "otel")]
162pub fn init_otel_tracing(
163 config: Option<OtelConfig>,
164) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
165 let config = config.unwrap_or_default();
166
167 let tracer_provider = opentelemetry_otlp::new_pipeline()
169 .tracing()
170 .with_exporter(
171 opentelemetry_otlp::new_exporter()
172 .tonic()
173 .with_endpoint(&config.endpoint),
174 )
175 .with_trace_config(opentelemetry_sdk::trace::Config::default().with_resource(
176 Resource::new(vec![
177 opentelemetry::KeyValue::new("service.name", config.service_name),
178 opentelemetry::KeyValue::new("service.version", config.service_version),
179 opentelemetry::KeyValue::new("environment", config.environment),
180 ]),
181 ))
182 .install_batch(runtime::Tokio)?;
183
184 opentelemetry::global::set_tracer_provider(tracer_provider.clone());
186
187 let tracer = tracer_provider.tracer("open-lark-sdk");
189
190 let otel_layer = OpenTelemetryLayer::new(tracer);
192
193 let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
195
196 let fmt_layer = fmt::layer()
198 .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE)
199 .with_target(true)
200 .with_thread_ids(true);
201
202 Registry::default()
204 .with(env_filter)
205 .with(fmt_layer)
206 .with(otel_layer)
207 .try_init()?;
208
209 Ok(())
210}
211
212#[cfg(feature = "otel")]
214pub fn shutdown_otel() {
215 opentelemetry::global::shutdown_tracer_provider();
216}
217
218pub struct HttpTracker {
220 span: Span,
221 start_time: Instant,
222}
223
224pub struct AuthTracker {
228 span: Span,
229 start_time: Instant,
230}
231
232impl AuthTracker {
233 pub fn start(operation_type: &str, app_id: &str, token_type: &str) -> Self {
235 let span = span!(
236 Level::INFO,
237 "auth_operation",
238 operation = operation_type,
239 app_id = app_id,
240 token_type = token_type,
241 cache_hit = tracing::field::Empty,
242 duration_ms = tracing::field::Empty,
243 success = tracing::field::Empty,
244 error_code = tracing::field::Empty,
245 );
246
247 {
248 let _enter = span.enter();
249 tracing::debug!("Starting authentication operation");
250 }
251
252 Self {
253 span,
254 start_time: Instant::now(),
255 }
256 }
257
258 pub fn success(self, cache_hit: bool) {
260 let duration = self.start_time.elapsed();
261 let duration_ms = duration.as_millis() as u64;
262
263 self.span.record("duration_ms", duration_ms);
264 self.span.record("success", true);
265 self.span.record("cache_hit", cache_hit);
266
267 let _enter = self.span.enter();
268 tracing::info!("Authentication operation completed successfully");
269 }
270
271 pub fn error(self, error: &str, error_code: Option<i32>) {
273 let duration = self.start_time.elapsed();
274 let duration_ms = duration.as_millis() as u64;
275
276 self.span.record("duration_ms", duration_ms);
277 self.span.record("success", false);
278
279 if let Some(code) = error_code {
280 self.span.record("error_code", code);
281 }
282
283 let _enter = self.span.enter();
284 tracing::error!(error = error, "Authentication operation failed");
285 }
286
287 pub fn span(&self) -> &Span {
289 &self.span
290 }
291}
292
293pub struct ResponseTracker {
297 span: Span,
298 start_time: Instant,
299}
300
301impl ResponseTracker {
302 pub fn start(response_format: &str, response_size: Option<u64>) -> Self {
304 let span = span!(
305 Level::DEBUG,
306 "response_processing",
307 format = response_format,
308 input_size = response_size.unwrap_or(0),
309 parsing_duration_ms = tracing::field::Empty,
310 validation_duration_ms = tracing::field::Empty,
311 total_duration_ms = tracing::field::Empty,
312 success = tracing::field::Empty,
313 );
314
315 {
316 let _enter = span.enter();
317 tracing::debug!("Starting response processing");
318 }
319
320 Self {
321 span,
322 start_time: Instant::now(),
323 }
324 }
325
326 pub fn parsing_complete(&self) {
328 let parsing_duration = self.start_time.elapsed();
329 let parsing_duration_ms = parsing_duration.as_millis() as u64;
330 self.span.record("parsing_duration_ms", parsing_duration_ms);
331 }
332
333 pub fn validation_complete(&self) {
335 let total_duration = self.start_time.elapsed();
336 let validation_duration_ms = total_duration.as_millis() as u64;
337 self.span
338 .record("validation_duration_ms", validation_duration_ms);
339 }
340
341 pub fn success(self) {
343 let duration = self.start_time.elapsed();
344 let duration_ms = duration.as_millis() as u64;
345
346 self.span.record("total_duration_ms", duration_ms);
347 self.span.record("success", true);
348
349 let _enter = self.span.enter();
350 tracing::debug!("Response processing completed successfully");
351 }
352
353 pub fn error(self, error: &str) {
355 let duration = self.start_time.elapsed();
356 let duration_ms = duration.as_millis() as u64;
357
358 self.span.record("total_duration_ms", duration_ms);
359 self.span.record("success", false);
360
361 let _enter = self.span.enter();
362 tracing::error!(error = error, "Response processing failed");
363 }
364}
365
366impl HttpTracker {
367 pub fn start(method: &str, url: &str) -> Self {
369 let span = span!(
370 Level::INFO,
371 "http_request",
372 method = method,
373 url = url,
374 status_code = tracing::field::Empty,
375 duration_ms = tracing::field::Empty,
376 response_size = tracing::field::Empty,
377 );
378
379 {
380 let _enter = span.enter();
381 tracing::debug!("Sending HTTP request");
382 }
383
384 Self {
385 span,
386 start_time: Instant::now(),
387 }
388 }
389
390 pub fn response(self, status_code: u16, response_size: Option<u64>) {
392 let duration = self.start_time.elapsed();
393 let duration_ms = duration.as_millis() as u64;
394
395 self.span.record("status_code", status_code);
396 self.span.record("duration_ms", duration_ms);
397
398 if let Some(size) = response_size {
399 self.span.record("response_size", size);
400 }
401
402 let _enter = self.span.enter();
403 if (200..300).contains(&status_code) {
404 tracing::info!("HTTP request completed successfully");
405 } else if status_code >= 400 {
406 tracing::warn!("HTTP request failed");
407 } else {
408 tracing::info!("HTTP request completed");
409 }
410 }
411
412 pub fn error(self, error: &str) {
414 let duration = self.start_time.elapsed();
415 let duration_ms = duration.as_millis() as u64;
416
417 self.span.record("duration_ms", duration_ms);
418
419 let _enter = self.span.enter();
420 tracing::error!(error = error, "HTTP request failed");
421 }
422}
423
424#[macro_export]
428macro_rules! trace_performance {
429 ($name:expr, $code:block) => {{
430 let _tracker = $crate::core::observability::OperationTracker::start("performance", $name);
431 let result = $code;
432 _tracker.success();
433 result
434 }};
435 ($service:expr, $operation:expr, $code:block) => {{
436 let _tracker = $crate::core::observability::OperationTracker::start($service, $operation);
437 let result = $code;
438 _tracker.success();
439 result
440 }};
441}
442
443#[macro_export]
445macro_rules! trace_async_performance {
446 ($name:expr, $code:expr) => {{
447 let tracker = $crate::core::observability::OperationTracker::start("performance", $name);
448 match $code.await {
449 Ok(result) => {
450 tracker.success();
451 Ok(result)
452 }
453 Err(err) => {
454 tracker.error(&err.to_string());
455 Err(err)
456 }
457 }
458 }};
459 ($service:expr, $operation:expr, $code:expr) => {{
460 let tracker = $crate::core::observability::OperationTracker::start($service, $operation);
461 match $code.await {
462 Ok(result) => {
463 tracker.success();
464 Ok(result)
465 }
466 Err(err) => {
467 tracker.error(&err.to_string());
468 Err(err)
469 }
470 }
471 }};
472}
473
474#[macro_export]
478macro_rules! trace_auth_operation {
479 ($operation:expr, $app_id:expr, $token_type:expr, $code:expr) => {
480 async move {
481 let tracker =
482 $crate::core::observability::AuthTracker::start($operation, $app_id, $token_type);
483 match $code.await {
484 Ok((result, cache_hit)) => {
485 tracker.success(cache_hit);
486 Ok(result)
487 }
488 Err(err) => {
489 let error_code =
491 if let $crate::core::error::LarkAPIError::APIError { code, .. } = &err {
492 Some(*code)
493 } else {
494 None
495 };
496 tracker.error(&err.to_string(), error_code);
497 Err(err)
498 }
499 }
500 }
501 };
502}
503
504#[macro_export]
508macro_rules! trace_http_request {
509 ($method:expr, $url:expr, $code:expr) => {
510 async move {
511 let tracker = $crate::core::observability::HttpTracker::start($method, $url);
512 match $code.await {
513 Ok(response) => {
514 let status_code = if let Ok(status) = response.status() {
515 status.as_u16()
516 } else {
517 0
518 };
519 tracker.response(status_code, None);
520 Ok(response)
521 }
522 Err(err) => {
523 tracker.error(&err.to_string());
524 Err(err)
525 }
526 }
527 }
528 };
529}
530
531#[macro_export]
535macro_rules! trace_response_processing {
536 ($format:expr, $size:expr, $parsing:expr, $validation:expr) => {{
537 let tracker = $crate::core::observability::ResponseTracker::start($format, $size);
538
539 let parsed_result = $parsing;
541 tracker.parsing_complete();
542
543 match parsed_result {
544 Ok(parsed_data) => {
545 match $validation(parsed_data) {
547 Ok(validated_data) => {
548 tracker.validation_complete();
549 tracker.success();
550 Ok(validated_data)
551 }
552 Err(err) => {
553 tracker.error(&err.to_string());
554 Err(err)
555 }
556 }
557 }
558 Err(err) => {
559 tracker.error(&err.to_string());
560 Err(err)
561 }
562 }
563 }};
564}
565
566pub fn trace_health_check<F, T>(service_name: &str, check_fn: F) -> T
568where
569 F: FnOnce() -> T,
570{
571 let tracker = OperationTracker::start(service_name, "health_check");
572 let result = check_fn();
573 tracker.success();
574 result
575}
576
577pub async fn trace_async_health_check<F, Fut, T, E>(service_name: &str, check_fn: F) -> Result<T, E>
579where
580 F: FnOnce() -> Fut,
581 Fut: std::future::Future<Output = Result<T, E>>,
582 E: std::fmt::Display,
583{
584 let tracker = OperationTracker::start(service_name, "health_check");
585 match check_fn().await {
586 Ok(result) => {
587 tracker.success();
588 Ok(result)
589 }
590 Err(err) => {
591 tracker.error(&err.to_string());
592 Err(err)
593 }
594 }
595}
596
597#[cfg(test)]
598mod tests {
599 use super::*;
600 use std::time::Duration;
601 use tracing_test::traced_test;
602
603 #[traced_test]
604 #[test]
605 fn test_operation_tracker_success() {
606 let tracker = OperationTracker::start("test_service", "test_operation");
607 std::thread::sleep(Duration::from_millis(10));
608
609 assert!(tracker.elapsed() >= Duration::from_millis(10));
611
612 tracker.success();
613
614 assert!(logs_contain("Starting operation"));
616 assert!(logs_contain("Operation completed successfully"));
617 }
618
619 #[traced_test]
620 #[test]
621 fn test_operation_tracker_error() {
622 let tracker = OperationTracker::start("test_service", "test_operation");
623 tracker.error("Test error message");
624
625 assert!(logs_contain("Operation failed"));
627 assert!(logs_contain("Test error message"));
628 }
629
630 #[traced_test]
631 #[test]
632 fn test_http_tracker() {
633 let tracker = HttpTracker::start("GET", "https://api.example.com/test");
634 tracker.response(200, Some(1024));
635
636 assert!(logs_contain("Sending HTTP request"));
638 assert!(logs_contain("HTTP request completed successfully"));
639 }
640
641 #[traced_test]
642 #[test]
643 fn test_performance_macro() {
644 let result = trace_performance!("test_perf", {
645 std::thread::sleep(Duration::from_millis(5));
646 42
647 });
648
649 assert_eq!(result, 42);
650 assert!(logs_contain("Starting operation"));
651 assert!(logs_contain("Operation completed successfully"));
652 }
653
654 #[traced_test]
655 #[tokio::test]
656 async fn test_async_performance_macro() {
657 let result: Result<i32, &str> =
658 trace_async_performance!("test_service", "async_op", async {
659 tokio::time::sleep(Duration::from_millis(5)).await;
660 Ok::<i32, &str>(42)
661 });
662
663 assert_eq!(result.unwrap(), 42);
664 assert!(logs_contain("Starting operation"));
665 assert!(logs_contain("Operation completed successfully"));
666 }
667
668 #[traced_test]
669 #[test]
670 fn test_trace_health_check() {
671 let result = trace_health_check("test_service", || "healthy");
672
673 assert_eq!(result, "healthy");
674 assert!(logs_contain("Starting operation"));
675 assert!(logs_contain("Operation completed successfully"));
676 }
677
678 #[traced_test]
679 #[tokio::test]
680 async fn test_trace_async_health_check_success() {
681 let result =
682 trace_async_health_check("test_service", || async { Ok::<&str, &str>("healthy") })
683 .await;
684
685 assert_eq!(result.unwrap(), "healthy");
686 assert!(logs_contain("Starting operation"));
687 assert!(logs_contain("Operation completed successfully"));
688 }
689
690 #[traced_test]
691 #[tokio::test]
692 async fn test_trace_async_health_check_error() {
693 let result =
694 trace_async_health_check("test_service", || async { Err::<&str, &str>("unhealthy") })
695 .await;
696
697 assert!(result.is_err());
698 assert!(logs_contain("Starting operation"));
699 assert!(logs_contain("Operation failed"));
700 assert!(logs_contain("unhealthy"));
701 }
702
703 #[traced_test]
704 #[test]
705 fn test_auth_tracker_success() {
706 let tracker = AuthTracker::start("get_token", "test_app", "tenant");
707 std::thread::sleep(Duration::from_millis(10));
708
709 tracker.success(true);
711
712 assert!(logs_contain("Starting authentication operation"));
714 assert!(logs_contain(
715 "Authentication operation completed successfully"
716 ));
717 }
718
719 #[traced_test]
720 #[test]
721 fn test_auth_tracker_error() {
722 let tracker = AuthTracker::start("refresh_token", "test_app", "user");
723 tracker.error("Invalid credentials", Some(401));
724
725 assert!(logs_contain("Starting authentication operation"));
727 assert!(logs_contain("Authentication operation failed"));
728 assert!(logs_contain("Invalid credentials"));
729 }
730
731 #[traced_test]
732 #[test]
733 fn test_response_tracker() {
734 let tracker = ResponseTracker::start("json", Some(1024));
735 std::thread::sleep(Duration::from_millis(5));
736
737 tracker.parsing_complete();
739 std::thread::sleep(Duration::from_millis(3));
740
741 tracker.validation_complete();
743
744 tracker.success();
746
747 assert!(logs_contain("Starting response processing"));
749 assert!(logs_contain("Response processing completed successfully"));
750 }
751
752 #[traced_test]
753 #[test]
754 fn test_response_tracker_error() {
755 let tracker = ResponseTracker::start("xml", Some(512));
756 tracker.error("Parse error: invalid XML structure");
757
758 assert!(logs_contain("Starting response processing"));
760 assert!(logs_contain("Response processing failed"));
761 assert!(logs_contain("Parse error: invalid XML structure"));
762 }
763
764 #[traced_test]
765 #[tokio::test]
766 async fn test_trace_auth_operation_macro() {
767 use crate::core::error::LarkAPIError;
768
769 let result = trace_auth_operation!("get_app_token", "test_app", "app", async {
771 Ok::<(String, bool), LarkAPIError>(("token_value".to_string(), true))
772 })
773 .await;
774
775 assert!(result.is_ok());
776 assert!(logs_contain("Starting authentication operation"));
777 assert!(logs_contain(
778 "Authentication operation completed successfully"
779 ));
780 }
781
782 #[traced_test]
783 #[tokio::test]
784 async fn test_trace_response_processing_macro() {
785 let format = "json";
787 let size = Some(256_u64);
788
789 let parsing_fn = || Ok::<String, std::io::Error>("parsed_data".to_string());
790 let validation_fn = |data: String| {
791 if data.is_empty() {
792 Err(std::io::Error::new(
793 std::io::ErrorKind::InvalidData,
794 "Empty data",
795 ))
796 } else {
797 Ok(data)
798 }
799 };
800
801 let result = trace_response_processing!(format, size, parsing_fn(), validation_fn);
802
803 assert!(result.is_ok());
804 assert_eq!(result.unwrap(), "parsed_data");
805 assert!(logs_contain("Starting response processing"));
806 assert!(logs_contain("Response processing completed successfully"));
807 }
808
809 #[traced_test]
812 #[test]
813 fn test_operation_tracker_span_access() {
814 let tracker = OperationTracker::start("test_service", "test_operation");
815
816 let span = tracker.span();
818 assert!(!span.is_disabled());
819
820 let elapsed = tracker.elapsed();
822 assert!(elapsed >= Duration::from_nanos(0));
823
824 tracker.success();
825 }
826
827 #[traced_test]
828 #[test]
829 fn test_auth_tracker_span_access() {
830 let tracker = AuthTracker::start("get_token", "test_app", "tenant");
831
832 let span = tracker.span();
834 assert!(!span.is_disabled());
835
836 tracker.success(false); assert!(logs_contain("Starting authentication operation"));
839 assert!(logs_contain(
840 "Authentication operation completed successfully"
841 ));
842 }
843
844 #[traced_test]
845 #[test]
846 fn test_auth_tracker_error_without_code() {
847 let tracker = AuthTracker::start("refresh_token", "test_app", "user");
848 tracker.error("Network timeout", None); assert!(logs_contain("Authentication operation failed"));
851 assert!(logs_contain("Network timeout"));
852 }
853
854 #[traced_test]
855 #[test]
856 fn test_http_tracker_different_status_codes() {
857 let tracker1 = HttpTracker::start("GET", "https://api.example.com/users");
859 tracker1.response(201, Some(512));
860 assert!(logs_contain("HTTP request completed successfully"));
861
862 let tracker2 = HttpTracker::start("GET", "https://api.example.com/redirect");
864 tracker2.response(302, None);
865 assert!(logs_contain("HTTP request completed"));
866
867 let tracker3 = HttpTracker::start("POST", "https://api.example.com/invalid");
869 tracker3.response(404, Some(128));
870 assert!(logs_contain("HTTP request failed"));
871
872 let tracker4 = HttpTracker::start("PUT", "https://api.example.com/error");
874 tracker4.response(500, Some(256));
875 assert!(logs_contain("HTTP request failed"));
876 }
877
878 #[traced_test]
879 #[test]
880 fn test_http_tracker_network_error() {
881 let tracker = HttpTracker::start("GET", "https://api.example.com/timeout");
882 tracker.error("Connection timeout after 30 seconds");
883
884 assert!(logs_contain("Sending HTTP request"));
885 assert!(logs_contain("HTTP request failed"));
886 assert!(logs_contain("Connection timeout after 30 seconds"));
887 }
888
889 #[traced_test]
890 #[test]
891 fn test_response_tracker_with_none_size() {
892 let tracker = ResponseTracker::start("xml", None);
893
894 tracker.parsing_complete();
896 tracker.success();
897
898 assert!(logs_contain("Starting response processing"));
899 assert!(logs_contain("Response processing completed successfully"));
900 }
901
902 #[traced_test]
903 #[test]
904 fn test_response_tracker_validation_timing() {
905 let tracker = ResponseTracker::start("binary", Some(2048));
906
907 std::thread::sleep(Duration::from_millis(2));
909 tracker.parsing_complete();
910
911 std::thread::sleep(Duration::from_millis(3));
912 tracker.validation_complete();
913
914 std::thread::sleep(Duration::from_millis(1));
915 tracker.success();
916
917 assert!(logs_contain("Starting response processing"));
918 assert!(logs_contain("Response processing completed successfully"));
919 }
920
921 #[traced_test]
922 #[tokio::test]
923 async fn test_async_performance_macro_error() {
924 let result: Result<i32, &str> =
925 trace_async_performance!("test_service", "failing_op", async {
926 tokio::time::sleep(Duration::from_millis(2)).await;
927 Err::<i32, &str>("Operation failed")
928 });
929
930 assert!(result.is_err());
931 assert_eq!(result.unwrap_err(), "Operation failed");
932 assert!(logs_contain("Starting operation"));
933 assert!(logs_contain("Operation failed"));
934 }
935
936 #[traced_test]
937 #[test]
938 fn test_performance_macro_with_service_and_operation() {
939 let result = trace_performance!("my_service", "complex_operation", {
940 std::thread::sleep(Duration::from_millis(1));
941 "result"
942 });
943
944 assert_eq!(result, "result");
945 assert!(logs_contain("Starting operation"));
946 assert!(logs_contain("Operation completed successfully"));
947 }
948
949 #[traced_test]
950 #[tokio::test]
951 async fn test_trace_auth_operation_macro_error() {
952 use crate::core::error::LarkAPIError;
953
954 let result = trace_auth_operation!("get_tenant_token", "test_app", "tenant", async {
956 Err::<(String, bool), LarkAPIError>(LarkAPIError::APIError {
957 code: 10001,
958 msg: "App not found".to_string(),
959 error: None,
960 })
961 })
962 .await;
963
964 assert!(result.is_err());
965 assert!(logs_contain("Starting authentication operation"));
966 assert!(logs_contain("Authentication operation failed"));
967 assert!(logs_contain("App not found"));
968 }
969
970 #[traced_test]
971 #[tokio::test]
972 async fn test_trace_auth_operation_macro_non_api_error() {
973 use crate::core::error::LarkAPIError;
974
975 let result = trace_auth_operation!("validate_token", "test_app", "user", async {
977 Err::<(String, bool), LarkAPIError>(LarkAPIError::RequestError(
978 "Network connection failed".to_string(),
979 ))
980 })
981 .await;
982
983 assert!(result.is_err());
984 assert!(logs_contain("Starting authentication operation"));
985 assert!(logs_contain("Authentication operation failed"));
986 assert!(logs_contain("Network connection failed"));
987 }
988
989 #[traced_test]
990 #[test]
991 fn test_trace_response_processing_macro_parsing_error() {
992 let format = "yaml";
993 let size = Some(128_u64);
994
995 let parsing_fn = || {
996 Err::<String, std::io::Error>(std::io::Error::new(
997 std::io::ErrorKind::InvalidData,
998 "Invalid YAML syntax",
999 ))
1000 };
1001 let validation_fn = |data: String| Ok::<String, std::io::Error>(data);
1002
1003 let result = trace_response_processing!(format, size, parsing_fn(), validation_fn);
1004
1005 assert!(result.is_err());
1006 assert!(logs_contain("Starting response processing"));
1007 assert!(logs_contain("Response processing failed"));
1008 assert!(logs_contain("Invalid YAML syntax"));
1009 }
1010
1011 #[traced_test]
1012 #[test]
1013 fn test_trace_response_processing_macro_validation_error() {
1014 let format = "csv";
1015 let size = Some(64_u64);
1016
1017 let parsing_fn = || Ok::<String, std::io::Error>("".to_string()); let validation_fn = |data: String| {
1019 if data.is_empty() {
1020 Err(std::io::Error::new(
1021 std::io::ErrorKind::InvalidData,
1022 "CSV data cannot be empty",
1023 ))
1024 } else {
1025 Ok(data)
1026 }
1027 };
1028
1029 let result = trace_response_processing!(format, size, parsing_fn(), validation_fn);
1030
1031 assert!(result.is_err());
1032 assert!(logs_contain("Starting response processing"));
1033 assert!(logs_contain("Response processing failed"));
1034 assert!(logs_contain("CSV data cannot be empty"));
1035 }
1036
1037 #[test]
1038 fn test_init_tracing_functions() {
1039 let result1 = init_tracing();
1045 let result2 = init_tracing_with_filter("debug");
1046 let result3 = init_structured_tracing();
1047
1048 assert!(result1.is_ok() || result1.is_err()); assert!(result2.is_ok() || result2.is_err());
1052 assert!(result3.is_ok() || result3.is_err());
1053 }
1054
1055 #[cfg(feature = "otel")]
1056 #[test]
1057 fn test_otel_config_default() {
1058 let config = OtelConfig::default();
1059
1060 assert_eq!(config.endpoint, "http://localhost:4317");
1061 assert_eq!(config.service_name, "open-lark-sdk");
1062 assert_eq!(config.service_version, env!("CARGO_PKG_VERSION"));
1063 assert_eq!(config.environment, "development");
1064 }
1065
1066 #[cfg(feature = "otel")]
1067 #[test]
1068 fn test_otel_config_custom() {
1069 let config = OtelConfig {
1070 endpoint: "https://otel.example.com:4317".to_string(),
1071 service_name: "custom-service".to_string(),
1072 service_version: "1.2.3".to_string(),
1073 environment: "production".to_string(),
1074 };
1075
1076 assert_eq!(config.endpoint, "https://otel.example.com:4317");
1077 assert_eq!(config.service_name, "custom-service");
1078 assert_eq!(config.service_version, "1.2.3");
1079 assert_eq!(config.environment, "production");
1080 }
1081
1082 #[cfg(feature = "otel")]
1083 #[test]
1084 fn test_otel_config_clone_and_debug() {
1085 let config = OtelConfig::default();
1086 let cloned_config = config.clone();
1087
1088 assert_eq!(config.endpoint, cloned_config.endpoint);
1089 assert_eq!(config.service_name, cloned_config.service_name);
1090
1091 let debug_str = format!("{:?}", config);
1092 assert!(debug_str.contains("OtelConfig"));
1093 assert!(debug_str.contains("endpoint"));
1094 assert!(debug_str.contains("service_name"));
1095 }
1096
1097 #[traced_test]
1102 #[test]
1103 fn test_operation_tracker_zero_elapsed_time() {
1104 let tracker = OperationTracker::start("instant_service", "instant_operation");
1105 let elapsed = tracker.elapsed();
1107 tracker.success();
1108
1109 assert!(elapsed >= Duration::from_nanos(0));
1111 assert!(logs_contain("Starting operation"));
1112 assert!(logs_contain("Operation completed successfully"));
1113 }
1114
1115 #[traced_test]
1116 #[test]
1117 fn test_multiple_trackers_concurrent() {
1118 let tracker1 = OperationTracker::start("service1", "operation1");
1119 let tracker2 = AuthTracker::start("auth_op", "app1", "tenant");
1120 let tracker3 = HttpTracker::start("POST", "https://example.com/api");
1121 let tracker4 = ResponseTracker::start("json", Some(1024));
1122
1123 tracker2.success(true);
1125 tracker4.success();
1126 tracker1.success();
1127 tracker3.response(200, Some(512));
1128
1129 assert!(logs_contain("Starting operation"));
1131 assert!(logs_contain("Starting authentication operation"));
1132 assert!(logs_contain("Sending HTTP request"));
1133 assert!(logs_contain("Starting response processing"));
1134 }
1135
1136 #[traced_test]
1137 #[test]
1138 fn test_edge_case_empty_strings() {
1139 let tracker1 = OperationTracker::start("", "");
1140 tracker1.success();
1141
1142 let tracker2 = AuthTracker::start("", "", "");
1143 tracker2.error("", None);
1144
1145 let tracker3 = HttpTracker::start("", "");
1146 tracker3.error("");
1147
1148 let tracker4 = ResponseTracker::start("", Some(0));
1149 tracker4.error("");
1150
1151 assert!(logs_contain("Starting operation"));
1153 assert!(logs_contain("Starting authentication operation"));
1154 assert!(logs_contain("Sending HTTP request"));
1155 assert!(logs_contain("Starting response processing"));
1156 }
1157}