NAME

Medusa::XS - High-performance XS audit logging with the :Audit attribute

VERSION

Version 0.02

SYNOPSIS

package MyApp::User;
use Medusa::XS;

sub create :Audit {
    my ($self, %args) = @_;
    # ... your code ...
    return $user;
}

# Every call to create() is now automatically logged:
#   entry  - arguments, caller stack, GUID, timestamp
#   exit   - return values, elapsed time

DESCRIPTION

Medusa::XS is a drop-in XS replacement for Medusa that provides automatic subroutine audit logging via Perl attributes. Marking a subroutine with the :Audit attribute causes every call to be logged with entry arguments, return values, a unique GUID, caller stack trace, timestamp and elapsed time.

All hot-path work is done in C: UUID generation (via the Horus library, RFC 9562), caller-stack walking, timestamp formatting, argument serialisation (via the Loo library with optional ANSI colour), and log dispatch. The only Perl-level data is the %Medusa::XS::LOG configuration hash.

USAGE

Applying the :Audit attribute

use Medusa::XS;

sub my_method :Audit {
    ...
}

When the attribute is compiled, Medusa::XS wraps the subroutine with an XS wrapper that logs entry and exit automatically.

Passing configuration at import

use Medusa::XS (
    LOG_LEVEL   => 'info',
    LOG_FILE    => '/var/log/myapp-audit.log',
    TIME        => 'localtime',
    TIME_FORMAT => '%Y-%m-%dT%H:%M:%S.%ms',
);

Key-value pairs are merged into %Medusa::XS::LOG.

CONFIGURATION

All configuration lives in the package variable %Medusa::XS::LOG. Defaults are set at BEGIN time and may be overridden before or after use Medusa::XS.

LOGGER (string, default "Medusa::XS::Logger")

Class name of the logger to instantiate when LOG_INIT is called.

LOG_FILE (string, default "audit.log")

Path passed to the logger constructor.

LOG_LEVEL (string, default "debug")

The log level written into each message. Also selects which method is called on the logger object (via LOG_FUNCTIONS).

LOG_INIT (coderef)

Called once (lazily, on the first audited call) to create the logger object. Must return a blessed reference that is stored in $LOG{LOG}.

LOG (object, default undef)

The live logger instance. Set automatically by LOG_INIT, or assign your own object before any audited call:

$Medusa::XS::LOG{LOG} = My::Logger->new;

The object must implement the method named by LOG_FUNCTIONS for the current LOG_LEVEL (e.g. debug, info, or error).

TIME (string, default "gmtime")

"gmtime" or "localtime" — controls the time zone of timestamps.

TIME_FORMAT (string, default "default")

A strftime(3)-compatible format string. The special token %ms is replaced with zero-padded milliseconds. The literal string "default" produces strftime("%a %b %e %H:%M:%S %Y").

QUOTE (string, default "†" (U+2020 DAGGER))

Delimiter wrapped around field values in the formatted log line.

FORMAT_MESSAGE (coderef, default \&xs_format_message)

Formatter callback. When set to the built-in xs_format_message (or left at the default), the fast all-C formatting path is used. Supply your own coderef to customize log output:

$Medusa::XS::LOG{FORMAT_MESSAGE} = sub {
    my (%p) = @_;
    # %p contains: message, guid, caller, level, params, prefix,
    #              elapsed_call (on exit)
    return "CUSTOM: $p{message}";
};

Note: a custom Perl callback disables the zero-allocation fast path.

LOG_FUNCTIONS (hashref)

Maps log level names to method names on the logger object:

{
    error => 'error',
    info  => 'info',
    debug => 'debug',
}
OPTIONS (hashref)

Feature flags and GUID configuration. Boolean fields default to 1 (enabled):

{
    date           => 1,       # timestamp
    guid           => 1,       # unique call ID
    guid_version   => 4,       # UUID version (1-8, 0=NIL, -1=MAX)
    guid_namespace => undef,   # for v3/v5: dns, url, oid, x500, or UUID string
    guid_name      => undef,   # for v3/v5: arbitrary name string
    level          => 1,       # log level label
    caller         => 1,       # caller stack trace
    elapsed_call   => 1,       # elapsed time (exit messages only)
    colour         => 1,       # 0=off, 1=on, 'auto'=detect terminal
    colour_theme   => 'default', # Loo theme name
}

