Revision history for Log-Abstraction

0.31	Jun  8 20:55:28 EDT 2026
	[ Architecture ]
	- Added use autodie qw(:all); file writes wrapped in eval{} to keep
	  logging failures silent (app must not crash when log file is unavailable)
	- Extracted _format_message($self, $level, $str, $use_class) helper,
	  eliminating 4+ copies of the format-token substitution block
	- Extracted _validate_file_path($self, $path) helper, unifying path
	  validation for hash-backend file, scalar-path, and self->{'file'} under
	  one implementation
	- Added Readonly constants for all magic strings and numbers (SMTP defaults,
	  port range, syslog defaults, format strings, validation regexes)
	- Collapsed _high_priority duplicate if/else blocks into one
	- All public void methods now return $self to allow method chaining;
	  level() setter returns undef on invalid input to signal the error
	- Fixed syslog priority mapping: all levels (trace→debug, debug→debug,
	  info→info, notice→notice, warn→warning, error→err) now correctly mapped
	  via %LEVEL_TO_SYSLOG_PRIORITY hash instead of a binary error/warning test
	- Moved Readonly from test-only to runtime dependency (Makefile.PL, cpanfile)

	[ Documentation ]
	- Added Z-notation FORMAL SPECIFICATION to every public method POD
	- Added API Specification (Params::Validate::Strict input and Return::Set
	  output schemas) to every public method POD
	- Added MESSAGES table to every public method POD
	- Added internal-routine comment blocks (purpose, entry, exit, side
	  effects, notes) for _log, _high_priority, _format_message,
	  _validate_file_path, _sanitize_email_header, DESTROY
	- Suppressed %class% for the base Log::Abstraction class (appears empty,
	  as it added no useful information when not subclassed)

	[ Tests ]
	- Added t/locales.t: covers geographic (Locale::Country ISO-code sanity
	  with BAIL_OUT on drift, case-insensitive codes, concurrent instances) and
	  POSIX system-locale (LC_ALL en_US.UTF-8/de_DE.UTF-8/ja_JP.UTF-8 error
	  paths using local $! = ENOENT; my $msg = "$!" pattern)
	- Updated edge_cases.t and function.t to use unified "Invalid file name"
	  error message (standardised via _validate_file_path)

	[ Security ]
	- Fixed path traversal in HASH-backend file logger: path regex now blocks
	  '..' sequences, preventing writes outside the intended directory
	- Fixed open() in HASH-backend file logger to use the untainted $file
	  variable rather than the original $logger->{'file'} value
	- Added path validation (same allowlist + '..' check) to the scalar-string
	  logger path, which previously called open() with no validation at all
	- Validated SMTP host in sendmail backend: only [a-zA-Z0-9.-] characters
	  accepted, preventing header/protocol injection via a crafted hostname
	- Validated SMTP port in sendmail backend: must be an integer 1-65535,
	  preventing SSRF port-scanning via an attacker-controlled config file
	- Fixed %env_*% format expansion in HASH file backend to use /ge with
	  defined-or fallback, consistent with the fd and scalar-path backends

	[ Bug Fixes ]
	- Fixed use Readonly::Values::Syslog version in source to 0.04, matching
	  Makefile.PL and cpanfile (was still 0.03 after the 0.30 bump)

	[ Documentation ]
	- Fixed EXAMPLES POD: sample output class field was "main"; corrected to
	  "Log::Abstraction" (blessed class of the logger object)
	- Fixed EXAMPLES POD: sample output warn-row level field was "warning";
	  corrected to "warn" to match what the module actually passes to callbacks
	- Added note explaining that file/line resolve to the module's internal
	  dispatch for warn/error calls due to the extra _high_priority stack frame
	- Replaced broken combined file+sendmail example (file backend writes
	  native text format, not CSV) with a correct sendmail-only example; added
	  level => 'warn' key to restrict emails to warn-and-above; noted that
	  combining CSV output with email requires both in a single code-ref

	[ Tests ]
	- Simplified $parse_line regex in integration.t: removed dead |"$ alternation
	  subsumed by "(?:,|$)

0.30	Wed May 27 13:09:35 EDT 2026

	[ Enhancements ]
	- Added EXAMPLES POD section: CSV file logging for BI import (code-ref
	  backend writing timestamp/level/class/file/line/message rows, with a
	  combined sendmail+min_interval variant for real-time alerting)

	[ Bug Fixes]
	- Bump minimum Readonly::Values::Syslog to 0.04 to fix is_debug() returning
	  false at trace level on older installations
	  Fixes https://www.cpantesters.org/cpan/report/32b653da-59c4-11f1-ba50-8ade6d8775ea

0.29	Tue May 26 20:36:13 EDT 2026

	[ Enhancements ]
	- Added min_interval throttle to sendmail backend: at most one email per
	  configured interval (seconds); cooldown state stored in _last_email_sent
	  on the object and inherited by clones

	- Bump minimum Test::Mockingbird version to fix https://www.cpantesters.org/cpan/report/f148a3e6-50a2-11f1-8224-a122dd379578

