2828import requests
2929import serial
3030import yaml
31+
32+ import log_schema
3133from aind_auto_train .schema .task import TrainingStage
3234from aind_behavior_services .session import AindBehaviorSessionModel
3335from aind_data_schema .core .session import Session
@@ -351,6 +353,9 @@ def __init__(self, parent=None, box_number=1, start_bonsai_ide=True):
351353
352354 # load the rig metadata
353355 self ._load_rig_metadata ()
356+
357+ # setup life-cycle logger
358+ self .lifecycle_logger = self .setup_lifecycle_logger ()
354359
355360 # Initializes session log handler as None
356361 self .session_log_handler = None
@@ -365,6 +370,27 @@ def __init__(self, parent=None, box_number=1, start_bonsai_ide=True):
365370 self ._ReconnectBonsai ()
366371 logging .info ("Start up complete" )
367372
373+ def setup_lifecycle_logger (self ) -> logging .Logger :
374+
375+ """
376+ Creates logger for start, stop, and failure events with formatter adhering to aind log standards.
377+ """
378+
379+ # Ensure the directory exists
380+ os .makedirs (Path (self .Settings ["lifecycle_log_dir" ]), exist_ok = True )
381+
382+ lifecycle_logger = logging .getLogger ("lifecycle" )
383+ lifecycle_logger .setLevel (logging .INFO )
384+
385+ timestamp = datetime .now ().strftime ("%Y%m%dT%H%M%SZ" )
386+ filename = f"lifecycle_log_{ timestamp } .jsonl"
387+ file_handler = logging .FileHandler (os .path .join (self .Settings ["lifecycle_log_dir" ], filename ), encoding = "utf-8" )
388+ file_handler .setLevel (logging .INFO )
389+ file_handler .setFormatter (log_schema .DefaultFormatter ())
390+ lifecycle_logger .addHandler (file_handler )
391+
392+ return lifecycle_logger
393+
368394 def _load_rig_metadata (self ):
369395 """Load the latest rig metadata"""
370396
@@ -1594,6 +1620,7 @@ def _restartlogging(self, log_folder=None,start_from_camera=False):
15941620 self .Save .setStyleSheet (
15951621 "color: white;background-color : mediumorchid"
15961622 )
1623+
15971624 else :
15981625 # temporary logging
15991626 loggingtype = 1
@@ -1961,6 +1988,11 @@ def _GetSettings(self):
19611988 "aind_watchdog_service" ,
19621989 "manifest" ,
19631990 ),
1991+ "lifecycle_log_dir" : os .path .join (
1992+ os .path .expanduser ("~" ),
1993+ "Documents" ,
1994+ "lifecycle_logs" ,
1995+ ),
19641996 "transfer_service_job_type" : "dynamic_foraging_compression" ,
19651997 "auto_engage" : True ,
19661998 "clear_figure_after_save" : True ,
@@ -3937,7 +3969,7 @@ def _Save(self, ForceSave=0, SaveAs=0, SaveContinue=0, BackupSave=0):
39373969 if self .CreateNewFolder == 1 :
39383970 self ._GetSaveFolder ()
39393971 self .CreateNewFolder = 0
3940-
3972+
39413973 if not os .path .exists (os .path .dirname (self .SaveFileJson )):
39423974 os .makedirs (os .path .dirname (self .SaveFileJson ))
39433975 logging .info (
@@ -4225,6 +4257,12 @@ def _Save(self, ForceSave=0, SaveAs=0, SaveContinue=0, BackupSave=0):
42254257 elif session is None :
42264258 logging .warning (f"Waterlog for mouse { self .behavior_session_model .subject } cannot be added to slims"
42274259 f" due do metadata generation failure." )
4260+
4261+ # add complete log to lifecycle
4262+ self .lifecycle_logger .info ("Session ended." , extra = {"subject_id" : self .behavior_session_model .subject ,
4263+ "acquisition_name" : self .behavior_session_model .session_name ,
4264+ "event_type" : "stage_complete" })
4265+
42284266 except Exception as e :
42294267 logging .warning (
42304268 "Meta data is not saved!" ,
@@ -6186,6 +6224,10 @@ def _Start(self):
61866224 # Start logging if the formal logging is not started
61876225 if self .logging_type != 0 :
61886226 self .Ot_log_folder = self ._restartlogging ()
6227+ # Need to log start event after session_name has been set in_restartlogging
6228+ self .lifecycle_logger .info ("Session started." , extra = {"subject_id" : self .behavior_session_model .subject ,
6229+ "acquisition_name" : self .behavior_session_model .session_name ,
6230+ "event_type" : "stage_start" })
61896231 except Exception as e :
61906232 if "ConnectionAbortedError" in str (e ):
61916233 logging .info ("lost bonsai connection: restartlogging()" )
@@ -6641,6 +6683,9 @@ def _StartTrialLoop(self, GeneratedTrials, worker1, worker_save):
66416683 self .ANewTrial = 1
66426684 self .Start .setChecked (False )
66436685 self .Start .setStyleSheet ("background-color : none" )
6686+ self .lifecycle_logger .info ("Session failed." , extra = {"subject_id" : self .behavior_session_model .subject ,
6687+ "acquisition_name" : self .behavior_session_model .session_name ,
6688+ "event_type" : "stage_failure" })
66446689 break
66456690 # receive licks and update figures
66466691 if self .actionDrawing_after_stopping .isChecked () == False :
@@ -7432,7 +7477,6 @@ def setup_loki_logging(box_number):
74327477 handler .setLevel (logging .INFO )
74337478 logger .root .addHandler (handler )
74347479
7435-
74367480def start_gui_log_file (box_number ):
74377481 """
74387482 Starts a log file for the gui.
0 commit comments