{"id":26130563,"url":"https://github.com/nigelhorne/log-abstraction","last_synced_at":"2026-05-30T22:31:57.627Z","repository":{"id":281063539,"uuid":"944083068","full_name":"nigelhorne/Log-Abstraction","owner":"nigelhorne","description":"Logging abstraction layer","archived":false,"fork":false,"pushed_at":"2025-03-10T13:40:54.000Z","size":43,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-10T14:31:28.096Z","etag":null,"topics":["cpan","cpan-module","logger","logging","perl","perl5"],"latest_commit_sha":null,"homepage":"","language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nigelhorne.png","metadata":{"files":{"readme":"README.md","changelog":"Changes","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-06T18:56:21.000Z","updated_at":"2025-03-10T13:40:57.000Z","dependencies_parsed_at":"2025-03-10T14:42:35.043Z","dependency_job_id":null,"html_url":"https://github.com/nigelhorne/Log-Abstraction","commit_stats":null,"previous_names":["nigelhorne/log-yetanother","nigelhorne/log-abstraction"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FLog-Abstraction","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FLog-Abstraction/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FLog-Abstraction/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FLog-Abstraction/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nigelhorne","download_url":"https://codeload.github.com/nigelhorne/Log-Abstraction/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242926219,"owners_count":20207754,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpan","cpan-module","logger","logging","perl","perl5"],"created_at":"2025-03-10T20:46:24.449Z","updated_at":"2026-05-30T22:31:57.621Z","avatar_url":"https://github.com/nigelhorne.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NAME\n\nLog::Abstraction - Logging Abstraction Layer\n\n# VERSION\n\n0.30\n\n# SYNOPSIS\n\n    use Log::Abstraction;\n\n    my $logger = Log::Abstraction-\u003enew(logger =\u003e 'logfile.log');\n\n    $logger-\u003edebug('This is a debug message');\n    $logger-\u003einfo('This is an info message');\n    $logger-\u003enotice('This is a notice message');\n    $logger-\u003etrace('This is a trace message');\n    $logger-\u003ewarn({ warning =\u003e 'This is a warning message' });\n\n# DESCRIPTION\n\nThe `Log::Abstraction` class provides a flexible logging layer on top of different types of loggers,\nincluding code references, arrays, file paths, and objects.\nIt also supports logging to syslog if configured.\n\n# METHODS\n\n## new\n\n    my $logger = Log::Abstraction-\u003enew(%args);\n\nCreates a new `Log::Abstraction` object.\n\nThe argument can be a hash,\na reference to a hash or the `logger` value.\nThe following arguments can be provided:\n\n- `carp_on_warn`\n\n    If set to 1,\n    and `logger` is not given,\n    call `Carp:carp` on `warn()`.\n\n    Causes `error()` to `carp` if `croak_on_error` is not given.\n\n- `croak_on_error`\n\n    If set to 1,\n    and `logger` is not given,\n    call `Carp:croak` on `error()`.\n\n- `config_file`\n\n    Points to a configuration file which contains the parameters to `new()`.\n    The file can be in any common format,\n    including `YAML`, `XML`, and `INI`.\n    This allows the parameters to be set at run time.\n\n    On a non-Windows system,\n    the class can be configured using environment variables starting with `\"Log::Abstraction::\"`.\n    For example:\n\n        export Log::Abstraction::script_name=foo\n\n    It doesn't work on Windows because of the case-insensitive nature of that system.\n\n- `level`\n\n    The minimum level at which to log something,\n    the default is \"warning\".\n\n- `logger`\n\n    A logger can be one or more of:\n\n    - a code reference\n\n        The code will be called with a hashref containing:\n\n        - class\n        - file\n        - line\n        - level\n        - message - an arrayref of messages\n        - ctx - passed to `new()`, an argument that can help to give context to the caller\n\n    - an object\n    - a hash of options\n    - sendmail - send higher priority messages to an email address\n\n        To send an e-mail,\n        you need [\"require Email::Simple\"](#require-email-simple), [\"require Email::Sender::Simple\"](#require-email-sender-simple) and [Email::Sender::Transport::SMTP](https://metacpan.org/pod/Email%3A%3ASender%3A%3ATransport%3A%3ASMTP).\n\n        The `sendmail` hash also accepts a `min_interval` key (seconds).\n        When set, at most one email is sent per `min_interval` seconds; any\n        messages that arrive during the cooldown are still logged to other\n        backends but do not trigger a new email.\n        The send time is stored in `_last_email_sent` on the object, so each\n        instance has its own cooldown window; cloned objects inherit the\n        parent's last-send timestamp at the moment of cloning.\n\n    - array - a reference to an array\n    - fd - containing a file descriptor to log to\n    - file - containing the filename\n\n    Defaults to [Log::Log4perl](https://metacpan.org/pod/Log%3A%3ALog4perl).\n    In that case,\n    the argument 'verbose' to new() will raise the logging level.\n\n- `format`\n\n    The format of the message.\n    Expands:\n\n    - %callstack%\n    - %level%\n    - %class%\n    - %message%\n    - %timestamp%\n\n        \u0026#x3d;%item \\* %env\\_foo%\n\n        Replaces with `$ENV{foo}`\n\n- `syslog`\n\n    A hash reference for syslog configuration.\n    Only warnings and above will be sent to syslog.\n    This restriction should be lifted in the future,\n    since it's reasonable to send notices and above to the syslog.\n\n- `script_name`\n\n    The name of the script.\n    It's needed when `syslog` is given,\n    if none is passed, the value is guessed.\n\nClone existing objects with or without modifications:\n\n    my $clone = $logger-\u003enew();\n\n## \\_sanitize\\_email\\_header\n\n    my $clean_value = _sanitize_email_header($raw_value);\n\nInternal routine to remove carriage return and line feed characters from an email header value to prevent header injection or formatting issues.\n\n- Input\n\n    Takes a single scalar value, typically a string representing an email header field.\n\n- Behavior\n\n    If the input is undefined, returns \\`undef\\`. Otherwise, removes all newline characters (\\`\\\\n\\`), carriage returns (\\`\\\\r\\`), and CRLF pairs from the string.\n\n- Output\n\n    Returns the sanitized string with CR/LF characters removed.\n\n### FORMAL SPECIFICATION\n\nIf the input is undefined (∅), the output is also undefined (∅).\n\nIf the input is defined, the result is a defined string with CR and LF characters removed.\n\n    [CHAR]\n\n    CR, LF : CHAR\n    CR == '\\r'\n    LF == '\\n'\n\n    STRING == seq CHAR\n\n    SanitizeEmailHeader\n        raw?: STRING\n        sanitized!: STRING\n        -------------------------------------------------\n        sanitized! = [ c : raw? | c ≠ CR ∧ c ≠ LF ]\n\n## level($self, $level)\n\nGet/set the minimum level to log at.\nReturns the current level, as an integer.\n\n## is\\_debug\n\nAre we at a debug level that will emit debug messages?\nFor compatibility with [Log::Any](https://metacpan.org/pod/Log%3A%3AAny).\n\n## messages\n\nReturn all the messages emitted so far\n\n## debug\n\n    $logger-\u003edebug(@messages);\n\nLogs a debug message.\n\n## info\n\n    $logger-\u003einfo(@messages);\n\nLogs an info message.\n\n## notice\n\n    $logger-\u003enotice(@messages);\n\nLogs a notice message.\n\n## error\n\n    $logger-\u003eerror(@messages);\n\nLogs an error message. This method also supports logging to syslog if configured.\nIf not logging mechanism is set,\nfalls back to `croak`.\n\n## fatal\n\n    $logger-\u003efatal(@messages);\n\nSynonym of error.\n\n## trace\n\n    $logger-\u003etrace(@messages);\n\nLogs a trace message.\n\n## warn\n\n    $logger-\u003ewarn(@messages);\n    $logger-\u003ewarn(\\@messages);\n    $logger-\u003ewarn(warning =\u003e \\@messages);\n\nLogs a warning message. This method also supports logging to syslog if configured.\nIf not logging mechanism is set,\nfalls back to `Carp`.\n\n## \\_high\\_priority\n\nHelper to handle important messages.\n\n# EXAMPLES\n\n## CSV file logging for BI import\n\nThe code-reference backend gives you full control over the output format.\nThe example below writes every message at `trace` level and above as a\nCSV row to a file,\nproducing output that can be loaded directly into a spreadsheet or BI tool\n(Tableau, Power BI, Metabase, etc.).\n\nEach row contains: `timestamp`, `level`, `class`, `file`, `line`, `message`.\n\n    use Log::Abstraction;\n\n    my $csv_file = 'app_events.csv';\n\n    # Write the header row once (skip if the file already exists and has data).\n    unless (-s $csv_file) {\n        open my $fh, '\u003e', $csv_file or die \"Cannot open $csv_file: $!\";\n        print $fh qq{timestamp,level,class,file,line,message\\n};\n        close $fh;\n    }\n\n    # Helper: quote a single CSV field (escapes embedded double-quotes).\n    my $csv_field = sub {\n        my $v = defined $_[0] ? $_[0] : '';\n        $v =~ s/\"/\"\"/g;\n        return qq{\"$v\"};\n    };\n\n    my $logger = Log::Abstraction-\u003enew(\n        level  =\u003e 'trace',        # capture everything from trace upwards\n        logger =\u003e sub {\n            my $args = $_[0];\n\n            my $timestamp = POSIX::strftime('%Y-%m-%dT%H:%M:%SZ', gmtime);\n            my $message  = join(' ', @{ $args-\u003e{message} // [] });\n\n            open my $fh, '\u003e\u003e', $csv_file or return;\n            print $fh join(',',\n                $csv_field-\u003e($timestamp),\n                $csv_field-\u003e($args-\u003e{level}),\n                $csv_field-\u003e($args-\u003e{class}),\n                $csv_field-\u003e($args-\u003e{file}),\n                $csv_field-\u003e($args-\u003e{line}),\n                $csv_field-\u003e($message),\n            ), \"\\n\";\n            close $fh;\n        },\n    );\n\n    $logger-\u003etrace('application started');\n    $logger-\u003einfo('user logged in', { user =\u003e 'alice' });\n    $logger-\u003ewarn({ warning =\u003e 'disk usage above 80%' });\n\nThe resulting `app_events.csv` looks like:\n\n    timestamp,level,class,file,line,message\n    \"2026-05-27T14:00:00Z\",\"trace\",\"Log::Abstraction\",\"app.pl\",\"42\",\"application started\"\n    \"2026-05-27T14:00:01Z\",\"info\",\"Log::Abstraction\",\"app.pl\",\"43\",\"user logged in\"\n    \"2026-05-27T14:00:02Z\",\"warn\",\"Log::Abstraction\",\"Log/Abstraction.pm\",\"820\",\"disk usage above 80%\"\n\nNote: `class` is always `Log::Abstraction` (or the subclass name if you subclass the\nmodule).  For `trace`, `debug`, `info`, and `notice` calls, `file` and `line`\nresolve to the caller's source location.  For `warn` and `error` calls the\nextra `_high_priority` stack frame shifts the resolution one level inward, so\n`file` and `line` point into the module rather than the calling script.\n\nFor production use, consider replacing the manual `$csv_field` quoting with\n[Text::CSV](https://metacpan.org/pod/Text%3A%3ACSV) for correct handling of embedded newlines and other edge cases.\n\nIf you also want real-time alerting on critical events, add the email logic\ndirectly inside the code-ref callback — test `$args-\u003e{level}` and call\nyour mailer for `warn` / `error` messages while still writing the CSV row\nfor every message.\n\nAlternatively, use the `sendmail` hash-ref backend on its own (without the\ncode-ref) and add a `level` key to restrict emails to warn-and-above:\n\n    my $logger = Log::Abstraction-\u003enew(\n        level  =\u003e 'warn',\n        logger =\u003e {\n            sendmail =\u003e {\n                host         =\u003e 'smtp.example.com',\n                to           =\u003e 'ops@example.com',\n                from         =\u003e 'logger@example.com',\n                subject      =\u003e 'Application alert',\n                level        =\u003e 'warn',   # only email at warn level and above\n                min_interval =\u003e 300,      # at most one alert email per 5 minutes\n            },\n        },\n    );\n\nNote: the `sendmail` backend writes the module's standard text format, not\nCSV.  To produce CSV rows _and_ send email alerts from the same logger,\nembed both the CSV-write and the mail-send logic inside a single code-ref\ncallback as described above.\n\n# AUTHOR\n\nNigel Horne `njh@nigelhorne.com`\n\n# SEE ALSO\n\n- [Test Dashboard](https://nigelhorne.github.io/Log-Abstraction/coverage/)\n\n# SUPPORT\n\nThis module is provided as-is without any warranty.\n\nPlease report any bugs or feature requests to `bug-log-abstraction at rt.cpan.org`,\nor through the web interface at\n[http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Log-Abstraction](http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Log-Abstraction).\nI will be notified, and then you'll\nautomatically be notified of progress on your bug as I make changes.\n\nYou can find documentation for this module with the perldoc command.\n\n    perldoc Log::Abstraction\n\nYou can also look for information at:\n\n- MetaCPAN\n\n    [https://metacpan.org/dist/Log-Abstraction](https://metacpan.org/dist/Log-Abstraction)\n\n- RT: CPAN's request tracker\n\n    [https://rt.cpan.org/NoAuth/Bugs.html?Dist=Log-Abstraction](https://rt.cpan.org/NoAuth/Bugs.html?Dist=Log-Abstraction)\n\n- CPAN Testers' Matrix\n\n    [http://matrix.cpantesters.org/?dist=Log-Abstraction](http://matrix.cpantesters.org/?dist=Log-Abstraction)\n\n- CPAN Testers Dependencies\n\n    [http://deps.cpantesters.org/?module=Log::Abstraction](http://deps.cpantesters.org/?module=Log::Abstraction)\n\n# COPYRIGHT AND LICENSE\n\nCopyright (C) 2025-2026 Nigel Horne\n\nUsage is subject to the GPL2 licence terms.\nIf you use it,\nplease let me know.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnigelhorne%2Flog-abstraction","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnigelhorne%2Flog-abstraction","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnigelhorne%2Flog-abstraction/lists"}