=encoding utf8

=head1 NAME

OpenTelemetry::Instrumentation - Top-level interface for OpenTelemetry instrumentations

=head1 SYNOPSIS

    # Load instrumentations for specific modules
    use OpenTelemetry::Instrumentation qw( HTTP::Tiny DBI );

    # Some instrumentations can take additional options
    use OpenTelemetry::Instrumentation (
        'HTTP::Tiny' => \%options,
        'DBI'        => \@ordered_options,
    );

    # Load every available instrumentation
    use OpenTelemetry::Instrumentation -all;

=head1 DESCRIPTION

This is the base class for handling tracing instrumentation with other CPAN
modules.

It provides functionality for loading available instrumentations
via the import list on a C<use> statement:

=over 4

=item *

with C<-all>, all available OpenTelemetry instrumentations will be loaded,
if the module the instrumentation is for has already been loaded

=item *

with a specific list of modules, they will be loaded (even if they haven't
been before) and instrumentations for them will be applied if available

=back

This means that you can expect L<HTTP::Tiny> to be traced if you have the
L<OpenTelemetry::Instrumentation::HTTP::Tiny> module installed and you do this:

    use OpenTelemetry::Instrumentation 'HTTP::Tiny';

or this:

    use HTTP::Tiny;
    use OpenTelemetry::Instrumentation -all;

but it will B<not> be traced if you do this:

    use OpenTelemetry::Instrumentation -all;
    use HTTP::Tiny;

The rationale behind this apparently inconsistent behaviour is that, with a
large install, C<:all> might load unexpected instrumentations. This behaviour
allows you instead to add this line after your module imports, and any
functionality that is actively being used in the code (for which an
instrumentation module is available) would gain tracing.

=head2 Configuration

Some instrumentation libraries can take configuration parameters. These can
be passed during import following the relevant instrumentation name, either as
a hash reference if their order does not matter, or as an array reference if
their order is important. Regardless, these wil be passed to the C<install>
method of the instrumentation, as a list.

In the following example

    use OpenTelemetry::Instrumentation (
        'No::Options',
        'Unordered::Options' => \%options,
        'Ordered::Options'   => \@options,
    );

the instrumentation for No::Options would be loaded without parameters,
and that for Unordered::Options and Ordered::Options would be called with
unordered and ordered options accordingly.

Please refer to the documentation for each instrumentation library for
details on what these options are and how to use them.

=head2 Legacy namespace

With the release of version 0.026, the namespace for instrumentation
libraries moved from L<OpenTelemetry::Integration> to this one. The legacy
namespace is still supported for backwards compatibility, but should not be
used for new code.

To support it, this module will look for instrumentation libraries in both
namespaces, and prefer the ones in the new namespace over those in the legacy
one. Instrumentation libraries in the legacy namespace will only be loaded if
no equivalent instrumentation exists in the new namespace.

This behaviour is enabled by default, but can be disabled by setting the
C<OTEL_PERL_USE_LEGACY_INSTRUMENTATIONS> environment variable to a false value.

=head1 WRITING INTEGRATIONS

Additional instrumentations can be written and used as long as they are in the
the OpenTelemetry::Instrumentation namespace. They should subclass this module
(OpenTelemetry::Instrumentation) and should implement an C<install> class method
as described in detail below. Other methods described in this section are
optional, but can be used to provide additional features.

=head2 install

    $bool = $class->install(%configuration);

Installs the instrumentation. Will be called with a (possibly empty) list of
key/value pairs with configuration options, and is expected to return a
true value if the instrumentation was installed, or false otherwise.

An example implementation of this method might look like the following:

    package OpenTelemetry::Instrumentation::Foo::Bar;

    use experimental 'signatures';
    use Class::Inspector;
    use Class::Method::Modifiers 'install_modifier';

    my $loaded;
    sub install ( $class, %config ) {
        return if $loaded++;
        return unless Class::Inspector->loaded('Foo::Bar');

        install_modifier 'Foo::Bar' => around => reticulate_splines => sub {
            my ( $orig, $self, @args ) = @_;
            ...
            my $value = $self->$orig(@args);
            ...
            return $value;
        };

        return 1;
    }

=head2 dependencies

    @package_names = $class->dependencies;

Should return the names of the packages that should be loaded to install
this instrumentation. This method will be called in list context when loading
dependencies automatically (ie. not when using the C<all> flag) to load
the dependencies before calling L</install>.

=head1 UTILITY METHODS

These methods are not intended to be overriden by specific instrumentations,
but are made available to allow for some degree of introspection.

=head2 for_package

    $class = OpenTelemetry::Instrumentation->for_package($package);

Takes a package name and checks the installed instrumentations for one that
applies to this package. If one if found, it will return the package name
of that instrumentation library. Otherwise, it will return undefined.

Available from 0.032.

=head1 COPYRIGHT

This software is copyright (c) 2023 by José Joaquín Atria.

This is free software; you can redistribute it and/or modify it under the same
terms as the Perl 5 programming language system itself.