colour controls ANSI colour output in serialised parameters. Set to 0 to disable, 1 to enable (default), or 'auto' to auto-detect based on terminal and $ENV{NO_COLOR}.

colour_theme selects the colour palette. Built-in themes: "default", "light", "monokai", "none".

Set guid_version to 7 for time-ordered UUIDs (recommended for database primary keys). Versions 3 and 5 require guid_namespace and guid_name to be set.

FUNCTIONS

These are available as Medusa::XS::function_name() and are implemented entirely in C.

generate_guid

my $v4  = Medusa::XS::generate_guid();            # default (v4)
my $v7  = Medusa::XS::generate_guid(7);            # time-ordered
my $v5  = Medusa::XS::generate_guid(5, 'dns', 'example.com');
my $nil = Medusa::XS::generate_guid(0);            # NIL UUID
my $max = Medusa::XS::generate_guid(-1);           # MAX UUID

Returns a UUID string (36 characters) generated via the Horus library (RFC 9562). The optional first argument selects the UUID version:

1 - Time-based (Gregorian 100ns timestamp + node)
3 - MD5 namespace (requires namespace and name arguments)
4 - Random (default)
5 - SHA-1 namespace (requires namespace and name arguments)
6 - Reordered time (sortable, Gregorian)
7 - Unix epoch time-ordered (recommended for databases)
8 - Custom (random data with version/variant stamped)
0 - NIL UUID (all zeros)
-1 - MAX UUID (all ones)

For versions 3 and 5, the second argument is the namespace (one of "dns", "url", "oid", "x500", or a UUID string) and the third argument is the name string.

format_time

my $ts = Medusa::XS::format_time();             # gmtime, default fmt
my $ts = Medusa::XS::format_time(0);            # localtime
my $ts = Medusa::XS::format_time(1, '%Y-%m-%d');

Returns a formatted timestamp string. First argument selects gmtime (true) or localtime (false); second is an optional strftime format.

collect_caller_stack

my $stack = Medusa::XS::collect_caller_stack();

Walks Perl's context stack in C and returns a string like "main:12->Foo::Bar:34->Baz::Qux:56".

clean_dumper

my $cleaned = Medusa::XS::clean_dumper($input);

Legacy pass-through kept for backward compatibility. Returns the input unchanged. Previously cleaned Data::Dumper output, but Loo's terse mode makes this unnecessary.

dump_sv

my $str = Medusa::XS::dump_sv($value);

Serialises any Perl value to a compact string using the Loo library. Supports scalars, array/hash refs, blessed objects, coderefs, regexps, and circular references. Respects the colour and colour_theme settings in $LOG{OPTIONS}. Used internally for argument logging.

is_audited

if (Medusa::XS::is_audited(\&Some::Sub)) { ... }

Returns true if the code reference has been wrapped with :Audit.

wrap_sub

Medusa::XS::wrap_sub(\&Some::Sub);
Medusa::XS::wrap_sub(\&Some::Sub, 'method_name');

Programmatically wraps a subroutine with audit logging (same effect as the :Audit attribute). Optional second argument overrides the method name used in log messages.

log_message

Medusa::XS::log_message(
    message => 'something happened',
    guid    => $guid,
    caller  => $stack,
    params  => \@args,
    prefix  => 'arg',
);

Low-level: formats and dispatches a single log entry through the configured FORMAT_MESSAGE and logger.

xs_format_message

my $line = Medusa::XS::xs_format_message(%params);

The default FORMAT_MESSAGE implementation, written in C. Accepts the same keys as log_message and returns a formatted string.

init_logger

Medusa::XS::init_logger();

Calls $LOG{LOG_INIT} if $LOG{LOG} is not yet set. Normally called automatically on the first audited subroutine call.

DEPENDENCIES

Horus — pure C UUID library (header-only, bundled).

Loo — pure XS data serialiser with colour support (header-only, bundled).

Neither requires separate installation; both are compiled directly into Medusa::XS.

SEE ALSO

Medusa::XS::Logger — the default XS file logger.

Medusa — the pure-Perl original that this module replaces.

LICENSE

This module is free software; you may redistribute and/or modify it under the same terms as Perl itself.