@@ -24,11 +24,11 @@ use std::{
24
24
iter:: FromIterator ,
25
25
path:: { Path , PathBuf } ,
26
26
} ;
27
- use tauri:: Emitter ;
28
27
use tauri:: {
29
28
plugin:: { self , TauriPlugin } ,
30
29
Manager , Runtime ,
31
30
} ;
31
+ use tauri:: { AppHandle , Emitter } ;
32
32
33
33
pub use fern;
34
34
use time:: OffsetDateTime ;
@@ -75,6 +75,18 @@ const DEFAULT_LOG_TARGETS: [Target; 2] = [
75
75
Target :: new ( TargetKind :: LogDir { file_name : None } ) ,
76
76
] ;
77
77
78
+ #[ derive( Debug , thiserror:: Error ) ]
79
+ pub enum Error {
80
+ #[ error( transparent) ]
81
+ Tauri ( #[ from] tauri:: Error ) ,
82
+ #[ error( transparent) ]
83
+ Io ( #[ from] std:: io:: Error ) ,
84
+ #[ error( transparent) ]
85
+ TimeFormat ( #[ from] time:: error:: Format ) ,
86
+ #[ error( transparent) ]
87
+ InvalidFormatDescription ( #[ from] time:: error:: InvalidFormatDescription ) ,
88
+ }
89
+
78
90
/// An enum representing the available verbosity levels of the logger.
79
91
///
80
92
/// It is very similar to the [`log::Level`], but serializes to unsigned ints instead of strings.
@@ -395,111 +407,158 @@ impl Builder {
395
407
} )
396
408
}
397
409
398
- pub fn build < R : Runtime > ( mut self ) -> TauriPlugin < R > {
399
- plugin:: Builder :: new ( "log" )
400
- . invoke_handler ( tauri:: generate_handler![ log] )
401
- . setup ( move |app_handle, _api| {
402
- let app_name = & app_handle. package_info ( ) . name ;
410
+ fn acquire_logger < R : Runtime > (
411
+ app_handle : & AppHandle < R > ,
412
+ mut dispatch : fern:: Dispatch ,
413
+ rotation_strategy : RotationStrategy ,
414
+ timezone_strategy : TimezoneStrategy ,
415
+ max_file_size : u128 ,
416
+ targets : Vec < Target > ,
417
+ ) -> Result < ( log:: LevelFilter , Box < dyn log:: Log > ) , Error > {
418
+ let app_name = & app_handle. package_info ( ) . name ;
419
+
420
+ // setup targets
421
+ for target in targets {
422
+ let mut target_dispatch = fern:: Dispatch :: new ( ) ;
423
+ for filter in target. filters {
424
+ target_dispatch = target_dispatch. filter ( filter) ;
425
+ }
403
426
404
- // setup targets
405
- for target in self . targets {
406
- let mut target_dispatch = fern:: Dispatch :: new ( ) ;
407
- for filter in target. filters {
408
- target_dispatch = target_dispatch. filter ( filter) ;
427
+ let logger = match target. kind {
428
+ #[ cfg( target_os = "android" ) ]
429
+ TargetKind :: Stdout | TargetKind :: Stderr => fern:: Output :: call ( android_logger:: log) ,
430
+ #[ cfg( target_os = "ios" ) ]
431
+ TargetKind :: Stdout | TargetKind :: Stderr => fern:: Output :: call ( move |record| {
432
+ let message = format ! ( "{}" , record. args( ) ) ;
433
+ unsafe {
434
+ ios:: tauri_log (
435
+ match record. level ( ) {
436
+ log:: Level :: Trace | log:: Level :: Debug => 1 ,
437
+ log:: Level :: Info => 2 ,
438
+ log:: Level :: Warn | log:: Level :: Error => 3 ,
439
+ } ,
440
+ ios:: NSString :: new ( message. as_str ( ) ) . 0 as _ ,
441
+ ) ;
442
+ }
443
+ } ) ,
444
+ #[ cfg( desktop) ]
445
+ TargetKind :: Stdout => std:: io:: stdout ( ) . into ( ) ,
446
+ #[ cfg( desktop) ]
447
+ TargetKind :: Stderr => std:: io:: stderr ( ) . into ( ) ,
448
+ TargetKind :: Folder { path, file_name } => {
449
+ if !path. exists ( ) {
450
+ fs:: create_dir_all ( & path) ?;
409
451
}
410
452
411
- let logger = match target. kind {
412
- #[ cfg( target_os = "android" ) ]
413
- TargetKind :: Stdout | TargetKind :: Stderr => {
414
- fern:: Output :: call ( android_logger:: log)
415
- }
416
- #[ cfg( target_os = "ios" ) ]
417
- TargetKind :: Stdout | TargetKind :: Stderr => {
418
- fern:: Output :: call ( move |record| {
419
- let message = format ! ( "{}" , record. args( ) ) ;
420
- unsafe {
421
- ios:: tauri_log (
422
- match record. level ( ) {
423
- log:: Level :: Trace | log:: Level :: Debug => 1 ,
424
- log:: Level :: Info => 2 ,
425
- log:: Level :: Warn | log:: Level :: Error => 3 ,
426
- } ,
427
- ios:: NSString :: new ( message. as_str ( ) ) . 0 as _ ,
428
- ) ;
429
- }
430
- } )
431
- }
432
- #[ cfg( desktop) ]
433
- TargetKind :: Stdout => std:: io:: stdout ( ) . into ( ) ,
434
- #[ cfg( desktop) ]
435
- TargetKind :: Stderr => std:: io:: stderr ( ) . into ( ) ,
436
- TargetKind :: Folder { path, file_name } => {
437
- if !path. exists ( ) {
438
- fs:: create_dir_all ( & path) ?;
439
- }
440
-
441
- fern:: log_file ( get_log_file_path (
442
- & path,
443
- file_name. as_deref ( ) . unwrap_or ( app_name) ,
444
- & self . rotation_strategy ,
445
- & self . timezone_strategy ,
446
- self . max_file_size ,
447
- ) ?) ?
448
- . into ( )
449
- }
450
- #[ cfg( mobile) ]
451
- TargetKind :: LogDir { .. } => continue ,
452
- #[ cfg( desktop) ]
453
- TargetKind :: LogDir { file_name } => {
454
- let path = app_handle. path ( ) . app_log_dir ( ) ?;
455
- if !path. exists ( ) {
456
- fs:: create_dir_all ( & path) ?;
457
- }
458
-
459
- fern:: log_file ( get_log_file_path (
460
- & path,
461
- file_name. as_deref ( ) . unwrap_or ( app_name) ,
462
- & self . rotation_strategy ,
463
- & self . timezone_strategy ,
464
- self . max_file_size ,
465
- ) ?) ?
466
- . into ( )
467
- }
468
- TargetKind :: Webview => {
469
- let app_handle = app_handle. clone ( ) ;
470
-
471
- fern:: Output :: call ( move |record| {
472
- let payload = RecordPayload {
473
- message : record. args ( ) . to_string ( ) ,
474
- level : record. level ( ) . into ( ) ,
475
- } ;
476
- let app_handle = app_handle. clone ( ) ;
477
- tauri:: async_runtime:: spawn ( async move {
478
- let _ = app_handle. emit ( "log://log" , payload) ;
479
- } ) ;
480
- } )
481
- }
482
- } ;
483
- target_dispatch = target_dispatch. chain ( logger) ;
484
-
485
- self . dispatch = self . dispatch . chain ( target_dispatch) ;
453
+ fern:: log_file ( get_log_file_path (
454
+ & path,
455
+ file_name. as_deref ( ) . unwrap_or ( app_name) ,
456
+ & rotation_strategy,
457
+ & timezone_strategy,
458
+ max_file_size,
459
+ ) ?) ?
460
+ . into ( )
486
461
}
462
+ #[ cfg( mobile) ]
463
+ TargetKind :: LogDir { .. } => continue ,
464
+ #[ cfg( desktop) ]
465
+ TargetKind :: LogDir { file_name } => {
466
+ let path = app_handle. path ( ) . app_log_dir ( ) ?;
467
+ if !path. exists ( ) {
468
+ fs:: create_dir_all ( & path) ?;
469
+ }
487
470
488
- self . dispatch . apply ( ) ?;
471
+ fern:: log_file ( get_log_file_path (
472
+ & path,
473
+ file_name. as_deref ( ) . unwrap_or ( app_name) ,
474
+ & rotation_strategy,
475
+ & timezone_strategy,
476
+ max_file_size,
477
+ ) ?) ?
478
+ . into ( )
479
+ }
480
+ TargetKind :: Webview => {
481
+ let app_handle = app_handle. clone ( ) ;
482
+
483
+ fern:: Output :: call ( move |record| {
484
+ let payload = RecordPayload {
485
+ message : record. args ( ) . to_string ( ) ,
486
+ level : record. level ( ) . into ( ) ,
487
+ } ;
488
+ let app_handle = app_handle. clone ( ) ;
489
+ tauri:: async_runtime:: spawn ( async move {
490
+ let _ = app_handle. emit ( "log://log" , payload) ;
491
+ } ) ;
492
+ } )
493
+ }
494
+ } ;
495
+ target_dispatch = target_dispatch. chain ( logger) ;
496
+
497
+ dispatch = dispatch. chain ( target_dispatch) ;
498
+ }
499
+
500
+ Ok ( dispatch. into_log ( ) )
501
+ }
502
+
503
+ fn plugin_builder < R : Runtime > ( ) -> plugin:: Builder < R > {
504
+ plugin:: Builder :: new ( "log" ) . invoke_handler ( tauri:: generate_handler![ log] )
505
+ }
506
+
507
+ #[ allow( clippy:: type_complexity) ]
508
+ pub fn split < R : Runtime > (
509
+ self ,
510
+ app_handle : & AppHandle < R > ,
511
+ ) -> Result < ( TauriPlugin < R > , log:: LevelFilter , Box < dyn log:: Log > ) , Error > {
512
+ let plugin = Self :: plugin_builder ( ) ;
513
+ let ( max_level, log) = Self :: acquire_logger (
514
+ app_handle,
515
+ self . dispatch ,
516
+ self . rotation_strategy ,
517
+ self . timezone_strategy ,
518
+ self . max_file_size ,
519
+ self . targets ,
520
+ ) ?;
521
+
522
+ Ok ( ( plugin. build ( ) , max_level, log) )
523
+ }
524
+
525
+ pub fn build < R : Runtime > ( self ) -> TauriPlugin < R > {
526
+ Self :: plugin_builder ( )
527
+ . setup ( move |app_handle, _api| {
528
+ let ( max_level, log) = Self :: acquire_logger (
529
+ app_handle,
530
+ self . dispatch ,
531
+ self . rotation_strategy ,
532
+ self . timezone_strategy ,
533
+ self . max_file_size ,
534
+ self . targets ,
535
+ ) ?;
536
+
537
+ attach_logger ( max_level, log) ?;
489
538
490
539
Ok ( ( ) )
491
540
} )
492
541
. build ( )
493
542
}
494
543
}
495
544
545
+ /// Attaches the given logger
546
+ pub fn attach_logger (
547
+ max_level : log:: LevelFilter ,
548
+ log : Box < dyn log:: Log > ,
549
+ ) -> Result < ( ) , log:: SetLoggerError > {
550
+ log:: set_boxed_logger ( log) ?;
551
+ log:: set_max_level ( max_level) ;
552
+ Ok ( ( ) )
553
+ }
554
+
496
555
fn get_log_file_path (
497
556
dir : & impl AsRef < Path > ,
498
557
file_name : & str ,
499
558
rotation_strategy : & RotationStrategy ,
500
559
timezone_strategy : & TimezoneStrategy ,
501
560
max_file_size : u128 ,
502
- ) -> Result < PathBuf , Box < dyn std :: error :: Error > > {
561
+ ) -> Result < PathBuf , Error > {
503
562
let path = dir. as_ref ( ) . join ( format ! ( "{file_name}.log" ) ) ;
504
563
505
564
if path. exists ( ) {
0 commit comments