@@ -451,11 +451,11 @@ impl LogsHandle {
451451 service : & str ,
452452 zip : & mut zip:: ZipWriter < W > ,
453453 log_snapshots : & mut LogSnapshots ,
454- logfile : & Utf8Path ,
454+ logfile : & LogFile ,
455455 logtype : LogType ,
456456 ) -> Result < ( ) , LogError > {
457457 let snapshot_logfile =
458- self . find_log_in_snapshot ( log_snapshots, logfile) . await ?;
458+ self . find_log_in_snapshot ( log_snapshots, & logfile. path ) . await ?;
459459
460460 if logtype == LogType :: Current {
461461 // Since we are processing the current log files in a zone we need
@@ -501,13 +501,24 @@ impl LogsHandle {
501501 . filter ( |f| is_log_file ( f. path ( ) , filename) )
502502 {
503503 let logfile = f. path ( ) ;
504+ let system_mtime =
505+ f. metadata ( ) . and_then ( |m| m. modified ( ) ) . inspect_err ( |e| {
506+ warn ! ( & self . log, "sled-diagnostic failed to get mtime of logfile" ;
507+ "error" => %e,
508+ "logfile" => %logfile,
509+ ) ;
510+ } ) . ok ( ) ;
511+ let mtime = system_mtime
512+ . and_then ( |m| jiff:: Timestamp :: try_from ( m) . ok ( ) ) ;
513+
504514 if logfile. is_file ( ) {
505515 write_log_to_zip (
506516 & self . log ,
507517 service,
508518 zip,
509519 LogType :: Current ,
510520 logfile,
521+ mtime,
511522 ) ?;
512523 }
513524 }
@@ -522,6 +533,7 @@ impl LogsHandle {
522533 zip,
523534 logtype,
524535 & snapshot_logfile,
536+ logfile. modified ,
525537 ) ?;
526538 }
527539 false => {
@@ -612,7 +624,7 @@ impl LogsHandle {
612624 & service,
613625 & mut zip,
614626 & mut log_snapshots,
615- & current. path ,
627+ & current,
616628 LogType :: Current ,
617629 )
618630 . await ?;
@@ -628,13 +640,13 @@ impl LogsHandle {
628640 . archived
629641 . into_iter ( )
630642 . filter ( |log| log. path . as_str ( ) . contains ( "crypt/debug" ) )
631- . map ( |log| log. path )
632643 . collect ( ) ;
633644
634645 // Since these logs can be spread out across multiple U.2 devices
635646 // we need to sort them by timestamp.
636647 archived. sort_by_key ( |log| {
637- log. as_str ( )
648+ log. path
649+ . as_str ( )
638650 . rsplit_once ( "." )
639651 . and_then ( |( _, date) | date. parse :: < u64 > ( ) . ok ( ) )
640652 . unwrap_or ( 0 )
@@ -703,6 +715,7 @@ fn write_log_to_zip<W: Write + Seek>(
703715 zip : & mut zip:: ZipWriter < W > ,
704716 logtype : LogType ,
705717 snapshot_logfile : & Utf8Path ,
718+ mtime : Option < jiff:: Timestamp > ,
706719) -> Result < ( ) , LogError > {
707720 let Some ( log_name) = snapshot_logfile. file_name ( ) else {
708721 warn ! (
@@ -715,10 +728,19 @@ fn write_log_to_zip<W: Write + Seek>(
715728 } ;
716729
717730 let mut src = File :: open ( & snapshot_logfile) ?;
731+
732+ let zip_mtime = mtime
733+ . and_then ( |ts| {
734+ let zoned = ts. in_tz ( "UTC" ) . ok ( ) ?;
735+ zip:: DateTime :: try_from ( zoned. datetime ( ) ) . ok ( )
736+ } )
737+ . unwrap_or_else ( zip:: DateTime :: default) ;
738+
718739 let zip_path = format ! ( "{service}/{logtype}/{log_name}" ) ;
719740 zip. start_file_from_path (
720741 zip_path,
721742 FullFileOptions :: default ( )
743+ . last_modified_time ( zip_mtime)
722744 . compression_method ( zip:: CompressionMethod :: Zstd )
723745 . compression_level ( Some ( 3 ) )
724746 // NB: From the docs
@@ -757,8 +779,8 @@ enum ExtraLogKind<'a> {
757779
758780#[ derive( Debug , Default , PartialEq ) ]
759781struct ExtraLogs < ' a > {
760- current : Option < & ' a Utf8Path > ,
761- rotated : Vec < & ' a Utf8Path > ,
782+ current : Option < & ' a LogFile > ,
783+ rotated : Vec < & ' a LogFile > ,
762784}
763785
764786fn sort_extra_logs < ' a > (
@@ -780,15 +802,15 @@ fn sort_extra_logs<'a>(
780802 warn ! (
781803 logger,
782804 "found multiple current log files for {name}" ;
783- "old" => %old_path,
805+ "old" => %old_path. path ,
784806 "new" => %log. path,
785807 ) ;
786808 }
787- entry. current = Some ( & log. path ) ;
809+ entry. current = Some ( & log) ;
788810 }
789811 ExtraLogKind :: Rotated { name, log } => {
790812 let entry = res. entry ( name) . or_default ( ) ;
791- entry. rotated . push ( & log. path ) ;
813+ entry. rotated . push ( & log) ;
792814 }
793815 }
794816 }
@@ -838,9 +860,9 @@ fn sort_cockroach_extra_logs(logs: &[LogFile]) -> HashMap<&str, ExtraLogs<'_>> {
838860 let entry = interested. entry ( prefix) . or_default ( ) ;
839861
840862 if file_name == format ! ( "{prefix}.log" ) {
841- entry. current = Some ( log. path . as_path ( ) ) ;
863+ entry. current = Some ( log) ;
842864 } else {
843- entry. rotated . push ( log. path . as_path ( ) ) ;
865+ entry. rotated . push ( log) ;
844866 }
845867 }
846868 }
@@ -917,52 +939,30 @@ mod test {
917939 "bogus.log" ,
918940 "some/dir"
919941 ] . into_iter ( ) . map ( |l| {
920- oxlog:: LogFile { path : Utf8PathBuf :: from ( l) , size : None , modified : None }
921- } ) . collect ( ) ;
942+ oxlog:: LogFile { path : Utf8PathBuf :: from ( l) , size : None , modified : None }
943+ } ) . collect ( ) ;
944+ let logs_map: HashMap < _ , _ > =
945+ logs. iter ( ) . map ( |l| ( l. path . as_str ( ) , l) ) . collect ( ) ;
922946
923947 let mut expected: HashMap < & str , ExtraLogs < ' _ > > = HashMap :: new ( ) ;
924948
925949 // cockroach
926950 expected. entry ( "cockroach" ) . or_default ( ) . current =
927- Some ( Utf8Path :: new ( "cockroach.log" ) ) ;
928- expected
929- . entry ( "cockroach" )
930- . or_default ( )
931- . rotated
932- . push ( Utf8Path :: new ( "cockroach.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2025-01-31T17_11_45Z.011435.log" ) ) ;
933- expected
934- . entry ( "cockroach" )
935- . or_default ( )
936- . rotated
937- . push ( Utf8Path :: new ( "cockroach.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2025-02-01T01_51_51Z.011486.log" ) ) ;
951+ Some ( & logs_map[ "cockroach.log" ] ) ;
952+ expected. entry ( "cockroach" ) . or_default ( ) . rotated . push ( & logs_map[ "cockroach.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2025-01-31T17_11_45Z.011435.log" ] ) ;
953+ expected. entry ( "cockroach" ) . or_default ( ) . rotated . push ( & logs_map[ "cockroach.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2025-02-01T01_51_51Z.011486.log" ] ) ;
938954
939955 // cockroach-health
940956 expected. entry ( "cockroach-health" ) . or_default ( ) . current =
941- Some ( Utf8Path :: new ( "cockroach-health.log" ) ) ;
942- expected
943- . entry ( "cockroach-health" )
944- . or_default ( )
945- . rotated
946- . push ( Utf8Path :: new ( "cockroach-health.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2025-01-31T21_43_26Z.011435.log" ) ) ;
947- expected
948- . entry ( "cockroach-health" )
949- . or_default ( )
950- . rotated
951- . push ( Utf8Path :: new ( "cockroach-health.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2025-02-01T01_51_53Z.011486.log" ) ) ;
957+ Some ( & logs_map[ "cockroach-health.log" ] ) ;
958+ expected. entry ( "cockroach-health" ) . or_default ( ) . rotated . push ( & logs_map[ "cockroach-health.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2025-01-31T21_43_26Z.011435.log" ] ) ;
959+ expected. entry ( "cockroach-health" ) . or_default ( ) . rotated . push ( & logs_map[ "cockroach-health.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2025-02-01T01_51_53Z.011486.log" ] ) ;
952960
953961 // cockroach-stderr
954962 expected. entry ( "cockroach-stderr" ) . or_default ( ) . current =
955- Some ( Utf8Path :: new ( "cockroach-stderr.log" ) ) ;
956- expected
957- . entry ( "cockroach-stderr" )
958- . or_default ( )
959- . rotated
960- . push ( Utf8Path :: new ( "cockroach-stderr.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2023-08-30T18_56_19Z.011950.log" ) ) ;
961- expected
962- . entry ( "cockroach-stderr" )
963- . or_default ( )
964- . rotated
965- . push ( Utf8Path :: new ( "cockroach-stderr.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2023-08-31T02_59_24Z.010479.log" ) ) ;
963+ Some ( & logs_map[ "cockroach-stderr.log" ] ) ;
964+ expected. entry ( "cockroach-stderr" ) . or_default ( ) . rotated . push ( & logs_map[ "cockroach-stderr.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2023-08-30T18_56_19Z.011950.log" ] ) ;
965+ expected. entry ( "cockroach-stderr" ) . or_default ( ) . rotated . push ( & logs_map[ "cockroach-stderr.oxzcockroachdba3628a56-6f85-43b5-be50-71d8f0e04877.root.2023-08-31T02_59_24Z.010479.log" ] ) ;
966966
967967 let extra = sort_cockroach_extra_logs ( logs. as_slice ( ) ) ;
968968 assert_eq ! (
@@ -1177,6 +1177,12 @@ mod illumos_tests {
11771177 let mut logfile_handle =
11781178 fs_err:: tokio:: File :: create_new ( & logfile) . await . unwrap ( ) ;
11791179 logfile_handle. write_all ( data. as_bytes ( ) ) . await . unwrap ( ) ;
1180+
1181+ // 2025-10-15T00:00:00
1182+ let mtime = std:: time:: SystemTime :: UNIX_EPOCH
1183+ + std:: time:: Duration :: from_secs ( 1760486400 ) ;
1184+ let std_file = logfile_handle. into_std ( ) . await . into_file ( ) ;
1185+ std_file. set_modified ( mtime) . unwrap ( ) ;
11801186 }
11811187
11821188 // Populate some file with similar names that should be skipped over
@@ -1197,27 +1203,40 @@ mod illumos_tests {
11971203 let zipfile_path = mountpoint. join ( "test.zip" ) ;
11981204 let zipfile = File :: create_new ( & zipfile_path) . unwrap ( ) ;
11991205 let mut zip = ZipWriter :: new ( zipfile) ;
1206+ let log = LogFile {
1207+ path : mountpoint
1208+ . join ( format ! ( "var/svc/log/{}" , logfile_to_data[ 0 ] . 0 ) ) ,
1209+ size : None ,
1210+ modified : None ,
1211+ } ;
12001212
12011213 loghandle
12021214 . process_logs (
12031215 "mg-ddm" ,
12041216 & mut zip,
12051217 & mut log_snapshots,
1206- & mountpoint
1207- . join ( format ! ( "var/svc/log/{}" , logfile_to_data[ 0 ] . 0 ) ) ,
1218+ & log,
12081219 LogType :: Current ,
12091220 )
12101221 . await
12111222 . unwrap ( ) ;
12121223
12131224 zip. finish ( ) . unwrap ( ) ;
12141225
1215- // Confirm the zip has our file and data
1226+ let expected_zip_mtime =
1227+ zip:: DateTime :: from_date_and_time ( 2025 , 10 , 15 , 0 , 0 , 0 )
1228+ . unwrap ( ) ;
1229+
1230+ // Confirm the zip has our file and data with the right mtime
12161231 let mut archive =
12171232 ZipArchive :: new ( File :: open ( zipfile_path) . unwrap ( ) ) . unwrap ( ) ;
12181233 for ( name, data) in logfile_to_data {
12191234 let mut file_in_zip =
12201235 archive. by_name ( & format ! ( "mg-ddm/current/{name}" ) ) . unwrap ( ) ;
1236+
1237+ let mtime = file_in_zip. last_modified ( ) . unwrap ( ) ;
1238+ assert_eq ! ( mtime, expected_zip_mtime, "file mtime matches" ) ;
1239+
12211240 let mut contents = String :: new ( ) ;
12221241 file_in_zip. read_to_string ( & mut contents) . unwrap ( ) ;
12231242
@@ -1283,13 +1302,14 @@ mod illumos_tests {
12831302 let zipfile_path = mountpoint. join ( "test.zip" ) ;
12841303 let zipfile = File :: create_new ( & zipfile_path) . unwrap ( ) ;
12851304 let mut zip = ZipWriter :: new ( zipfile) ;
1305+ let log = LogFile { path : logfile, size : None , modified : None } ;
12861306
12871307 loghandle
12881308 . process_logs (
12891309 "mg-ddm" ,
12901310 & mut zip,
12911311 & mut log_snapshots,
1292- & logfile ,
1312+ & log ,
12931313 LogType :: Current ,
12941314 )
12951315 . await
0 commit comments