update
This commit is contained in:
BIN
phoenix/deps/telemetry/.hex
Normal file
BIN
phoenix/deps/telemetry/.hex
Normal file
Binary file not shown.
141
phoenix/deps/telemetry/CHANGELOG.md
Normal file
141
phoenix/deps/telemetry/CHANGELOG.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.4.1](https://github.com/elixir-telemetry/telemetry/tree/v1.4.1)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Avoid crashes when `telemetry` is invoked before started (such as during Elixir compile-time)
|
||||
|
||||
## [1.4.0](https://github.com/elixir-telemetry/telemetry/tree/v1.4.0)
|
||||
|
||||
### Added
|
||||
|
||||
- Add `telemetry:persist/0` which uses persistent term for faster dispatches (writes are extremely discouraged after persist)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix the `telemetry:span_function/0` type
|
||||
|
||||
## [1.3.0](https://github.com/elixir-telemetry/telemetry/tree/v1.3.0)
|
||||
|
||||
### Added
|
||||
|
||||
- Ability to return extra measurements from `telemetry:span/3`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Rewrite docs from edoc to OTP 27 `-moduledoc`/`-doc`.
|
||||
|
||||
Internal macros `?DOC` and `?MODULEDOC` are used. They are no-ops prior to OTP 27.
|
||||
|
||||
## [1.2.1](https://github.com/elixir-telemetry/telemetry/tree/v1.2.0)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a local handler warning log when using `telemetry_test`. (#124)
|
||||
|
||||
## [1.2.0](https://github.com/elixir-telemetry/telemetry/tree/v1.2.0)
|
||||
|
||||
### Added
|
||||
|
||||
- Added `telemetry_test` module for testing telemetry events. (#118)
|
||||
|
||||
## [1.1.0](https://github.com/elixir-telemetry/telemetry/tree/v1.1.0)
|
||||
|
||||
### Added
|
||||
|
||||
- Added `monotonic_time` measurement to all span events. (#92)
|
||||
- Added a `[telemetry, handler, failure]` event emitted when any handler fails and is detached. (#98)
|
||||
- Added a `mix.exs` file, lowering the memory footprint of compilation in Elixir projects. (#103)
|
||||
|
||||
## [1.0.0](https://github.com/elixir-telemetry/telemetry/tree/v1.0.0)
|
||||
|
||||
There are no changes in the 1.0.0 release - it marks the stability of the API.
|
||||
|
||||
## [0.4.3](https://github.com/elixir-telemetry/telemetry/tree/v0.4.3)
|
||||
|
||||
This release improves the `telemetry:span/3` function by adding the `telemetry_span_context` metadata
|
||||
to all span events. The new metadata enables correlating span events that belong to the same span.
|
||||
|
||||
### Added
|
||||
|
||||
- Added `telemetry_span_context` metadata to all events emitted by `telemetry:span/3`.
|
||||
|
||||
## [0.4.2](https://github.com/elixir-telemetry/telemetry/tree/v0.4.2)
|
||||
|
||||
### Added
|
||||
|
||||
- Added the `telemetry:span/3` function.
|
||||
|
||||
## [0.4.1](https://github.com/elixir-telemetry/telemetry/tree/v0.4.1)
|
||||
|
||||
Apart from the code changes listed below, this release includes a few improvements
|
||||
to the documentation.
|
||||
|
||||
### Changed
|
||||
|
||||
- Calls to `execute/3` when `telemetry` application is not started no longer cause an error.
|
||||
|
||||
## [0.4.0](https://github.com/elixir-telemetry/telemetry/tree/v0.4.0)
|
||||
|
||||
A single event value has been replaced by a map of measurements. Now it is up to the consumer of the
|
||||
event to decide what part of the payload is important. This is useful in cases where event indicates
|
||||
that a thing happened but there are many properties describing it. For example, a database query
|
||||
event may include total time, decode time, wait time and other measurements.
|
||||
|
||||
### Changed
|
||||
|
||||
- `execute/3` now accepts a map of measurements instead of event value
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `:telemetry.execute/3` with an event value in favour of `:telemetry.execute/3` with a map of
|
||||
measurements. If the event value is provided, it is put in a map under a `:value` key and provided
|
||||
as measurements to a handler function.
|
||||
|
||||
## [0.3.0](https://github.com/elixir-telemetry/telemetry/tree/v0.3.0)
|
||||
|
||||
This release marks the conversion from Elixir to Erlang. This is a breaking change, but the benefits
|
||||
largely surpass the drawbacks - Telemetry core can now be used by all projects running on the BEAM,
|
||||
regardless of the language they're written in.
|
||||
|
||||
### Added
|
||||
|
||||
- Added `:telemetry.handler/0`, `:telemetry.handler_function/0` and `:telemetry.handler_config/0`
|
||||
types.
|
||||
|
||||
### Changed
|
||||
|
||||
- The library has been rewritten to Erlang. In Elixir, `:telemetry` module has to be used in place
|
||||
of `Telemetry`. In Erlang, `telemetry` module has to be used in place of `'Elixir.Telemetry'`;
|
||||
- `:telemetry.list_handlers/1` returns a list of maps (of type `:telemetry.handler/0`) instead of
|
||||
a list of tuples;
|
||||
- `:telemetry.attach/4` and `:telemetry.attach_many/4` replaced the 5-arity versions and now accept
|
||||
an anonymous function for the handler function instead of a module and function name.
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed `:telemetry.attach/5` and `:telemetry.attach_many/5` - 4-arity versions need to be used
|
||||
now instead.
|
||||
|
||||
## [0.2.0](https://github.com/elixir-telemetry/telemetry/tree/v0.2.0)
|
||||
|
||||
The main point of this release is to mark base Telemetry API as stable, so that other libraries can
|
||||
rely on it without worrying about backwards compatibility.
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed `Telemetry.attach/4` and `Telemetry.attach_many/4` - the handler config is now required.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed type specs which were producing Dialyzer errors.
|
||||
|
||||
## [0.1.0](https://github.com/elixir-telemetry/telemetry/tree/v0.1.0)
|
||||
|
||||
First release of Telemetry library.
|
||||
177
phoenix/deps/telemetry/LICENSE
Normal file
177
phoenix/deps/telemetry/LICENSE
Normal file
@@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
13
phoenix/deps/telemetry/NOTICE
Normal file
13
phoenix/deps/telemetry/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright (c) 2018, Chris McCord and Erlang Solutions
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
260
phoenix/deps/telemetry/README.md
Normal file
260
phoenix/deps/telemetry/README.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# Telemetry
|
||||
|
||||
[](https://codecov.io/gh/beam-telemetry/telemetry/branch/master/graphs/badge.svg)
|
||||
|
||||
[Documentation](https://hexdocs.pm/telemetry/)
|
||||
|
||||
Telemetry is a lightweight library for dynamic dispatching of events, with a focus on
|
||||
metrics and instrumentation. Any Erlang or Elixir library can use `telemetry` to emit
|
||||
events. Application code and other libraries can then hook into those events and run
|
||||
custom handlers.
|
||||
|
||||
> Note: this library is agnostic to tooling and therefore is not directly related to
|
||||
> OpenTelemetry. For OpenTelemetry in the Erlang VM, see
|
||||
> [opentelemetry-erlang](https://github.com/open-telemetry/opentelemetry-erlang), and check
|
||||
> [opentelemetry_telemetry](https://github.com/open-telemetry/opentelemetry-erlang-contrib/tree/main/utilities/opentelemetry_telemetry)
|
||||
> to connect both libraries.
|
||||
|
||||
## Usage
|
||||
|
||||
In a nutshell, you register a custom module and function to be invoked for certain events,
|
||||
which are executed whenever there is such an event. The event name is a list of atoms. Each event is
|
||||
composed of a numeric value and can have metadata attached to it. Let's look at an example.
|
||||
|
||||
Imagine that you have a web application and you'd like to log latency and response status for each
|
||||
incoming request. With Telemetry, you can build a module which does exactly that whenever a response
|
||||
is sent. The first step is to execute a measurement.
|
||||
|
||||
In Elixir:
|
||||
|
||||
```elixir
|
||||
:telemetry.execute(
|
||||
[:web, :request, :done],
|
||||
%{latency: latency},
|
||||
%{request_path: path, status_code: status}
|
||||
)
|
||||
```
|
||||
|
||||
In Erlang:
|
||||
|
||||
```erlang
|
||||
telemetry:execute(
|
||||
[web, request, done],
|
||||
#{latency => Latency},
|
||||
#{request_path => Path, status_code => Status}
|
||||
)
|
||||
```
|
||||
|
||||
Then you can create a module to be invoked whenever the event happens.
|
||||
|
||||
In Elixir:
|
||||
|
||||
```elixir
|
||||
defmodule LogResponseHandler do
|
||||
require Logger
|
||||
|
||||
def handle_event([:web, :request, :done], measurements, metadata, _config) do
|
||||
Logger.info(
|
||||
"[#{metadata.request_path}] #{metadata.status_code} sent in #{measurements.latency}"
|
||||
)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
In Erlang:
|
||||
|
||||
```erlang
|
||||
-module(log_response_handler).
|
||||
|
||||
-include_lib("kernel/include/logger.hrl").
|
||||
|
||||
handle_event([web, request, done], #{latency := Latency}, #{request_path := Path,
|
||||
status_code := Status}, _Config) ->
|
||||
?LOG_INFO("[~s] ~p sent in ~p", [Path, Status, Latency]).
|
||||
|
||||
```
|
||||
|
||||
**Important note:**
|
||||
|
||||
The `handle_event` callback of each handler is invoked synchronously on each `telemetry:execute` call.
|
||||
Therefore, it is extremely important to avoid blocking operations. If you need to perform any action
|
||||
that is not immediate, consider offloading the work to a separate process (or a pool of processes)
|
||||
by sending a message.
|
||||
|
||||
Finally, all you need to do is to attach the module to the executed event.
|
||||
|
||||
In Elixir:
|
||||
|
||||
```elixir
|
||||
:ok =
|
||||
:telemetry.attach(
|
||||
# unique handler id
|
||||
"log-response-handler",
|
||||
[:web, :request, :done],
|
||||
&LogResponseHandler.handle_event/4,
|
||||
nil
|
||||
)
|
||||
```
|
||||
|
||||
In Erlang:
|
||||
|
||||
```erlang
|
||||
ok = telemetry:attach(
|
||||
%% unique handler id
|
||||
<<"log-response-handler">>,
|
||||
[web, request, done],
|
||||
fun log_response_handler:handle_event/4,
|
||||
[]
|
||||
)
|
||||
```
|
||||
|
||||
You might think that it isn't very useful, because you could just as well write a log statement
|
||||
instead of calling `telemetry:execute/3` – and you would be right! But now imagine that each Elixir library
|
||||
would publish its own set of events with information useful for introspection. Currently each library
|
||||
rolls their own instrumentation layer – Telemetry aims to provide a single interface for these use
|
||||
cases across the whole ecosystem.
|
||||
|
||||
### Spans
|
||||
|
||||
In order to provide uniform events that capture the start and end of discrete events, it is recommended
|
||||
that you use the `telemetry:span/3` call. This function will generate a start event and a stop or exception
|
||||
event depending on whether the provided function executed successfully or raised an error. Under the hood,
|
||||
the `telemetry:span/3` function leverages the `telemetry:execute/3` function, so all the same usage patterns
|
||||
apply. If an exception does occur, an `EventPrefix ++ [exception]` event will be emitted and the caught error
|
||||
will be re-raised.
|
||||
|
||||
The measurements for the `EventPrefix ++ [start]` event will contain a key called `system_time` which is
|
||||
derived by calling `erlang:system_time/0`. For `EventPrefix ++ [stop]` and `EventPrefix ++ [exception]`
|
||||
events, the measurements will contain a key called `duration` and its value is derived by calling
|
||||
`erlang:monotonic_time() - StartMonotonicTime`. All events include a `monotonic_time` measurement too.
|
||||
All of them represent time as native units.
|
||||
|
||||
To convert the duration from native units you can use:
|
||||
|
||||
```elixir
|
||||
milliseconds = System.convert_time_unit(duration, :native, :millisecond)
|
||||
```
|
||||
|
||||
To create span events you would do something like this:
|
||||
|
||||
In Elixir:
|
||||
|
||||
```elixir
|
||||
def process_message(message) do
|
||||
start_metadata = %{message: message}
|
||||
|
||||
result =
|
||||
:telemetry.span(
|
||||
[:worker, :processing],
|
||||
start_metadata,
|
||||
fn ->
|
||||
result = ... # Process the message
|
||||
{result, %{metadata: "Information related to the processing of the message"}}
|
||||
end
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
In Erlang:
|
||||
|
||||
```erlang
|
||||
process_message(Message) ->
|
||||
StartMetadata = #{message => Message},
|
||||
Result = telemetry:span(
|
||||
[worker, processing],
|
||||
StartMetadata,
|
||||
fun() ->
|
||||
Result = % Process the message
|
||||
{Result, #{metadata => "Information related to the processing of the message"}}
|
||||
end
|
||||
).
|
||||
```
|
||||
|
||||
To then attach to the events that `telemetry:span/3` emits you would do the following:
|
||||
|
||||
In Elixir:
|
||||
|
||||
```elixir
|
||||
:ok =
|
||||
:telemetry.attach_many(
|
||||
"log-response-handler",
|
||||
[
|
||||
[:worker, :processing, :start],
|
||||
[:worker, :processing, :stop],
|
||||
[:worker, :processing, :exception]
|
||||
],
|
||||
&LogResponseHandler.handle_event/4,
|
||||
nil
|
||||
)
|
||||
```
|
||||
|
||||
In Erlang:
|
||||
|
||||
```erlang
|
||||
ok = telemetry:attach_many(
|
||||
<<"log-response-handler">>,
|
||||
[
|
||||
[worker, processing, start],
|
||||
[worker, processing, stop],
|
||||
[worker, processing, exception]
|
||||
],
|
||||
fun log_response_handler:handle_event/4,
|
||||
[]
|
||||
)
|
||||
```
|
||||
|
||||
With the following event handler module defined:
|
||||
|
||||
In Elixir:
|
||||
|
||||
```elixir
|
||||
defmodule LogResponseHandler do
|
||||
require Logger
|
||||
|
||||
def handle_event(event, measurements, metadata, _config) do
|
||||
Logger.info("Event: #{inspect(event)}")
|
||||
Logger.info("Measurements: #{inspect(measurements)}")
|
||||
Logger.info("Metadata: #{inspect(metadata)}")
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
In Erlang:
|
||||
|
||||
```erlang
|
||||
-module(log_response_handler).
|
||||
|
||||
-include_lib("kernel/include/logger.hrl").
|
||||
|
||||
handle_event(Event, Measurements, Metadata, _Config) ->
|
||||
?LOG_INFO("Event: ~p", [Event]),
|
||||
?LOG_INFO("Measurements: ~p", [Measurements]),
|
||||
?LOG_INFO("Metadata: ~p", [Metadata]).
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
Telemetry is available on [Hex](https://hex.pm/packages/telemetry). To install, just add it to
|
||||
your dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
defp deps() do
|
||||
[
|
||||
{:telemetry, "~> 1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
or `rebar.config`:
|
||||
|
||||
```erlang
|
||||
{deps, [{telemetry, "~> 1.0"}]}.
|
||||
```
|
||||
|
||||
## Copyright and License
|
||||
|
||||
Copyright (c) 2019 Erlang Ecosystem Foundation and Erlang Solutions.
|
||||
|
||||
Telemetry's source code is released under the Apache License, Version 2.0.
|
||||
|
||||
See the [LICENSE](LICENSE) and [NOTICE](NOTICE) files for more information.
|
||||
Binary file not shown.
17
phoenix/deps/telemetry/hex_metadata.config
Normal file
17
phoenix/deps/telemetry/hex_metadata.config
Normal file
@@ -0,0 +1,17 @@
|
||||
{<<"app">>,<<"telemetry">>}.
|
||||
{<<"build_tools">>,[<<"rebar3">>]}.
|
||||
{<<"description">>,
|
||||
<<"Dynamic dispatching library for metrics and instrumentations">>}.
|
||||
{<<"files">>,
|
||||
[<<"CHANGELOG.md">>,<<"LICENSE">>,<<"NOTICE">>,<<"README.md">>,<<"mix.exs">>,
|
||||
<<"rebar.config">>,<<"rebar.lock">>,<<"src">>,<<"src/telemetry.app.src">>,
|
||||
<<"src/telemetry.erl">>,<<"src/telemetry.hrl">>,<<"src/telemetry_app.erl">>,
|
||||
<<"src/telemetry_ets.erl">>,<<"src/telemetry_handler_table.erl">>,
|
||||
<<"src/telemetry_pt.erl">>,<<"src/telemetry_sup.erl">>,
|
||||
<<"src/telemetry_test.erl">>]}.
|
||||
{<<"licenses">>,[<<"Apache-2.0">>]}.
|
||||
{<<"links">>,
|
||||
[{<<"GitHub">>,<<"https://github.com/beam-telemetry/telemetry">>}]}.
|
||||
{<<"name">>,<<"telemetry">>}.
|
||||
{<<"requirements">>,[]}.
|
||||
{<<"version">>,<<"1.4.1">>}.
|
||||
20
phoenix/deps/telemetry/mix.exs
Normal file
20
phoenix/deps/telemetry/mix.exs
Normal file
@@ -0,0 +1,20 @@
|
||||
# This file is only used by Telemetry as a dependency.
|
||||
# Use rebar3 instead for compiling, running tests, etc.
|
||||
defmodule Telemetry.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
{:ok, [{:application, :telemetry, props}]} = :file.consult("src/telemetry.app.src")
|
||||
@props Keyword.take(props, [:applications, :description, :env, :mod, :vsn])
|
||||
|
||||
def application do
|
||||
@props
|
||||
end
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :telemetry,
|
||||
version: to_string(application()[:vsn]),
|
||||
language: :erlang
|
||||
]
|
||||
end
|
||||
end
|
||||
32
phoenix/deps/telemetry/rebar.config
Normal file
32
phoenix/deps/telemetry/rebar.config
Normal file
@@ -0,0 +1,32 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
{deps, []}.
|
||||
|
||||
{profiles, [
|
||||
{test, [{erl_opts, [nowarn_export_all]},
|
||||
%% create junit xml for circleci
|
||||
{ct_opts, [{ct_hooks, [cth_surefire]}]},
|
||||
{cover_enabled, true},
|
||||
{cover_opts, [verbose]},
|
||||
%% convert to data codecov understands
|
||||
{plugins, [covertool]},
|
||||
{covertool, [{coverdata_files, ["ct.coverdata"]}]}
|
||||
]}
|
||||
]}.
|
||||
|
||||
{shell, [{apps, [telemetry]}]}.
|
||||
|
||||
%% take out warnings for unused exported functions
|
||||
{xref_checks,[undefined_function_calls, undefined_functions, locals_not_used,
|
||||
deprecated_function_calls, deprecated_functions]}.
|
||||
|
||||
{project_plugins, [rebar3_hex, rebar3_ex_doc]}.
|
||||
|
||||
{hex, [
|
||||
{doc, #{provider => ex_doc}}
|
||||
]}.
|
||||
|
||||
{ex_doc, [
|
||||
{source_url, <<"https://github.com/beam-telemetry/telemetry">>},
|
||||
{extras, [<<"README.md">>, <<"CHANGELOG.md">>, <<"LICENSE">>, <<"NOTICE">>]},
|
||||
{main, <<"readme">>}
|
||||
]}.
|
||||
1
phoenix/deps/telemetry/rebar.lock
Normal file
1
phoenix/deps/telemetry/rebar.lock
Normal file
@@ -0,0 +1 @@
|
||||
[].
|
||||
13
phoenix/deps/telemetry/src/telemetry.app.src
Normal file
13
phoenix/deps/telemetry/src/telemetry.app.src
Normal file
@@ -0,0 +1,13 @@
|
||||
{application,telemetry,
|
||||
[{description,"Dynamic dispatching library for metrics and instrumentations"},
|
||||
{vsn,"1.4.1"},
|
||||
{registered,[]},
|
||||
{mod,{telemetry_app,[]}},
|
||||
{applications,[kernel,stdlib]},
|
||||
{env,[]},
|
||||
{modules,[]},
|
||||
{licenses,["Apache-2.0"]},
|
||||
{links,[{"GitHub",
|
||||
"https://github.com/beam-telemetry/telemetry"}]},
|
||||
{doc,"doc"},
|
||||
{include_files,["mix.exs"]}]}.
|
||||
457
phoenix/deps/telemetry/src/telemetry.erl
Normal file
457
phoenix/deps/telemetry/src/telemetry.erl
Normal file
@@ -0,0 +1,457 @@
|
||||
-module(telemetry).
|
||||
|
||||
-export([attach/4,
|
||||
attach_many/4,
|
||||
persist/0,
|
||||
detach/1,
|
||||
list_handlers/1,
|
||||
execute/2,
|
||||
execute/3,
|
||||
span/3]).
|
||||
|
||||
-export([report_cb/1]).
|
||||
|
||||
-include("telemetry.hrl").
|
||||
|
||||
?MODULEDOC("""
|
||||
`telemetry` allows you to invoke certain functions whenever a
|
||||
particular event is emitted.
|
||||
|
||||
For more information see the documentation for `attach/4`, `attach_many/4`
|
||||
and `execute/2`.
|
||||
""").
|
||||
|
||||
-type handler_id() :: term().
|
||||
-type event_name() :: [atom(), ...].
|
||||
-type event_measurements() :: map().
|
||||
-type event_metadata() :: map().
|
||||
-type event_value() :: number().
|
||||
-type event_prefix() :: [atom()].
|
||||
-type handler_config() :: term().
|
||||
-type handler_function() :: fun((event_name(), event_measurements(), event_metadata(), handler_config()) -> any()).
|
||||
-type span_result() :: term().
|
||||
-type span_function() :: fun(() -> {span_result(), event_metadata()} | {span_result(), event_measurements(), event_metadata()}).
|
||||
-type handler() :: #{id := handler_id(),
|
||||
event_name := event_name(),
|
||||
function := handler_function(),
|
||||
config := handler_config()}.
|
||||
|
||||
-export_type([handler_id/0,
|
||||
event_name/0,
|
||||
event_measurements/0,
|
||||
event_metadata/0,
|
||||
event_value/0,
|
||||
event_prefix/0,
|
||||
handler_config/0,
|
||||
handler_function/0,
|
||||
handler/0,
|
||||
span_result/0,
|
||||
span_function/0]).
|
||||
|
||||
-import_lib("kernel/import/logger.hrl").
|
||||
|
||||
?DOC("""
|
||||
Attaches the handler to the event.
|
||||
|
||||
`HandlerId` must be unique, if another handler with the same ID already exists the
|
||||
`{error, already_exists}` tuple is returned.
|
||||
|
||||
See `execute/3` to learn how the handlers are invoked.
|
||||
|
||||
> #### Function Captures {: .info}
|
||||
>
|
||||
> Due to how anonymous functions are implemented in the Erlang VM, it is best to use
|
||||
> function captures (`fun mod:fun/4` in Erlang or `&Mod.fun/4` in Elixir) as event handlers
|
||||
> to achieve the best performance. In other words, avoid using literal anonymous functions
|
||||
> (`fun(...) -> ... end` or `fn ... -> ... end`) or local function captures (`fun handle_event/4`
|
||||
> or `&handle_event/4`) as event handlers.
|
||||
|
||||
All the handlers are executed by the process dispatching event. If the function fails (raises,
|
||||
exits or throws) then the handler is removed and a failure event is emitted.
|
||||
|
||||
Note that you should not rely on the order in which handlers are invoked.
|
||||
|
||||
### Failing Handlers
|
||||
|
||||
When a handler fails, it is removed and a **failure event** is emitted.
|
||||
This is useful for monitoring and diagnostic purposes.
|
||||
|
||||
Handler failure events are executed as:
|
||||
|
||||
* Event name: `[telemetry, handler, failure]`
|
||||
* Measurements:
|
||||
* `monotonic_time` - The current monotonic time in native units from calling
|
||||
`erlang:monotonic_time/0`
|
||||
* `system_time` - The current system time in native units from calling
|
||||
`erlang:system_time/0`
|
||||
* Metadata:
|
||||
* `event_name` - The event that failed (`t:event_name/0`)
|
||||
* `handler_id` - The ID of the handler that failed
|
||||
* `handler_config` - The configuration of the handler that failed
|
||||
* `kind` - The kind of failure (`error`, `exit`, `throw`)
|
||||
* `reason` - The reason for the failure
|
||||
* `stacktrace` - The stacktrace for the failure
|
||||
|
||||
These handler failure events should only be used for monitoring and diagnostic purposes.
|
||||
Re-attaching a failed handler will likely result in the handler failing again.
|
||||
""").
|
||||
-spec attach(HandlerId, EventName, Function, Config) -> ok | {error, already_exists} when
|
||||
HandlerId :: handler_id(),
|
||||
EventName :: event_name(),
|
||||
Function :: handler_function(),
|
||||
Config :: handler_config().
|
||||
attach(HandlerId, EventName, Function, Config) ->
|
||||
attach_many(HandlerId, [EventName], Function, Config).
|
||||
|
||||
?DOC("""
|
||||
Attaches the handler to many events.
|
||||
|
||||
The handler will be invoked whenever any of the events in the `EventNames` list is emitted. Note
|
||||
that failure of the handler on any of these invocations will detach it from all the events in
|
||||
`EventNames` (the same applies to manual detaching using `detach/1`).
|
||||
|
||||
<b>Note:</b> due to how anonymous functions are implemented in the Erlang VM, it is best to use
|
||||
function captures (i.e. `fun mod:fun/4` in Erlang or `&Mod.fun/4` in Elixir) as event handlers
|
||||
to achieve maximum performance. In other words, avoid using literal anonymous functions
|
||||
(`fun(...) -> ... end` or `fn ... -> ... end`) or local function captures (`fun handle_event/4`
|
||||
or `&handle_event/4`) as event handlers.
|
||||
|
||||
All the handlers are executed by the process dispatching event. If the function fails (raises,
|
||||
exits or throws) a handler failure event is emitted and then the handler is removed.
|
||||
|
||||
Failing handlers emit a failure event, which is documented in `attach/4`.
|
||||
|
||||
Note that you should not rely on the order in which handlers are invoked.
|
||||
""").
|
||||
-spec attach_many(HandlerId, [EventName], Function, Config) -> ok | {error, already_exists} when
|
||||
HandlerId :: handler_id(),
|
||||
EventName :: event_name(),
|
||||
Function :: handler_function(),
|
||||
Config :: handler_config().
|
||||
attach_many(HandlerId, EventNames, Function, Config) when is_function(Function, 4) ->
|
||||
assert_event_names(EventNames),
|
||||
case erlang:fun_info(Function, type) of
|
||||
{type, external} ->
|
||||
ok;
|
||||
{type, local} ->
|
||||
?LOG_INFO(#{handler_id => HandlerId,
|
||||
event_names => EventNames,
|
||||
function => Function,
|
||||
config => Config,
|
||||
type => local},
|
||||
#{report_cb => fun ?MODULE:report_cb/1})
|
||||
end,
|
||||
telemetry_handler_table:insert(HandlerId, EventNames, Function, Config).
|
||||
|
||||
?DOC("""
|
||||
Persist telemetry handlers.
|
||||
|
||||
This will improve performance of calling Telemetry handlers at the cost of
|
||||
reducing performance of attaching or detaching new handlers.
|
||||
|
||||
This function should be used with care.
|
||||
""").
|
||||
?DOC_SINCE("1.4.0").
|
||||
-spec persist() -> ok.
|
||||
persist() ->
|
||||
telemetry_handler_table:persist().
|
||||
|
||||
?DOC("""
|
||||
Removes the existing handler.
|
||||
|
||||
If the handler with given ID doesn't exist, `{error, not_found}` is returned.
|
||||
""").
|
||||
-spec detach(handler_id()) -> ok | {error, not_found}.
|
||||
detach(HandlerId) ->
|
||||
telemetry_handler_table:delete(HandlerId).
|
||||
|
||||
?DOC("""
|
||||
Emits the event, invoking handlers attached to it.
|
||||
|
||||
When the event is emitted, the handler function provided to `attach/4` is called with four
|
||||
arguments:
|
||||
|
||||
* the event name
|
||||
* the map of measurements
|
||||
* the map of event metadata
|
||||
* the handler configuration given to `attach/4`
|
||||
|
||||
#### Best practices and conventions:
|
||||
|
||||
While you are able to emit messages of any `t:event_name/0` structure, it is recommended that you follow the
|
||||
the guidelines laid out in `span/3` if you are capturing start/stop events.
|
||||
""").
|
||||
-spec execute(EventName, Measurements, Metadata) -> ok when
|
||||
EventName :: event_name(),
|
||||
Measurements :: event_measurements() | event_value(),
|
||||
Metadata :: event_metadata().
|
||||
execute(EventName, Value, Metadata) when is_number(Value) ->
|
||||
?LOG_WARNING("Using execute/3 with a single event value is deprecated. "
|
||||
"Use a measurement map instead.", []),
|
||||
execute(EventName, #{value => Value}, Metadata);
|
||||
execute([_ | _] = EventName, Measurements, Metadata) when is_map(Measurements) and is_map(Metadata) ->
|
||||
Handlers = telemetry_handler_table:list_for_event(EventName),
|
||||
do_execute(Handlers, EventName, Measurements, Metadata).
|
||||
|
||||
do_execute([], _EventName, _Measurements, _Metadata) -> ok;
|
||||
do_execute([Handler | Rest], EventName, Measurements, Metadata) ->
|
||||
#handler{id=HandlerId,
|
||||
function=HandlerFunction,
|
||||
config=Config} = Handler,
|
||||
try
|
||||
HandlerFunction(EventName, Measurements, Metadata, Config)
|
||||
catch
|
||||
?WITH_STACKTRACE(Class, Reason, Stacktrace)
|
||||
detach(HandlerId),
|
||||
FailureMetadata = #{event_name => EventName,
|
||||
handler_id => HandlerId,
|
||||
handler_config => Config,
|
||||
kind => Class,
|
||||
reason => Reason,
|
||||
stacktrace => Stacktrace},
|
||||
FailureMeasurements = #{monotonic_time => erlang:monotonic_time(), system_time => erlang:system_time()},
|
||||
execute([telemetry, handler, failure], FailureMeasurements, FailureMetadata),
|
||||
?LOG_ERROR("Handler ~p has failed and has been detached. "
|
||||
"Class=~p~nReason=~p~nStacktrace=~p~n",
|
||||
[HandlerId, Class, Reason, Stacktrace])
|
||||
end,
|
||||
do_execute(Rest, EventName, Measurements, Metadata).
|
||||
|
||||
|
||||
?DOC("""
|
||||
Runs the provided `SpanFunction`, emitting start and stop/exception events, invoking the handlers attached to each.
|
||||
|
||||
The `SpanFunction` must return a `{result, stop_metadata}` or a `{result, extra_measurements, stop_metadata}` tuple.
|
||||
|
||||
When this function is called, 2 events will be emitted via `execute/3`. Those events will be one of the following
|
||||
pairs:
|
||||
|
||||
* `EventPrefix ++ [start]` and `EventPrefix ++ [stop]`
|
||||
* `EventPrefix ++ [start]` and `EventPrefix ++ [exception]`
|
||||
|
||||
However, note that in case the current process crashes due to an exit signal
|
||||
of another process, then none or only part of those events would be emitted.
|
||||
Below is a breakdown of the measurements and metadata associated with each individual event.
|
||||
|
||||
When providing `StartMetadata` and `StopMetadata`, these values will be sent independently to `start` and
|
||||
`stop` events. If an exception occurs, exception metadata will be merged onto the `StartMetadata`. In general,
|
||||
it is **highly recommended** that `StopMetadata` should include the values from `StartMetadata`
|
||||
so that handlers, such as those used for metrics, can rely entirely on the `stop` event. Failure to include
|
||||
all of `StartMetadata` in `StopMetadata` can add significant complexity to event handlers.
|
||||
|
||||
A default span context is added to event metadata under the `telemetry_span_context` key if this key is not provided
|
||||
by the user in the `StartMetadata`. This context is useful for tracing libraries to identify unique
|
||||
executions of span events within a process to match start, stop, and exception events. Metadata keys which
|
||||
should be available to both `start` and `stop` events need to supplied separately for `StartMetadata` and
|
||||
`StopMetadata`.
|
||||
|
||||
If `SpanFunction` returns `{result, extra_measurements, stop_metadata}`, then a map of extra measurements
|
||||
will be merged with the measurements automatically provided. This is useful if you want to return, for example,
|
||||
bytes from an HTTP request. The standard measurements `duration` and `monotonic_time` cannot be overridden.
|
||||
|
||||
For `telemetry` events denoting the **start** of a larger event, the following data is provided:
|
||||
|
||||
* Event:
|
||||
|
||||
```
|
||||
EventPrefix ++ [start]
|
||||
```
|
||||
|
||||
* Measurements:
|
||||
|
||||
```
|
||||
#{
|
||||
% The current system time in native units from
|
||||
% calling: erlang:system_time()
|
||||
system_time => integer(),
|
||||
monotonic_time => integer(),
|
||||
}
|
||||
```
|
||||
|
||||
* Metadata:
|
||||
|
||||
```
|
||||
#{
|
||||
telemetry_span_context => term(),
|
||||
% User defined metadata as provided in StartMetadata
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
For `telemetry` events denoting the **stop** of a larger event, the following data is provided:
|
||||
|
||||
* Event:
|
||||
|
||||
```
|
||||
EventPrefix ++ [stop]
|
||||
```
|
||||
|
||||
* Measurements:
|
||||
|
||||
```
|
||||
#{
|
||||
% The current monotonic time minus the start monotonic time in native units
|
||||
% by calling: erlang:monotonic_time() - start_monotonic_time
|
||||
duration => integer(),
|
||||
monotonic_time => integer(),
|
||||
% User defined measurements when returning `SpanFunction` as a 3 element tuple
|
||||
}
|
||||
```
|
||||
|
||||
* Metadata:
|
||||
|
||||
```
|
||||
#{
|
||||
% An optional error field if the stop event is the result of an error
|
||||
% but not necessarily an exception.
|
||||
error => term(),
|
||||
telemetry_span_context => term(),
|
||||
% User defined metadata as provided in StopMetadata
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
For `telemetry` events denoting an **exception** of a larger event, the following data is provided:
|
||||
|
||||
* Event:
|
||||
|
||||
```
|
||||
EventPrefix ++ [exception]
|
||||
```
|
||||
|
||||
* Measurements:
|
||||
|
||||
```
|
||||
#{
|
||||
% The current monotonic time minus the start monotonic time in native units
|
||||
% by calling: erlang:monotonic_time() - start_monotonic_time
|
||||
duration => integer(),
|
||||
monotonic_time => integer()
|
||||
}
|
||||
```
|
||||
|
||||
* Metadata:
|
||||
|
||||
```
|
||||
#{
|
||||
kind => throw | error | exit,
|
||||
reason => term(),
|
||||
stacktrace => list(),
|
||||
telemetry_span_context => term(),
|
||||
% User defined metadata as provided in StartMetadata
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
""").
|
||||
-spec span(event_prefix(), event_metadata(), span_function()) -> span_result().
|
||||
span(EventPrefix, StartMetadata, SpanFunction) ->
|
||||
StartTime = erlang:monotonic_time(),
|
||||
DefaultCtx = erlang:make_ref(),
|
||||
execute(
|
||||
EventPrefix ++ [start],
|
||||
#{monotonic_time => StartTime, system_time => erlang:system_time()},
|
||||
merge_ctx(StartMetadata, DefaultCtx)
|
||||
),
|
||||
|
||||
try SpanFunction() of
|
||||
{Result, StopMetadata} ->
|
||||
StopTime = erlang:monotonic_time(),
|
||||
execute(
|
||||
EventPrefix ++ [stop],
|
||||
#{duration => StopTime - StartTime, monotonic_time => StopTime},
|
||||
merge_ctx(StopMetadata, DefaultCtx)
|
||||
),
|
||||
Result;
|
||||
{Result, ExtraMeasurements, StopMetadata} ->
|
||||
StopTime = erlang:monotonic_time(),
|
||||
Measurements = maps:merge(ExtraMeasurements, #{duration => StopTime - StartTime, monotonic_time => StopTime}),
|
||||
execute(
|
||||
EventPrefix ++ [stop],
|
||||
Measurements,
|
||||
merge_ctx(StopMetadata, DefaultCtx)
|
||||
),
|
||||
Result
|
||||
|
||||
catch
|
||||
?WITH_STACKTRACE(Class, Reason, Stacktrace)
|
||||
StopTime = erlang:monotonic_time(),
|
||||
execute(
|
||||
EventPrefix ++ [exception],
|
||||
#{duration => StopTime - StartTime, monotonic_time => StopTime},
|
||||
merge_ctx(StartMetadata#{kind => Class, reason => Reason, stacktrace => Stacktrace}, DefaultCtx)
|
||||
),
|
||||
erlang:raise(Class, Reason, Stacktrace)
|
||||
end.
|
||||
|
||||
?DOC("""
|
||||
Same as [`execute(EventName, Measurements, #{})`](`execute/3`).
|
||||
""").
|
||||
-spec execute(EventName, Measurements) -> ok when
|
||||
EventName :: event_name(),
|
||||
Measurements :: event_measurements() | event_value().
|
||||
execute(EventName, Measurements) ->
|
||||
execute(EventName, Measurements, #{}).
|
||||
|
||||
?DOC("""
|
||||
Returns all handlers attached to events with given prefix.
|
||||
|
||||
Handlers attached to many events at once using `attach_many/4` will be listed once for each
|
||||
event they're attached to.
|
||||
Note that you can list all handlers by feeding this function an empty list.
|
||||
""").
|
||||
-spec list_handlers(event_prefix()) -> [handler()].
|
||||
list_handlers(EventPrefix) ->
|
||||
assert_event_prefix(EventPrefix),
|
||||
[#{id => HandlerId,
|
||||
event_name => EventName,
|
||||
function => Function,
|
||||
config => Config} || #handler{id=HandlerId,
|
||||
event_name=EventName,
|
||||
function=Function,
|
||||
config=Config} <- telemetry_handler_table:list_by_prefix(EventPrefix)].
|
||||
|
||||
%%
|
||||
|
||||
-spec assert_event_names(term()) -> [ok].
|
||||
assert_event_names(List) when is_list(List) ->
|
||||
[assert_event_name(E) || E <- List];
|
||||
assert_event_names(Term) ->
|
||||
erlang:error(badarg, Term).
|
||||
|
||||
-spec assert_event_prefix(term()) -> ok.
|
||||
assert_event_prefix(List) when is_list(List) ->
|
||||
case lists:all(fun erlang:is_atom/1, List) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
erlang:error(badarg, List)
|
||||
end;
|
||||
assert_event_prefix(List) ->
|
||||
erlang:error(badarg, List).
|
||||
|
||||
-spec assert_event_name(term()) -> ok.
|
||||
assert_event_name([_ | _] = List) ->
|
||||
case lists:all(fun erlang:is_atom/1, List) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
erlang:error(badarg, List)
|
||||
end;
|
||||
assert_event_name(Term) ->
|
||||
erlang:error(badarg, Term).
|
||||
|
||||
-spec merge_ctx(event_metadata(), any()) -> event_metadata().
|
||||
merge_ctx(#{telemetry_span_context := _} = Metadata, _Ctx) -> Metadata;
|
||||
merge_ctx(Metadata, Ctx) -> Metadata#{telemetry_span_context => Ctx}.
|
||||
|
||||
?DOC(false).
|
||||
report_cb(#{handler_id := Id}) ->
|
||||
{"The function passed as a handler with ID ~w is a local function.\n"
|
||||
"This means that it is either an anonymous function or a capture of a function "
|
||||
"without a module specified. That may cause a performance penalty when calling "
|
||||
"that handler. For more details see the note in `telemetry:attach/4` "
|
||||
"documentation.\n\n"
|
||||
"https://hexdocs.pm/telemetry/telemetry.html#attach/4", [Id]}.
|
||||
32
phoenix/deps/telemetry/src/telemetry.hrl
Normal file
32
phoenix/deps/telemetry/src/telemetry.hrl
Normal file
@@ -0,0 +1,32 @@
|
||||
-record(handler, {id :: telemetry:handler_id() | '_',
|
||||
event_name :: telemetry:event_name() | '_',
|
||||
function :: telemetry:handler_function() | '_',
|
||||
config :: telemetry:handler_config() | '_'}).
|
||||
|
||||
-ifdef('OTP_RELEASE').
|
||||
-define(WITH_STACKTRACE(T, R, S), T:R:S ->).
|
||||
-else.
|
||||
-define(WITH_STACKTRACE(T, R, S), T:R -> S = erlang:get_stacktrace(),).
|
||||
-endif.
|
||||
|
||||
-ifdef('OTP_RELEASE').
|
||||
-include_lib("kernel/include/logger.hrl").
|
||||
-else.
|
||||
-define(LOG_ERROR(Msg, Args), error_logger:error_msg(Msg, Args)).
|
||||
-endif.
|
||||
|
||||
-ifdef('OTP_RELEASE').
|
||||
-include_lib("kernel/include/logger.hrl").
|
||||
-else.
|
||||
-define(LOG_WARNING(Msg, Args), error_logger:warning_msg(Msg, Args)).
|
||||
-endif.
|
||||
|
||||
-if(?OTP_RELEASE >= 27).
|
||||
-define(MODULEDOC(Str), -moduledoc(Str)).
|
||||
-define(DOC(Str), -doc(Str)).
|
||||
-define(DOC_SINCE(Vsn), -doc(#{since => Vsn})).
|
||||
-else.
|
||||
-define(MODULEDOC(Str), -compile([])).
|
||||
-define(DOC(Str), -compile([])).
|
||||
-define(DOC_SINCE(Vsn), -compile([])).
|
||||
-endif.
|
||||
13
phoenix/deps/telemetry/src/telemetry_app.erl
Normal file
13
phoenix/deps/telemetry/src/telemetry_app.erl
Normal file
@@ -0,0 +1,13 @@
|
||||
%% @private
|
||||
-module(telemetry_app).
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
start(_StartType, _StartArgs) ->
|
||||
telemetry_sup:start_link().
|
||||
|
||||
stop(_State) ->
|
||||
ok.
|
||||
|
||||
69
phoenix/deps/telemetry/src/telemetry_ets.erl
Normal file
69
phoenix/deps/telemetry/src/telemetry_ets.erl
Normal file
@@ -0,0 +1,69 @@
|
||||
-module(telemetry_ets).
|
||||
|
||||
-include("telemetry.hrl").
|
||||
|
||||
-export([persist/1,
|
||||
list_for_event/2,
|
||||
list_by_prefix/2,
|
||||
insert/5,
|
||||
delete/2]).
|
||||
|
||||
persist(TID) ->
|
||||
Handlers = ets:tab2list(TID),
|
||||
Map = lists:foldl(fun(Handler, Acc) ->
|
||||
maps:update_with(Handler#handler.event_name,
|
||||
fun(L) -> [Handler | L] end,
|
||||
[Handler],
|
||||
Acc)
|
||||
end,
|
||||
#{},
|
||||
Handlers),
|
||||
{ok, Map}.
|
||||
|
||||
list_for_event(TID, EventName) ->
|
||||
try
|
||||
ets:lookup(TID, EventName)
|
||||
catch
|
||||
error:badarg ->
|
||||
[]
|
||||
end.
|
||||
|
||||
list_by_prefix(TID, EventPrefix) ->
|
||||
Pattern = match_pattern_for_prefix(EventPrefix),
|
||||
try
|
||||
ets:match_object(TID, Pattern)
|
||||
catch
|
||||
error:badarg ->
|
||||
[]
|
||||
end.
|
||||
|
||||
match_pattern_for_prefix(EventPrefix) ->
|
||||
#handler{event_name=match_for_prefix(EventPrefix),
|
||||
_='_'}.
|
||||
|
||||
-dialyzer({nowarn_function, match_for_prefix/1}).
|
||||
match_for_prefix([]) ->
|
||||
'_';
|
||||
match_for_prefix([Segment | Rest]) ->
|
||||
[Segment | match_for_prefix(Rest)].
|
||||
|
||||
insert(TID, HandlerId, EventNames, Function, Config) ->
|
||||
case ets:match(TID, #handler{id=HandlerId,
|
||||
_='_'}) of
|
||||
[] ->
|
||||
Objects = [#handler{id=HandlerId,
|
||||
event_name=EventName,
|
||||
function=Function,
|
||||
config=Config} || EventName <- EventNames],
|
||||
ets:insert(TID, Objects),
|
||||
{ok, TID};
|
||||
_ ->
|
||||
{error, already_exists}
|
||||
end.
|
||||
|
||||
delete(TID, HandlerId) ->
|
||||
case ets:select_delete(TID, [{#handler{id=HandlerId,
|
||||
_='_'}, [], [true]}]) of
|
||||
0 -> {error, not_found};
|
||||
_ -> {ok, TID}
|
||||
end.
|
||||
125
phoenix/deps/telemetry/src/telemetry_handler_table.erl
Normal file
125
phoenix/deps/telemetry/src/telemetry_handler_table.erl
Normal file
@@ -0,0 +1,125 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%% @private ETS table for handlers.
|
||||
%%
|
||||
%% Each handler is stored in the table. A key is an event name the
|
||||
%% handler is attached to. All writes to a table go through a single
|
||||
%% Agent process to make sure that we don't get duplicate handler IDs.
|
||||
%%
|
||||
%% Reads (`list_handlers_...') are executed by the calling process.
|
||||
%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(telemetry_handler_table).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/0,
|
||||
persist/0,
|
||||
insert/4,
|
||||
delete/1,
|
||||
list_for_event/1,
|
||||
list_by_prefix/1]).
|
||||
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
code_change/3,
|
||||
terminate/2]).
|
||||
|
||||
-compile({inline, [impl_get/0]}).
|
||||
|
||||
-include("telemetry.hrl").
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
-spec insert(HandlerId, EventNames, Function, Config) -> ok | {error, already_exists} when
|
||||
HandlerId :: telemetry:handler_id(),
|
||||
EventNames :: [telemetry:event_name()],
|
||||
Function :: telemetry:handler_function(),
|
||||
Config :: telemetry:handler_config().
|
||||
insert(HandlerId, EventNames, Function, Config) ->
|
||||
gen_server:call(?MODULE, {insert, HandlerId, EventNames, Function, Config}).
|
||||
|
||||
-spec delete(telemetry:handler_id()) -> ok | {error, not_found}.
|
||||
delete(HandlerId) ->
|
||||
gen_server:call(?MODULE, {delete, HandlerId}).
|
||||
|
||||
persist() ->
|
||||
{Mod, State} = impl_get(),
|
||||
case Mod:persist(State) of
|
||||
{ok, NewState} ->
|
||||
persistent_term:put(telemetry, {telemetry_pt, NewState}),
|
||||
ok;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
impl_get() -> persistent_term:get(telemetry, undefined).
|
||||
|
||||
-spec list_for_event(telemetry:event_name()) -> [#handler{}].
|
||||
list_for_event(EventName) ->
|
||||
case impl_get() of
|
||||
{Mod, State} ->
|
||||
Mod:list_for_event(State, EventName);
|
||||
undefined ->
|
||||
?LOG_WARNING("Failed to lookup telemetry handlers. "
|
||||
"Ensure the telemetry application has been started. ", []),
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec list_by_prefix(telemetry:event_prefix()) -> [#handler{}].
|
||||
list_by_prefix(EventPrefix) ->
|
||||
case impl_get() of
|
||||
{Mod, State} ->
|
||||
Mod:list_by_prefix(State, EventPrefix);
|
||||
undefined ->
|
||||
?LOG_WARNING("Failed to lookup telemetry handlers. "
|
||||
"Ensure the telemetry application has been started. ", []),
|
||||
[]
|
||||
end.
|
||||
|
||||
init([]) ->
|
||||
TID = create_table(),
|
||||
|
||||
persistent_term:put(telemetry, {telemetry_ets, TID}),
|
||||
|
||||
{ok, []}.
|
||||
|
||||
handle_call({insert, HandlerId, EventNames, Function, Config}, _From, State) ->
|
||||
{Mod, MState} = impl_get(),
|
||||
case Mod:insert(MState, HandlerId, EventNames, Function, Config) of
|
||||
{ok, NewState} ->
|
||||
persistent_term:put(telemetry, {Mod, NewState}),
|
||||
{reply, ok, State};
|
||||
{error, _} = Error ->
|
||||
{reply, Error, State}
|
||||
end;
|
||||
handle_call({delete, HandlerId}, _From, State) ->
|
||||
{Mod, MState} = impl_get(),
|
||||
case Mod:delete(MState, HandlerId) of
|
||||
{ok, NewState} ->
|
||||
persistent_term:put(telemetry, {Mod, NewState}),
|
||||
{reply, ok, State};
|
||||
{error, _} = Error ->
|
||||
{reply, Error, State}
|
||||
end.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
code_change(_, State, _) ->
|
||||
{ok, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
persistent_term:erase(telemetry),
|
||||
ok.
|
||||
|
||||
%%
|
||||
|
||||
create_table() ->
|
||||
ets:new(?MODULE, [duplicate_bag, protected,
|
||||
{keypos, #handler.event_name}, {read_concurrency, true}]).
|
||||
62
phoenix/deps/telemetry/src/telemetry_pt.erl
Normal file
62
phoenix/deps/telemetry/src/telemetry_pt.erl
Normal file
@@ -0,0 +1,62 @@
|
||||
-module(telemetry_pt).
|
||||
|
||||
-include("telemetry.hrl").
|
||||
|
||||
-export([persist/1,
|
||||
list_for_event/2,
|
||||
list_by_prefix/2,
|
||||
insert/5,
|
||||
delete/2]).
|
||||
|
||||
persist(_Map) -> error.
|
||||
|
||||
list_for_event(Map, EventName) ->
|
||||
case Map of
|
||||
#{EventName := Handlers} -> Handlers;
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
list_by_prefix(Map, EventPrefix) ->
|
||||
[Handler ||
|
||||
{EventName, Handlers} <- maps:to_list(Map),
|
||||
starts_with(EventName, EventPrefix),
|
||||
Handler <- Handlers].
|
||||
|
||||
starts_with(_Haystack, []) -> true;
|
||||
starts_with([A | Haystack], [A | Needle]) -> starts_with(Haystack, Needle);
|
||||
starts_with(_Haystack, _Needle) -> false.
|
||||
|
||||
insert(Map, _HandlerId, [], _Function, _Config) ->
|
||||
{ok, Map};
|
||||
insert(Map, HandlerId, [EventName | Rest], Function, Config) ->
|
||||
Handler = #handler{id=HandlerId,
|
||||
event_name=EventName,
|
||||
function=Function,
|
||||
config=Config},
|
||||
OldHandlers = maps:get(EventName, Map, []),
|
||||
case OldHandlers of
|
||||
#{HandlerId := _} -> {error, already_exists};
|
||||
_ ->
|
||||
case put_new(Handler, OldHandlers) of
|
||||
{ok, NewHandlers} ->
|
||||
NewMap = Map#{EventName => NewHandlers},
|
||||
insert(NewMap, HandlerId, Rest, Function, Config);
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end
|
||||
end.
|
||||
|
||||
put_new(Handler, List) ->
|
||||
case lists:keymember(Handler#handler.id, #handler.id, List) of
|
||||
true -> {error, already_exists};
|
||||
false -> {ok, [Handler | List]}
|
||||
end.
|
||||
|
||||
delete(Map, HandlerId) ->
|
||||
Filtered = [{Event, lists:keydelete(HandlerId, #handler.id, Handlers)}
|
||||
|| {Event, Handlers} <- maps:to_list(Map)],
|
||||
NewMap = maps:from_list(Filtered),
|
||||
case NewMap =:= Map of
|
||||
true -> {error, not_found};
|
||||
false -> {ok, NewMap}
|
||||
end.
|
||||
24
phoenix/deps/telemetry/src/telemetry_sup.erl
Normal file
24
phoenix/deps/telemetry/src/telemetry_sup.erl
Normal file
@@ -0,0 +1,24 @@
|
||||
%% @private
|
||||
-module(telemetry_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([start_link/0]).
|
||||
-export([init/1]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
SupFlags = #{strategy => one_for_one,
|
||||
intensity => 1,
|
||||
period => 5},
|
||||
TableHandler = #{id => telemetry_handler_table,
|
||||
start => {telemetry_handler_table, start_link, []},
|
||||
restart => permanent,
|
||||
shutdown => 5000,
|
||||
type => worker,
|
||||
modules => [telemetry_handler_table]},
|
||||
{ok, {SupFlags, [TableHandler]}}.
|
||||
86
phoenix/deps/telemetry/src/telemetry_test.erl
Normal file
86
phoenix/deps/telemetry/src/telemetry_test.erl
Normal file
@@ -0,0 +1,86 @@
|
||||
-module(telemetry_test).
|
||||
|
||||
-include("telemetry.hrl").
|
||||
|
||||
?MODULEDOC("""
|
||||
Functions for testing execution of Telemetry events.
|
||||
|
||||
Testing that the correct Telemetry events are emitted with the
|
||||
right measurements and metadata is essential for library authors.
|
||||
It helps to maintain stable APIs and avoid accidental changes
|
||||
to events.
|
||||
""").
|
||||
|
||||
-export([attach_event_handlers/2, handle_event/4]).
|
||||
|
||||
?DOC("""
|
||||
Attaches a "message" handler to the given events.
|
||||
|
||||
The attached handler sends a message to `DestinationPID` every time it handles one of the
|
||||
events in `events`. The function returns a reference that you can use to make sure that
|
||||
messages come from this handler. This reference is also used as the handler ID, so you
|
||||
can use it to detach the handler with `telemetry:detach/1`.
|
||||
|
||||
The shape of messages sent to `DestinationPID` is:
|
||||
|
||||
<!-- tabs-open -->
|
||||
|
||||
### Erlang
|
||||
|
||||
```erlang
|
||||
{Event, Ref, Measurements, Metadata}
|
||||
```
|
||||
|
||||
### Elixir
|
||||
|
||||
```elixir
|
||||
{event, ref, measurements, metadata}
|
||||
```
|
||||
|
||||
<!-- tabs-close -->
|
||||
|
||||
## Examples
|
||||
|
||||
<!-- tabs-open -->
|
||||
|
||||
### Erlang
|
||||
|
||||
An example of a test in Erlang (using [`ct`](https://www.erlang.org/docs/23/man/ct)) could
|
||||
look like this:
|
||||
|
||||
```erlang
|
||||
Ref = telemetry_test:attach_event_handlers(self(), [[some, event]]),
|
||||
function_that_emits_the_event(),
|
||||
receive
|
||||
{[some, event], Ref, #{measurement := _}, #{meta := _}} ->
|
||||
telemetry:detach(Ref)
|
||||
after 1000 ->
|
||||
ct:fail(timeout_receive_attach_event_handlers)
|
||||
end.
|
||||
```
|
||||
|
||||
### Elixir
|
||||
|
||||
An example of an ExUnit test in Elixir could look like this:
|
||||
|
||||
```elixir
|
||||
ref = :telemetry_test.attach_event_handlers(self(), [[:some, :event]])
|
||||
function_that_emits_the_event()
|
||||
assert_received {[:some, :event], ^ref, %{measurement: _}, %{meta: _}}
|
||||
```
|
||||
|
||||
<!-- tabs-close -->
|
||||
|
||||
""").
|
||||
-spec attach_event_handlers(DestinationPID, Events) -> reference() when
|
||||
DestinationPID :: pid(),
|
||||
Events :: [telemetry:event_name(), ...].
|
||||
attach_event_handlers(DestPID, Events) when is_pid(DestPID) and is_list(Events) ->
|
||||
Ref = make_ref(),
|
||||
Config = #{dest_pid => DestPID, ref => Ref},
|
||||
telemetry:attach_many(Ref, Events, fun telemetry_test:handle_event/4, Config),
|
||||
Ref.
|
||||
|
||||
?DOC(false).
|
||||
handle_event(Event, Measurements, Metadata, #{dest_pid := DestPID, ref := Ref}) ->
|
||||
DestPID ! {Event, Ref, Measurements, Metadata}.
|
||||
Reference in New Issue
Block a user