0.28 Fri May 15 20:28:07 EDT 2026

	[ Enhancements ]
	- Added context (ctx) to code ref logger callbacks

	[ Bug Fixes]
	- Fixed carp_on_warn and croak_on_error when logging to an array backend

	- Fixed cloning to a different level (level string now converted to integer)

	- Fixed DESTROY to call Sys::Syslog::closelog() fully-qualified for correct mock interception

	- Fixed warn(undef) and warn(warning => undef) to be silent no-ops

	- Fixed warn(warning => [undef, ...]) to filter undef elements before joining

	- Fixed odd-count plain list in warn/error (_high_priority now wraps get_params in eval)

	- Fixed autovivification of sendmail key via exists() guards on sendmail hash access

	- Fixed %env_foo% format token to expand missing env vars to empty string without warning

	- Fixed ::new() to always construct normally regardless of arguments

	- Removed spurious Carp::croak/carp fallback when array backend is defined

0.27	Sun Jan 11 09:52:54 EST 2026
	Easier to read tests
	Added fatal as a synonym for error

0.26	Wed Oct 15 17:03:58 EDT 2025
	Added testing dashboard on GitHub Pages
	Added croak_on_error

0.25	Sun Aug 17 20:41:38 EDT 2025
	Added is_debug for consistency with Log::Any
	Use Return::Set to assert return values within specification
	Sanitize the e-mail headers
	Started to add format argument to new()
	Don't close fd that were passed in
	Only load the mail modules when needed

0.24	Sat Jul 19 16:52:41 EDT 2025
	Added the ability to send an email for higher priority messages
	Only load Log::Log4perl when needed
	Deep-clone the messages array during cloning
	Map notice to info for Log::Log4perl
	Syslog facility is now configurable (default is still local0)
	Connections to the syslog are now persistent

0.23	Thu Jul 17 14:45:54 EDT 2025
	Better error message when we don't know what to do
	Allow the syslog config to say 'server' instead of 'host' for consistency with CHI

0.22	Thu Jul 17 08:22:16 EDT 2025
	Error() now sends to syslog like warn()
	Ensure undef isn't passed in the messages array to coderefs

0.21	Wed Jul 16 16:21:25 EDT 2025
	Added error()

0.20	Sun Jun 15 21:19:03 EDT 2025
	Fix GitHub#2

0.19	Tue Jun 10 08:32:28 EDT 2025
	Bump minimum version of Sys::Syslog
	More untaint checking

0.18	Fri Jun  6 15:52:27 EDT 2025
	Fix level testing

0.17	Thu May 22 14:21:14 EDT 2025
	Added the messages() method

0.16	Thu May 22 07:40:24 EDT 2025
	Handle upper case levels, such as 'INFO'

0.15	Tue May 20 21:10:23 EDT 2025
	No need to create a Log::Log4perl when sending output to a file

0.14	Tue May 20 07:44:19 EDT 2025
	Adjusted minimum version of Config::Abstraction.
		It should be 0.19 in terms of testing but it's best to use the fixes in 0.25
		RT#165420 - ANDK
	Added 'array' to the logger hash
	Introduced 'level' - minimum level to log at, defaults to 'warn'
	Fixed where to log bugs
	Check and untaint the filename

0.13	Wed May 14 08:30:53 EDT 2025
	Use '>' after level rather than ':' to files, like lower level loggers
	Try not to put the name of this package in the logfiles,
		it adds nothing apart from disc usage
	Block setting logger => Log::Abstraction in new()
	Croak if we don't know how to handle a message

0.12	Fri May  9 07:42:35 EDT 2025
	Added the file parameter, so that both file and syslog can now be given,
		and file can be read in from a configuration file
	Added the fd descriptor - a file descriptor to log to
	Added separators between fields in file output

0.11	Wed May  7 15:37:37 EDT 2025
	Fixed calling can() on an unblessed variable
	Honour carp_on_warn when syslog is set

0.10	Tue May  6 20:43:48 EDT 2025
	Added carp_on_warn

0.09	Tue May  6 08:15:36 EDT 2025
	If no logger is given, use Log4perl

0.08	Mon May  5 11:39:58 EDT 2025
	Use Config::Abstraction instead of Config::Auto
	Guess the value if script_name if it's not given

0.07	Mon Mar 24 08:36:01 EDT 2025
	Handle "Socket operation on non-socket" on Solaris
	Calling new on an object now returns a clone rather than setting the defaults in the new object

0.06	Wed Mar 12 13:23:24 EDT 2025
	Don't put spaces between elements of an array

0.05	Tue Mar 11 14:10:10 EDT 2025
	Handle being passed a reference to an array

0.04	Mon Mar 10 09:39:46 EDT 2025
	Renamed from Log-YetAnother to Log-Abstraction
	Added config_file argument to new()

0.03	Sat Mar  8 08:40:00 EST 2025
	Fix passing an array to warn()

0.02	Thu Mar  6 17:09:32 EST 2025
	Improved argument checking to new()

0.01	Thu Mar  6 15:42:04 EST 2025
        First draft
