async def test_it_only_keeps_a_reference_to_the_loop_after_the_first_log_call( self): logger = Logger.with_default_handlers() self.assertIs(logger._loop, get_running_loop()) await logger.info("Xablau") self.assertIs(logger._loop, get_running_loop())
async def do_rollover(self): """ do a rollover; in this case, a date/time stamp is appended to the filename when the rollover happens. However, you want the file to be named for the start of the interval, not the current time. If there is a backup count, then we have to get a list of matching filenames, sort them and remove the one with the oldest suffix. """ if self.stream: await self.stream.close() self.stream = None # get the time that this sequence started at and make it a TimeTuple current_time = int(time.time()) dst_now = time.localtime(current_time)[-1] t = self.rollover_at - self.interval if self.utc: time_tuple = time.gmtime(t) else: time_tuple = time.localtime(t) dst_then = time_tuple[-1] if dst_now != dst_then: if dst_now: addend = ONE_HOUR_IN_SECONDS else: addend = -ONE_HOUR_IN_SECONDS time_tuple = time.localtime(t + addend) destination_file_path = self.rotation_filename( self.absolute_file_path + "." + time.strftime(self.suffix, time_tuple)) loop = get_running_loop() if await loop.run_in_executor( None, lambda: os.path.exists(destination_file_path)): await loop.run_in_executor( None, lambda: os.unlink(destination_file_path)) await self.rotate(self.absolute_file_path, destination_file_path) if self.backup_count > 0: files_to_delete = await self.get_files_to_delete() if files_to_delete: await self._delete_files(files_to_delete) await self._init_writer() new_rollover_at = self.compute_rollover(current_time) while new_rollover_at <= current_time: new_rollover_at = new_rollover_at + self.interval # If DST changes and midnight or weekly rollover, adjust for this. if (self.when == RolloverInterval.MIDNIGHT or self.when in RolloverInterval.WEEK_DAYS) and not self.utc: dst_at_rollover = time.localtime(new_rollover_at)[-1] if dst_now != dst_at_rollover: if not dst_now: # DST kicks in before next rollover, so we need to deduct an hour addend = -ONE_HOUR_IN_SECONDS else: # DST bows out before next rollover, so we need to add an hour addend = ONE_HOUR_IN_SECONDS new_rollover_at += addend self.rollover_at = new_rollover_at
async def _init_writer(self) -> StreamWriter: async with self._initialization_lock: if self.writer is not None: return self.writer loop = get_running_loop() transport, protocol = await loop.connect_write_pipe( self.protocol_class, self.stream) self.writer = StreamWriter( # type: ignore # https://github.com/python/typeshed/pull/2719 transport=transport, protocol=protocol, reader=None, loop=loop) return self.writer
async def get_files_to_delete(self) -> List[str]: """ Determine the files to delete when rolling over. """ dir_name, base_name = os.path.split(self.absolute_file_path) loop = get_running_loop() file_names = await loop.run_in_executor(None, lambda: os.listdir(dir_name)) result = [] prefix = base_name + "." plen = len(prefix) for file_name in file_names: if file_name[:plen] == prefix: suffix = file_name[plen:] if self.ext_match.match(suffix): result.append(os.path.join(dir_name, file_name)) if len(result) < self.backup_count: return [] else: result.sort(reverse=True) # os.listdir order is not defined return result[:len(result) - self.backup_count]
async def rotate(self, source: str, dest: str): """ When rotating, rotate the current log. The default implementation calls the 'rotator' attribute of the handler, if it's callable, passing the source and dest arguments to it. If the attribute isn't callable (the default is None), the source is simply renamed to the destination. :param source: The source filename. This is normally the base filename, e.g. 'test.log' :param dest: The destination filename. This is normally what the source is rotated to, e.g. 'test.log.1'. """ if self.rotator is None: # logging issue 18940: A file may not have been created if delay is True. loop = get_running_loop() if await loop.run_in_executor(None, lambda: os.path.exists(source)): await loop.run_in_executor( # type: ignore None, lambda: os.rename(source, dest)) else: self.rotator(source, dest)
async def _delete_files(self, file_paths: List[str]): loop = get_running_loop() delete_tasks = (loop.run_in_executor(None, lambda: os.unlink(file_path)) for file_path in file_paths) await asyncio.gather(*delete_tasks)