2# +==== BEGIN rotary_logger =================+
4# ..........####...####..........
5# ......###.....#.#########......
6# ....##........#.###########....
7# ...#..........#.############...
8# ...#..........#.#####.######...
9# ..#.....##....#.###..#...####..
10# .#.....#.##...#.##..##########.
11# #.....##########....##...######
12# #.....#...##..#.##..####.######
13# .#...##....##.#.##..###..#####.
14# ..#.##......#.#.####...######..
15# ..#...........#.#############..
16# ..#...........#.#############..
17# ...##.........#.############...
18# ......#.......#.#########......
19# .......#......#.########.......
20# .........#####...#####.........
22# PROJECT: rotary_logger
23# FILE: file_instance.py
24# CREATION DATE: 30-10-2025
25# LAST Modified: 10:44:15 27-03-2026
27# A module that provides a universal python light on iops way of logging to files your program execution.
29# COPYRIGHT: (c) Asperguide
30# PURPOSE: This is the file containing the class that is in charge of handling writing and rotating files regardless of the number of external processes calling it
32# +==== END rotary_logger =================+
37from datetime
import datetime, timezone
38from pathlib
import Path
39from typing
import Optional, Union, Dict, List
40from threading
import RLock
41from warnings
import warn
44 from .
import constants
as CONST
45 from .rogger
import Rogger, RI
47 import constants
as CONST
48 from rogger
import Rogger, RI
52 """Manage buffered writes, file descriptors, and log rotation.
54 Public methods are thread-safe and documented below. Writes are
55 appended to an in-memory buffer and flushed to disk when the
56 configured flush size is reached. Rotation is performed when the
57 underlying file grows beyond `max_size`.
62 file_path: Optional[Union[str, Path, CONST.FileInfo]],
63 override: Optional[bool] =
None,
64 merged: Optional[bool] =
None,
65 encoding: Optional[str] =
None,
66 prefix: Optional[CONST.Prefix] =
None,
68 max_size_mb: Optional[int] =
None,
69 flush_size_kb: Optional[int] =
None,
70 folder_prefix: Optional[CONST.StdMode] =
None,
71 log_to_file: bool =
True,
72 merge_stdin: Optional[bool] =
None,
74 """Create a FileInstance wrapper.
77 file_path (Optional[Union[str, Path, CONST.FileInfo]]): Initial file path, Path or FileInfo, or None to defer opening.
80 override (Optional[bool]): When True open files for write ('w') instead of append ('a'). Default: None
81 merged (Optional[bool]): Whether multiple streams should share the same file. Default: None
82 encoding (Optional[str]): Text encoding to use for file I/O. Default: None
83 prefix (Optional[CONST.Prefix]): Optional Prefix configuration to use when teeing. Default: None
84 max_size_mb (Optional[int]): Maximum logfile size in MB before rotation. Default: None
85 flush_size_kb (Optional[int]): Buffer flush threshold in KB. Default: None
86 folder_prefix (Optional[CONST.StdMode]): StdMode used to segregate per-stream subfolders. Default: None
87 log_to_file (bool): Whether file logging is enabled. Default: True
88 merge_stdin (Optional[bool]): Whether stdin is merged into the shared log file. Default: None
101 self.
prefix: Optional[CONST.Prefix] =
None
102 self.
max_size: int = CONST.DEFAULT_LOG_MAX_FILE_SIZE
107 if override
is not None:
109 if merged
is not None:
111 if merge_stdin
is not None:
113 if encoding
is not None:
115 if prefix
is not None:
117 if max_size_mb
is not None:
119 if flush_size_kb
is not None:
121 if folder_prefix
is not None:
123 if file_path
is not None:
127 f
"Initialized FileInstance with file_path={file_path}, override={override}, merged={merged}, encoding={encoding}, prefix={prefix}, max_size_mb={max_size_mb}, flush_size_kb={flush_size_kb}, folder_prefix={folder_prefix}, log_to_file={log_to_file}, merge_stdin={merge_stdin}",
128 stream=CONST.RAW_STDOUT
130 except (AttributeError, OSError, ValueError):
134 """Best-effort cleanup on object deletion.
136 Attempt to close the current file descriptor if it exists and is
137 open. This method must never raise during interpreter shutdown
138 (where `__del__` may be called), so IO-related errors are
139 swallowed. After attempting to close the descriptor the internal
140 `file` reference is cleared.
143 if not self.
filefile.descriptor.closed:
152 """Enable or disable file logging.
154 When `log_to_file` is False no data will be written to disk even
155 if a file descriptor is open.
158 log_to_file (bool): Whether writes to the log file are enabled. Default: True
161 lock (bool): When True the instance lock is acquired while updating the flag. Default: True
168 f
"set_log_to_file -> {log_to_file}",
169 stream=CONST.RAW_STDOUT
171 except (AttributeError, OSError, ValueError):
177 f
"set_log_to_file -> {log_to_file}",
178 stream=CONST.RAW_STDOUT
180 except (AttributeError, OSError, ValueError):
183 def set_max_size(self, max_size_mb: int, *, lock: bool =
True) ->
None:
184 """Public wrapper to set the maximum logfile size.
186 Delegates to `_set_max_size` which normalises the value to bytes
187 and applies range checks.
190 max_size_mb (int): Maximum size in megabytes. The value is normalised by `_set_max_size` and stored in `self.max_size` as bytes.
193 lock (bool): When True the instance lock is acquired while updating the configuration. Default: True
200 f
"set_max_size -> {max_size_mb}MB",
201 stream=CONST.RAW_STDOUT
203 except (AttributeError, OSError, ValueError):
209 f
"set_max_size -> {max_size_mb}MB",
210 stream=CONST.RAW_STDOUT
212 except (AttributeError, OSError, ValueError):
215 def set_folder_prefix(self, folder_prefix: Optional[CONST.StdMode], *, lock: bool =
True) ->
None:
216 """Public setter for `folder_prefix`.
218 When `lock` is True the instance lock is held while the value is
219 updated. The internal method `_set_folder_prefix` performs the
220 validation and assignment.
229 """Public wrapper to configure the buffer flush threshold.
231 `flush_size` is provided as a KB-like value and normalised by
232 `_set_flush_size`. If `lock` is True the operation is
233 performed while holding the instance lock.
240 f
"set_flush_size -> {flush_size}KB",
241 stream=CONST.RAW_STDOUT
243 except (AttributeError, OSError, ValueError):
249 f
"set_flush_size -> {flush_size}KB",
250 stream=CONST.RAW_STDOUT
252 except (AttributeError, OSError, ValueError):
255 def set_merged(self, merged: bool, *, lock: bool =
True) ->
None:
256 """Enable or disable stream merging.
258 When `merged` is True multiple streams may share a single log
259 file; when False separate per-stream files are used. The
260 `lock` parameter controls whether the instance lock is
265 self.
merged = bool(merged)
268 f
"set_merged -> {bool(merged)}",
269 stream=CONST.RAW_STDOUT
271 except (AttributeError, OSError, ValueError):
274 self.
merged = bool(merged)
277 f
"set_merged -> {bool(merged)}",
278 stream=CONST.RAW_STDOUT
280 except (AttributeError, OSError, ValueError):
284 """Enable or disable stdin merging into the shared log file.
286 When `merge_stdin` is True stdin data is written to the same file
287 as stdout/stderr; when False stdin is kept separate or not logged.
288 The `lock` parameter controls whether the instance lock is acquired.
291 merge_stdin (bool): Whether stdin should be merged into the shared log file.
294 lock (bool): When True the instance lock is acquired while updating the flag. Default: True
301 f
"set_merge_stdin -> {bool(merge_stdin)}",
302 stream=CONST.RAW_STDOUT
304 except (AttributeError, OSError, ValueError):
310 f
"set_merge_stdin -> {bool(merge_stdin)}",
311 stream=CONST.RAW_STDOUT
313 except (AttributeError, OSError, ValueError):
317 """Set the text encoding used for file I/O.
320 encoding (str): A codec name such as 'utf-8'.
323 lock (bool): When True the change is performed while holding the instance lock. Default: True
330 f
"set_encoding -> {encoding}",
331 stream=CONST.RAW_STDOUT
333 except (AttributeError, OSError, ValueError):
339 f
"set_encoding -> {encoding}",
340 stream=CONST.RAW_STDOUT
342 except (AttributeError, OSError, ValueError):
345 def set_prefix(self, prefix: Optional[CONST.Prefix], *, lock: bool =
True) ->
None:
346 """Public setter for `Prefix` configuration.
348 Copies the provided `prefix` into an internal `CONST.Prefix`
349 object. Use `get_prefix()` to obtain a safe copy of the
350 current configuration.
357 f
"set_prefix -> {prefix}",
358 stream=CONST.RAW_STDOUT
360 except (AttributeError, OSError, ValueError):
366 f
"set_prefix -> {prefix}",
367 stream=CONST.RAW_STDOUT
369 except (AttributeError, OSError, ValueError):
373 def set_override(self, override: bool =
False, *, lock: bool =
True) ->
None:
374 """Public setter for the file open mode.
376 When `override` is True files will be opened with mode 'w'
377 (overwrite); otherwise 'a' (append) is used. The lock
378 behaviour is controlled by `lock`.
380 _value: Dict[bool, str] = {
386 self.
_set_mode(_value[bool(override)], lock=
False)
389 f
"set_override -> {bool(override)}",
390 stream=CONST.RAW_STDOUT
392 except (AttributeError, OSError, ValueError):
395 self.
_set_mode(_value[bool(override)], lock=
False)
398 f
"set_override -> {bool(override)}",
399 stream=CONST.RAW_STDOUT
401 except (AttributeError, OSError, ValueError):
404 def set_filepath(self, file_path: Optional[Union[str, Path, CONST.FileInfo]], *, lock: bool =
True) ->
None:
405 """Set or clear the active file/file path for this instance.
408 file_path (Optional[Union[str, Path, CONST.FileInfo]]): A path-like object, a `CONST.FileInfo` instance describing an already-open file, or None to clear the current file.
411 lock (bool): When True the instance lock is held while the change is applied. Default: True
420 "set_filepath -> cleared file_path",
421 stream=CONST.RAW_STDOUT
423 except (AttributeError, OSError, ValueError):
430 "set_filepath -> cleared file_path",
431 stream=CONST.RAW_STDOUT
433 except (AttributeError, OSError, ValueError):
441 f
"set_filepath -> set to {file_path}",
442 stream=CONST.RAW_STDOUT
444 except (AttributeError, OSError, ValueError):
450 f
"set_filepath -> set to {file_path}",
451 stream=CONST.RAW_STDOUT
453 except (AttributeError, OSError, ValueError):
457 """Return True when file logging is enabled.
460 lock (bool): When True the instance lock is acquired before reading the flag. Default: True
463 True if writes to the log file are enabled, False otherwise.
471 """Return the current file open mode ('w' or 'a').
473 When `lock` is True the instance lock is acquired prior to
482 """Return the merged flag (True when streams share a file).
484 The optional `lock` parameter controls whether the instance
485 lock is held while reading the value.
493 """Return the merge_stdin flag (True when stdin is merged into the shared log file).
496 lock (bool): When True the instance lock is held while reading the value. Default: True
499 True if stdin is merged into the shared log file, False otherwise.
507 """Return the configured text encoding for file writes.
509 When `lock` is True the instance lock is held while the value
517 def get_prefix(self, *, lock: bool =
True) -> Optional[CONST.Prefix]:
518 """Return a safe copy of the current `Prefix` configuration.
520 The returned `CONST.Prefix` is a fresh object to avoid exposing
521 internal references. If no prefix is configured None is
522 returned. When `lock` is True the instance lock is held while
528 _prefix = CONST.Prefix()
529 _prefix.std_in = self.
prefix.std_in
530 _prefix.std_out = self.
prefix.std_out
531 _prefix.std_err = self.
prefix.std_err
533 _prefix = CONST.Prefix()
534 _prefix.std_in = self.
prefix.std_in
535 _prefix.std_out = self.
prefix.std_out
536 _prefix.std_err = self.
prefix.std_err
541 """Return True when override mode ('w') is active.
543 This convenience maps internal mode strings to a boolean.
544 When `lock` is True the instance lock is held for the check.
546 _value: Dict[str, bool] = {
552 mode: str = self.
get_mode(lock=
False).lower()
557 stream=CONST.RAW_STDERR
560 raise ValueError(
"Unsupported mode")
561 mode: str = self.
get_mode(lock=
False).lower()
566 stream=CONST.RAW_STDERR
568 raise ValueError(
"Unsupported mode")
570 def get_filepath(self, *, lock: bool =
True) -> Optional[CONST.FileInfo]:
571 """Return the internal `FileInfo` reference (may be None).
573 Note: the returned object may be shared; callers that need to
574 mutate it should take care to hold the instance lock or use
575 `copy()` to obtain an independent view.
583 """Return the configured buffer flush threshold in bytes."""
590 """Return the configured maximum file size in bytes."""
597 """Return the configured folder prefix (StdMode) or None."""
603 def update(self, file_data: Optional[
'FileInstance'], *, lock: bool =
True) ->
None:
604 """Public method to copy configuration from another instance.
606 This method acquires the lock by default and delegates to the
607 `_update` implementation which performs the actual field
616 def copy(self, *, lock: bool =
True) ->
"FileInstance":
617 """Return a shallow copy of this instance's configuration.
619 The returned `FileInstance` is a new object populated from the
620 current instance. By default the instance lock is acquired to
621 provide a consistent snapshot.
628 def write(self, message: str) ->
None:
629 """Append `message` to the internal buffer (thread-safe).
631 The message is encoded and counted toward `flush_size`. When the
632 buffer reaches the configured threshold a background flush is
633 triggered (performed synchronously inside `_flush_buffer()` but
634 with I/O outside the main lock to minimize blocking).
642 f
"write: appended {len(message)} chars, should_flush={should}",
643 stream=CONST.RAW_STDOUT
645 except (AttributeError, OSError, ValueError):
650 stream=CONST.RAW_STDOUT
655 """Flush any buffered log lines to disk immediately.
657 This is a blocking call that performs disk I/O; callers should
658 avoid calling it too frequently. Errors raised by the underlying
659 I/O are propagated as OSError or ValueError when appropriate.
663 "flush: manual flush requested",
664 stream=CONST.RAW_STDOUT
666 except (AttributeError, OSError, ValueError):
671 """Set the internal `Prefix` object from an external one.
673 This internal setter copies boolean flag values from `prefix`
674 into a fresh `CONST.Prefix()` instance. The caller is
675 responsible for holding any required locks; this routine does
676 not perform locking itself.
681 "Prefixes set to None",
682 stream=CONST.RAW_STDOUT
688 stream=CONST.RAW_STDOUT
690 self.
prefix = CONST.Prefix()
691 self.
prefix.std_in = prefix.std_in
692 self.
prefix.std_out = prefix.std_out
693 self.
prefix.std_err = prefix.std_err
695 f
"Prefixes set: {self.prefix}",
696 stream=CONST.RAW_STDOUT
700 """Configure the per-stream folder prefix.
702 If `folder_prefix` is a valid `CONST.StdMode` value present in
703 `CONST.CORRECT_FOLDER`, it is stored; otherwise the stored
704 `folder_prefix` is cleared (set to None).
706 if folder_prefix
and folder_prefix
in CONST.CORRECT_FOLDER:
709 f
"Folder prefix: {self.folder_prefix}",
710 stream=CONST.RAW_STDOUT
715 def _set_mode(self, mode: str, *, lock: bool =
True) ->
None:
716 """Set the file mode to 'w' (overwrite) or 'a' (append).
718 The `lock` parameter indicates whether this function should
719 acquire `self._file_lock` before updating the internal mode.
720 Invalid input is ignored.
724 if mode
in (
"w",
"a"):
727 if mode
in (
"w",
"a"):
731 """Configure the maximum file size used for rotation.
733 `max_size_mb` is interpreted as megabytes when reasonable; the
734 function attempts to coerce the input to an integer. Negative
735 or too-small values are corrected with warnings. The resulting
736 internal `self.max_size` is stored in bytes.
740 _resp: int = int(max_size_mb)
741 except (ValueError, TypeError):
742 _resp = CONST.DEFAULT_LOG_MAX_FILE_SIZE
744 warn(
"Max provided size cannot be negative, converting to positive")
747 warn(
"Max provided size is smaller than 1 MB, using default max file size")
748 _resp = CONST.DEFAULT_LOG_MAX_FILE_SIZE
750 if _resp < CONST.MB1:
756 """Configure the flush threshold for buffered writes.
758 `flush_size_kb` is interpreted as kilobytes when reasonable; the
759 function coerces input to int, normalises negative values and
760 ensures the internal `self.flush_size` is stored in bytes.
764 _resp = int(flush_size_kb)
765 except (ValueError, TypeError):
766 _resp = CONST.DEFAULT_LOG_BUFFER_FLUSH_SIZE
768 warn(
"Flush size cannot be negative, converting to positive")
771 warn(
"Flush size is smaller than 1 KB, using default flush size")
772 _resp = CONST.DEFAULT_LOG_BUFFER_FLUSH_SIZE
774 if _resp < CONST.KB1:
780 """Internal routine to set the instance's file reference.
782 This method closes any previously-open file, clears internal
783 state, and opens the provided `file_path`. The `file_path` may
784 be a string/Path (in which case a new `FileInfo` is created)
785 or an existing `CONST.FileInfo` instance which may be re-opened
791 if isinstance(file_path, (str, Path)):
792 _path = Path(file_path)
794 elif isinstance(file_path, CONST.FileInfo):
800 def _update(self, file_data: Optional[
'FileInstance']) ->
None:
801 """Copy configuration values from another FileInstance.
803 This helper updates the receiver to match the provided
804 `file_data`. The method is intended to be called while holding
805 the caller's lock; it delegates to public setters with the
806 `lock=False` flag to avoid deadlocks.
812 self.
set_merged(file_data.get_merged(), lock=
False)
814 self.
set_prefix(file_data.get_prefix(), lock=
False)
822 """Return a shallow copy of this FileInstance configuration.
824 The returned `FileInstance` will share the same `FileInfo`
825 reference but will otherwise have the same configuration
826 values (mode, encoding, prefix, sizes). The copy is useful for
827 creating per-stream views of shared configuration.
843 """Return the current UTC datetime used for naming files.
845 Centralising the time provider makes it easier to test
846 filename generation and ensures all timestamps use UTC.
848 return datetime.now(timezone.utc)
851 """Construct a timestamped log filename.
853 The filename format is driven by `CONST.FILE_LOG_DATE_FORMAT` and
854 uses the current UTC time returned by `_get_current_date()`.
859 """Check whether the in-memory buffer has reached the flush threshold.
862 True if the total encoded size of buffered lines meets or exceeds `flush_size`, False otherwise.
866 _tmp += len(line.encode(self.
encoding))
870 """Add the sizes of buffered lines to `file.written_bytes`.
872 This method is called after a successful write to update the
873 persisted byte counter. It encodes lines using the configured
874 encoding and falls back to 'utf-8' on lookup errors. The in-
875 memory buffer is cleared after accounting.
883 self.
filefile.written_bytes += len(line.encode(
'utf-8'))
887 """Check whether the current log file has exceeded the maximum size threshold.
890 True if the file's `written_bytes` exceeds `max_size` or no file is open, False otherwise.
897 """Rotate the current file if the bytes threshold is exceeded.
899 When rotation is needed the current descriptor is closed and a
900 fresh `FileInfo` is opened at a newly-created path returned by
901 `_create_log_path()`.
909 """Build the timestamped log file path and create the parent directories.
911 The path is organised as `<root>/logs/<year>/<month>/<day>/[<stream>/]<timestamp>.log`.
912 If `base_override` is provided it is used as the root; otherwise the instance's
913 current file path or the package directory is used as the fallback.
916 base_override (Optional[Path]): Override root directory for the log path. Default: None
919 The resolved Path for the new log file.
926 if base_override
is not None:
927 _root: Path = base_override
931 if candidate.suffix ==
'.log':
932 candidate.parent.mkdir(parents=
True, exist_ok=
True)
937 _root: Path = Path(__file__).parent
943 if _root.suffix ==
"" and CONST.LOG_FOLDER_BASE_NAME != _root.name:
944 _root = _root / CONST.LOG_FOLDER_BASE_NAME
945 elif _root.suffix !=
"" and CONST.LOG_FOLDER_BASE_NAME != _root.parent:
946 _root = _root.parent / CONST.LOG_FOLDER_BASE_NAME / _root.name
948 year_dir = _root / str(now.year)
949 month_dir = year_dir / f
"{now.month:02d}"
950 day_dir = month_dir / f
"{now.day:02d}"
956 _should_create =
False
961 day_dir.mkdir(parents=
True, exist_ok=
True)
964 f
"Determined file name: {filename}, determined file path: {day_dir}",
965 stream=CONST.RAW_STDOUT
967 return day_dir / filename
970 """Heuristic check to decide whether a path refers to a directory.
972 Returns True when the path has no file extension, already exists as a
973 directory, or ends with a path separator. Used to determine whether a
974 caller-supplied path should be treated as a folder (triggering
975 automatic log-file naming) or as an explicit file path.
978 dir_path (Path): Path to evaluate.
981 True if the path appears to represent a directory, False otherwise.
983 if not str(dir_path):
985 if dir_path.suffix ==
"":
988 if dir_path.exists():
989 return dir_path.is_dir()
992 str_path: str = str(dir_path)
993 looks_like_it = str_path.endswith(os.sep)
or str_path.endswith(
994 "/")
or str_path.endswith(
"\\")
996 f
"Looks_like_it: {looks_like_it}",
997 stream=CONST.RAW_STDOUT
1002 """Open or create the log file at `file_path` and return a populated `FileInfo`.
1004 If `file_path` looks like a directory (as determined by `_looks_like_directory`)
1005 a timestamped filename is generated via `_create_log_path`. The parent directory
1006 is created if it does not exist. The file descriptor is opened outside the
1007 instance lock; the resulting `FileInfo` is populated atomically under the lock.
1010 file_path (Path): Destination path for the log file, or a directory.
1013 A `CONST.FileInfo` with `path`, `descriptor`, and `written_bytes` populated.
1015 _node: CONST.FileInfo = CONST.FileInfo()
1016 _node.path = Path(file_path)
1020 _node.path.parent.mkdir(parents=
True, exist_ok=
True)
1031 "File is not open, attempting to open",
1032 stream=CONST.RAW_STDOUT
1041 except (OSError, ValueError):
1043 f
"Failed to open file: {_node.path}",
1044 stream=CONST.RAW_STDERR
1051 _node.descriptor = descriptor
1052 if file_path.exists():
1053 _node.written_bytes = file_path.stat().st_size
1055 _node.written_bytes = 0
1057 f
"Written bytes: {_node.written_bytes}",
1058 stream=CONST.RAW_STDOUT
1063 """Close the underlying file descriptor without acquiring locks.
1065 This inner helper is used by `_close_file()`; it performs the
1066 actual descriptor close and clears the reference. It is safe to
1067 call when already closed. Errors during close are suppressed
1068 because this is a best-effort operation.
1072 "File is open, closing",
1073 stream=CONST.RAW_STDOUT
1075 descriptor = getattr(self.
filefile,
"descriptor",
None)
1079 except (OSError, ValueError):
1085 except AttributeError:
1091 """Close the current file descriptor, optionally acquiring a lock.
1093 When `lock` is True the instance lock `self._file_lock` is
1094 acquired before closing the file. Returns True on completion.
1102 descriptor = getattr(self.
filefile,
"descriptor",
None)
1105 except AttributeError:
1109 descriptor = getattr(self.
filefile,
"descriptor",
None)
1114 except (OSError, ValueError):
1120 """Internal: detach pending buffer and write to disk.
1122 Implements the swap-buffer pattern: capture and clear the in-memory
1123 buffer while holding the lock, perform I/O outside the lock, then
1124 update counters and rotate under lock.
1130 self.
rogger.log_debug(
"Flushing buffer", stream=CONST.RAW_STDOUT)
1145 descriptor = getattr(self.
filefile,
"descriptor",
None)
1146 if descriptor
and not getattr(descriptor,
"closed",
False):
1157 f
"_flush_buffer: needs_open=True, to_write_lines={len(to_write)}",
1158 stream=CONST.RAW_STDOUT
1160 except (AttributeError, OSError, ValueError):
1165 except (OSError, ValueError):
1172 f
"_flush_buffer: performing write, lines={len(to_write)}",
1173 stream=CONST.RAW_STDOUT
1175 except (AttributeError, OSError, ValueError):
1181 descriptor = getattr(self.
filefile,
"descriptor",
None)
1182 if descriptor
and not getattr(descriptor,
"closed",
False):
1183 descriptor.writelines(to_write)
1185 except (ValueError, OSError):
1188 "_flush_buffer: write failed, attempting reopen",
1189 stream=CONST.RAW_STDERR
1191 except (AttributeError, OSError, ValueError):
1197 except (OSError, ValueError):
1201 descriptor = getattr(self.
filefile,
"descriptor",
None)
1202 if descriptor
and not getattr(descriptor,
"closed",
False):
1204 descriptor.writelines(to_write)
1206 except (ValueError, OSError):
1209 "_flush_buffer: retry write failed, giving up on this flush",
1210 stream=CONST.RAW_STDERR
1212 except (AttributeError, OSError, ValueError):
1219 for line
in to_write:
1221 _bytes += len(line.encode(self.
encoding))
1223 _bytes += len(line.encode(
'utf-8'))
1225 f
"_flush_buffer: write successful, approx_bytes={_bytes}",
1226 stream=CONST.RAW_STDOUT
1228 except (AttributeError, OSError, ValueError):
1234 for line
in to_write:
1238 self.
filefile.written_bytes += len(line.encode(
'utf-8'))
int get_max_size(self, *, bool lock=True)
None write(self, str message)
bool get_merge_stdin(self, *, bool lock=True)
Optional[CONST.Prefix] get_prefix(self, *, bool lock=True)
bool _close_file(self, *, Optional[bool] lock=True)
bool _close_file_inner(self)
None set_folder_prefix(self, Optional[CONST.StdMode] folder_prefix, *, bool lock=True)
None set_encoding(self, str encoding, *, bool lock=True)
None _set_flush_size(self, int flush_size_kb)
str get_mode(self, *, bool lock=True)
bool _should_rotate(self)
None set_filepath(self, Optional[Union[str, Path, CONST.FileInfo]] file_path, *, bool lock=True)
None set_prefix(self, Optional[CONST.Prefix] prefix, *, bool lock=True)
datetime _get_current_date(self)
Optional[CONST.FileInfo] get_filepath(self, *, bool lock=True)
None _set_mode(self, str mode, *, bool lock=True)
bool get_override(self, *, bool lock=True)
None set_override(self, bool override=False, *, bool lock=True)
None set_merge_stdin(self, bool merge_stdin, *, bool lock=True)
None set_max_size(self, int max_size_mb, *, bool lock=True)
"FileInstance" copy(self, *, bool lock=True)
None __init__(self, Optional[Union[str, Path, CONST.FileInfo]] file_path, Optional[bool] override=None, Optional[bool] merged=None, Optional[str] encoding=None, Optional[CONST.Prefix] prefix=None, *, Optional[int] max_size_mb=None, Optional[int] flush_size_kb=None, Optional[CONST.StdMode] folder_prefix=None, bool log_to_file=True, Optional[bool] merge_stdin=None)
None _set_folder_prefix(self, Optional[CONST.StdMode] folder_prefix)
None _refresh_written_bytes(self)
Optional[CONST.StdMode] folder_prefix
None _set_prefix(self, Optional[CONST.Prefix] prefix)
None _update(self, Optional['FileInstance'] file_data)
CONST.FileInfo _open_file(self, Path file_path)
Optional[CONST.FileInfo] file
bool get_log_to_file(self, *, bool lock=True)
"FileInstance" _copy(self)
None update(self, Optional['FileInstance'] file_data, *, bool lock=True)
None set_log_to_file(self, bool log_to_file=True, *, bool lock=True)
str get_encoding(self, *, bool lock=True)
None _set_max_size(self, int max_size_mb)
bool get_merged(self, *, bool lock=True)
Optional[CONST.Prefix] prefix
None _set_filepath_child(self, Union[str, Path, CONST.FileInfo] file_path)
None set_flush_size(self, int flush_size, *, bool lock=True)
None set_merged(self, bool merged, *, bool lock=True)
bool _looks_like_directory(self, Path dir_path)
Optional[CONST.StdMode] get_folder_prefix(self, *, bool lock=True)
Path _create_log_path(self, Optional[Path] base_override=None)
int get_flush_size(self, *, bool lock=True)