Add Nebulex instrumentation library (#83)
Add instrumentation for Nebulex, a distributed cache library. This library provides solid telemetry support for this initial implementation. Caching implementation is mostly based on in-memory storage (like ETS) and RPC calls for distribution (via OTP libraries, like :erpc). AFAICT, there is not much specifics for how to translate into Semantic Attributes: those caches are not quite a DB, except maybe for the one which implements the storage; the RPC can't be reliably captured either. Given the above constraints, this initial implementation instruments the library via custom attributes (namespaced as `nebulex.*`). It's not 100% clear the behaviour of OTel for actual distributed caches - from my tests, that may create some orphan spans. I think that's fine as first release. Nebulex follow the patterns of Ecto, so this instrumentation follows a similar pattern of OpentelemetryEcto. It does include a `setup_all/1` function for convenience, that leverages the :init events Nebulex emit on process start. Co-authored-by: Tristan Sloughter <t@crashfast.com>
This commit is contained in:
parent
1e2ae6707c
commit
dfed96874f
|
@ -53,6 +53,9 @@ opentelemetry_cowboy:
|
||||||
opentelemetry_ecto:
|
opentelemetry_ecto:
|
||||||
- instrumentation/opentelemetry_ecto/**/*
|
- instrumentation/opentelemetry_ecto/**/*
|
||||||
|
|
||||||
|
opentelemetry_nebulex:
|
||||||
|
- instrumentation/opentelemetry_nebulex/**/*
|
||||||
|
|
||||||
opentelemetry_oban:
|
opentelemetry_oban:
|
||||||
- instrumentation/opentelemetry_oban/**/*
|
- instrumentation/opentelemetry_oban/**/*
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,43 @@ jobs:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_DB: opentelemetry_ecto_test
|
POSTGRES_DB: opentelemetry_ecto_test
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: erlef/setup-beam@v1
|
||||||
|
with:
|
||||||
|
otp-version: ${{ matrix.otp_version }}
|
||||||
|
elixir-version: ${{ matrix.elixir_version }}
|
||||||
|
rebar3-version: ${{ matrix.rebar3_version }}
|
||||||
|
- name: Cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/deps
|
||||||
|
~/_build
|
||||||
|
key: ${{ runner.os }}-build-${{ matrix.otp_version }}-${{ matrix.elixir_version }}-v3-${{ hashFiles('**/mix.lock') }}
|
||||||
|
- name: Fetch deps
|
||||||
|
if: steps.deps-cache.outputs.cache-hit != 'true'
|
||||||
|
run: mix deps.get
|
||||||
|
- name: Compile project
|
||||||
|
run: mix compile --warnings-as-errors
|
||||||
|
- name: Check formatting
|
||||||
|
run: mix format --check-formatted
|
||||||
|
- name: Test
|
||||||
|
run: mix test
|
||||||
|
|
||||||
|
opentelemetry-nebulex:
|
||||||
|
needs: [test-matrix]
|
||||||
|
if: (contains(github.event.pull_request.labels.*.name, 'elixir') && contains(github.event.pull_request.labels.*.name, 'opentelemetry_nebulex'))
|
||||||
|
env:
|
||||||
|
app: 'opentelemetry_nebulex'
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: instrumentation/${{ env.app }}
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
name: Opentelemetry Nebulex test on Elixir ${{ matrix.elixir_version }} (OTP ${{ matrix.otp_version }})
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.test-matrix.outputs.matrix) }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: erlef/setup-beam@v1
|
- uses: erlef/setup-beam@v1
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
/instrumentation/opentelemetry_cowboy @bryannaegele @tsloughter
|
/instrumentation/opentelemetry_cowboy @bryannaegele @tsloughter
|
||||||
/instrumentation/opentelemetry_ecto @bryannaegele @tsloughter
|
/instrumentation/opentelemetry_ecto @bryannaegele @tsloughter
|
||||||
|
/instrumentation/opentelemetry_nebulex @andrewhr
|
||||||
/instrumentation/opentelemetry_oban @indrekj
|
/instrumentation/opentelemetry_oban @indrekj
|
||||||
/instrumentation/opentelemetry_phoenix @bryannaegele @tsloughter
|
/instrumentation/opentelemetry_phoenix @bryannaegele @tsloughter
|
||||||
/instrumentation/opentelemetry_redix @andrewhr
|
/instrumentation/opentelemetry_redix @andrewhr
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where third-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Also ignore archive artifacts (built via "mix archive.build").
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
|
opentelemetry_redix-*.tar
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
* Initial release
|
|
@ -0,0 +1,201 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,29 @@
|
||||||
|
# OpentelemetryNebulex
|
||||||
|
|
||||||
|
OpentelemetryNebulex uses `telemetry` handlers to create `OpenTelemetry` spans
|
||||||
|
from Nebulex command events.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The package can be installed by adding `opentelemetry_nebulex` to your list of
|
||||||
|
dependencies in `mix.exs`:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def deps do
|
||||||
|
[
|
||||||
|
{:opentelemetry_nebulex, "~> 0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compatibility Matrix
|
||||||
|
|
||||||
|
| OpentelemetryNebulex Version | Otel Version | Notes |
|
||||||
|
| :--------------------------- | :----------- | :---- |
|
||||||
|
| | | |
|
||||||
|
| v0.1.0 | v1.0.0 | |
|
||||||
|
|
||||||
|
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||||
|
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||||
|
be found at [https://hexdocs.pm/opentelemetry_nebulex](https://hexdocs.pm/opentelemetry_nebulex).
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# This file is responsible for configuring your application
|
||||||
|
# and its dependencies with the aid of the Mix.Config module.
|
||||||
|
import Config
|
||||||
|
|
||||||
|
# This configuration is loaded before any dependency and is restricted
|
||||||
|
# to this project. If another project depends on this project, this
|
||||||
|
# file won't be loaded nor affect the parent project. For this reason,
|
||||||
|
# if you want to provide default values for your application for
|
||||||
|
# third-party users, it should be done in your "mix.exs" file.
|
||||||
|
|
||||||
|
# You can configure your application as:
|
||||||
|
#
|
||||||
|
# config :opentelemetry_nebulex, key: :value
|
||||||
|
#
|
||||||
|
# and access this configuration in your application as:
|
||||||
|
#
|
||||||
|
# Application.get_env(:opentelemetry_nebulex, :key)
|
||||||
|
#
|
||||||
|
# You can also configure a third-party app:
|
||||||
|
#
|
||||||
|
# config :logger, level: :info
|
||||||
|
#
|
||||||
|
|
||||||
|
# It is also possible to import configuration files, relative to this
|
||||||
|
# directory. For example, you can emulate configuration per environment
|
||||||
|
# by uncommenting the line below and defining dev.exs, test.exs and such.
|
||||||
|
# Configuration from the imported file will override the ones defined
|
||||||
|
# here (which is why it is important to import them last).
|
||||||
|
#
|
||||||
|
try do
|
||||||
|
import_config "#{Mix.env()}.exs"
|
||||||
|
rescue
|
||||||
|
_ -> :ok
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
import Config
|
||||||
|
|
||||||
|
config :opentelemetry,
|
||||||
|
processors: [{:otel_simple_processor, %{}}]
|
|
@ -0,0 +1,111 @@
|
||||||
|
defmodule OpentelemetryNebulex do
|
||||||
|
@moduledoc """
|
||||||
|
OpentelemetryNebulex uses `telemetry` handlers to create `OpenTelemetry` spans
|
||||||
|
from Nebulex command events.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@tracer_id __MODULE__
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Initializes and configures telemetry handlers for a given cache.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
OpentelemetryNebulex.setup([:blog, :partitioned_cache])
|
||||||
|
"""
|
||||||
|
def setup(event_prefix, opts \\ []) do
|
||||||
|
:telemetry.attach(
|
||||||
|
{__MODULE__, event_prefix, :command_start},
|
||||||
|
event_prefix ++ [:command, :start],
|
||||||
|
&__MODULE__.handle_command_start/4,
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
|
||||||
|
:telemetry.attach(
|
||||||
|
{__MODULE__, event_prefix, :command_stop},
|
||||||
|
event_prefix ++ [:command, :stop],
|
||||||
|
&__MODULE__.handle_command_stop/4,
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
|
||||||
|
:telemetry.attach(
|
||||||
|
{__MODULE__, event_prefix, :command_exception},
|
||||||
|
event_prefix ++ [:command, :exception],
|
||||||
|
&__MODULE__.handle_command_exception/4,
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Initializes and configures telemetry handlers for all caches.
|
||||||
|
|
||||||
|
Use the `[:nebulex, :cache, :init]` event to automatically discover caches, and attach
|
||||||
|
the handlers dynamically. It only works for caches that start after this function is called.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
OpentelemetryNebulex.setup_all()
|
||||||
|
"""
|
||||||
|
def setup_all(opts \\ []) do
|
||||||
|
:telemetry.attach(
|
||||||
|
__MODULE__,
|
||||||
|
[:nebulex, :cache, :init],
|
||||||
|
&__MODULE__.handle_init/4,
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def handle_init(_event, _measurements, metadata, config) do
|
||||||
|
setup(metadata[:opts][:telemetry_prefix], config)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def handle_command_start(_event, _measurements, metadata, _config) do
|
||||||
|
span_name = "nebulex #{metadata.function_name}"
|
||||||
|
|
||||||
|
attributes =
|
||||||
|
%{
|
||||||
|
"nebulex.cache": metadata.adapter_meta.cache
|
||||||
|
}
|
||||||
|
|> maybe_put(:"nebulex.backend", metadata.adapter_meta[:backend])
|
||||||
|
|> maybe_put(:"nebulex.keyslot", metadata.adapter_meta[:keyslot])
|
||||||
|
|> maybe_put(:"nebulex.model", metadata.adapter_meta[:model])
|
||||||
|
|
||||||
|
OpentelemetryTelemetry.start_telemetry_span(@tracer_id, span_name, metadata, %{
|
||||||
|
attributes: attributes
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def handle_command_stop(_event, _measurements, metadata, _config) do
|
||||||
|
ctx = OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, metadata)
|
||||||
|
|
||||||
|
if action = extract_action(metadata) do
|
||||||
|
OpenTelemetry.Span.set_attribute(ctx, :"nebulex.action", action)
|
||||||
|
end
|
||||||
|
|
||||||
|
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def handle_command_exception(_event, _measurements, metadata, _config) do
|
||||||
|
ctx = OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, metadata)
|
||||||
|
|
||||||
|
OpenTelemetry.Span.record_exception(ctx, metadata.reason, metadata.stacktrace)
|
||||||
|
OpenTelemetry.Tracer.set_status(OpenTelemetry.status(:error, format_error(metadata.reason)))
|
||||||
|
|
||||||
|
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put(attributes, _key, nil), do: attributes
|
||||||
|
defp maybe_put(attributes, key, value), do: Map.put(attributes, key, value)
|
||||||
|
|
||||||
|
defp extract_action(%{function_name: f, result: :"$expired"}) when f in [:get, :take], do: :miss
|
||||||
|
defp extract_action(%{function_name: f, result: nil}) when f in [:get, :take], do: :miss
|
||||||
|
defp extract_action(%{function_name: f, result: _}) when f in [:get, :take], do: :hit
|
||||||
|
defp extract_action(_), do: nil
|
||||||
|
|
||||||
|
defp format_error(exception) when is_exception(exception), do: Exception.message(exception)
|
||||||
|
defp format_error(error), do: inspect(error)
|
||||||
|
end
|
|
@ -0,0 +1,59 @@
|
||||||
|
defmodule OpentelemetryNebulex.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :opentelemetry_nebulex,
|
||||||
|
description: description(),
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.11",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps(),
|
||||||
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
|
package: package(),
|
||||||
|
source_url:
|
||||||
|
"https://github.com/open-telemetry/opentelemetry-erlang-contrib/tree/main/instrumentation/opentelemetry_nebulex"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp description do
|
||||||
|
"OpenTelemetry tracing for Nebulex"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp package do
|
||||||
|
[
|
||||||
|
files: ~w(lib .formatter.exs mix.exs README* LICENSE* CHANGELOG*),
|
||||||
|
licenses: ["Apache-2.0"],
|
||||||
|
links: %{
|
||||||
|
"GitHub" =>
|
||||||
|
"https://github.com/open-telemetry/opentelemetry-erlang-contrib/tree/main/instrumentation/opentelemetry_nebulex",
|
||||||
|
"OpenTelemetry Erlang" => "https://github.com/open-telemetry/opentelemetry-erlang",
|
||||||
|
"OpenTelemetry Erlang Contrib" =>
|
||||||
|
"https://github.com/open-telemetry/opentelemetry-erlang-contrib",
|
||||||
|
"OpenTelemetry.io" => "https://opentelemetry.io"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
extra_applications: []
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||||
|
defp elixirc_paths(_), do: ["lib"]
|
||||||
|
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
{:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false},
|
||||||
|
{:ex_doc, "~> 0.28.0", only: [:dev], runtime: false},
|
||||||
|
{:nebulex, "~> 2.1", only: [:dev, :test]},
|
||||||
|
{:opentelemetry, "~> 1.0", only: [:dev, :test]},
|
||||||
|
{:opentelemetry_api, "~> 1.0"},
|
||||||
|
{:opentelemetry_exporter, "~> 1.0", only: [:dev, :test]},
|
||||||
|
{:opentelemetry_telemetry, "~> 1.0"},
|
||||||
|
{:telemetry, "~> 0.4 or ~> 1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
%{
|
||||||
|
"acceptor_pool": {:hex, :acceptor_pool, "1.0.0", "43c20d2acae35f0c2bcd64f9d2bde267e459f0f3fd23dab26485bf518c281b21", [:rebar3], [], "hexpm", "0cbcd83fdc8b9ad2eee2067ef8b91a14858a5883cb7cd800e6fcd5803e158788"},
|
||||||
|
"chatterbox": {:hex, :ts_chatterbox, "0.11.0", "b8f372c706023eb0de5bf2976764edb27c70fe67052c88c1f6a66b3a5626847f", [:rebar3], [{:hpack, "~>0.2.3", [hex: :hpack_erl, repo: "hexpm", optional: false]}], "hexpm", "722fe2bad52913ab7e87d849fc6370375f0c961ffb2f0b5e6d647c9170c382a6"},
|
||||||
|
"ctx": {:hex, :ctx, "0.6.0", "8ff88b70e6400c4df90142e7f130625b82086077a45364a78d208ed3ed53c7fe", [:rebar3], [], "hexpm", "a14ed2d1b67723dbebbe423b28d7615eb0bdcba6ff28f2d1f1b0a7e1d4aa5fc2"},
|
||||||
|
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
|
||||||
|
"earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"},
|
||||||
|
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||||
|
"ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"},
|
||||||
|
"gproc": {:hex, :gproc, "0.8.0", "cea02c578589c61e5341fce149ea36ccef236cc2ecac8691fba408e7ea77ec2f", [:rebar3], [], "hexpm", "580adafa56463b75263ef5a5df4c86af321f68694e7786cb057fd805d1e2a7de"},
|
||||||
|
"grpcbox": {:hex, :grpcbox, "0.14.0", "3eb321bcd2275baf8b54cf381feb7b0559a50c02544de28fda039c7f2f9d1a7a", [:rebar3], [{:acceptor_pool, "~>1.0.0", [hex: :acceptor_pool, repo: "hexpm", optional: false]}, {:chatterbox, "~>0.11.0", [hex: :ts_chatterbox, repo: "hexpm", optional: false]}, {:ctx, "~>0.6.0", [hex: :ctx, repo: "hexpm", optional: false]}, {:gproc, "~>0.8.0", [hex: :gproc, repo: "hexpm", optional: false]}], "hexpm", "e24159b7b6d3f9869bbe528845c0125fed2259366ba908fd04a1f45fe81d0660"},
|
||||||
|
"hpack": {:hex, :hpack_erl, "0.2.3", "17670f83ff984ae6cd74b1c456edde906d27ff013740ee4d9efaa4f1bf999633", [:rebar3], [], "hexpm", "06f580167c4b8b8a6429040df36cc93bba6d571faeaec1b28816523379cbb23a"},
|
||||||
|
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
|
||||||
|
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
|
||||||
|
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
|
||||||
|
"nebulex": {:hex, :nebulex, "2.3.2", "74d8b54e867ca58930edf14de7cc35056cf1ae802bdbc64b3c8c5336234cb1ca", [:mix], [{:decorator, "~> 1.4", [hex: :decorator, repo: "hexpm", optional: true]}, {:shards, "~> 1.0", [hex: :shards, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "09e7c2e687a9d4da2cb6bdde90de74170450f17684f7cc5691faa213c5aa5b13"},
|
||||||
|
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
|
||||||
|
"opentelemetry": {:hex, :opentelemetry, "1.0.3", "0d04f8f2c8b45c75cd7a6b31c0e3699f00bf82feee610f97f10971ddbcbb2010", [:rebar3], [{:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "1e94db9989276f24c3ce9f0df2f46074a42f3f6c19057a2c1a6f863b6a1f1463"},
|
||||||
|
"opentelemetry_api": {:hex, :opentelemetry_api, "1.0.3", "77f9644c42340cd8b18c728cde4822ed55ae136f0d07761b78e8c54da46af93a", [:mix, :rebar3], [], "hexpm", "4293e06bd369bc004e6fad5edbb56456d891f14bd3f9f1772b18f1923e0678ea"},
|
||||||
|
"opentelemetry_exporter": {:hex, :opentelemetry_exporter, "1.0.4", "60a64c75633a82b6c36a20043be355ac72a7b9b21633edd47407924c5596dde0", [:rebar3], [{:grpcbox, ">= 0.0.0", [hex: :grpcbox, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.0", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:tls_certificate_check, "~> 1.11", [hex: :tls_certificate_check, repo: "hexpm", optional: false]}], "hexpm", "61da65290fbb6cac3459b84b8cd630795bf608df93a2b2cc49251cae78200e5e"},
|
||||||
|
"opentelemetry_telemetry": {:hex, :opentelemetry_telemetry, "1.0.0", "d5982a319e725fcd2305b306b65c18a86afdcf7d96821473cf0649ff88877615", [:mix, :rebar3], [{:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.3.0", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "3401d13a1d4b7aa941a77e6b3ec074f0ae77f83b5b2206766ce630123a9291a9"},
|
||||||
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||||
|
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
|
||||||
|
"telemetry_registry": {:hex, :telemetry_registry, "0.3.0", "6768f151ea53fc0fbca70dbff5b20a8d663ee4e0c0b2ae589590e08658e76f1e", [:mix, :rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "492e2adbc609f3e79ece7f29fec363a97a2c484ac78a83098535d6564781e917"},
|
||||||
|
"tls_certificate_check": {:hex, :tls_certificate_check, "1.14.0", "6d1638d56ac68b25c987d401dffb7cd059281339aadc3f8bf27ab33ee19ddbfe", [:rebar3], [{:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "b4452ddd3ae89cd84451afa0e218cb3ccd5178fe3c1de7fabcbddb12a137bcf4"},
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
defmodule OpentelemetryNebulexTest do
|
||||||
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
|
doctest OpentelemetryNebulex
|
||||||
|
|
||||||
|
require OpenTelemetry.Tracer
|
||||||
|
require OpenTelemetry.Span
|
||||||
|
require Record
|
||||||
|
|
||||||
|
for {name, spec} <- Record.extract_all(from_lib: "opentelemetry/include/otel_span.hrl") do
|
||||||
|
Record.defrecord(name, spec)
|
||||||
|
end
|
||||||
|
|
||||||
|
for {name, spec} <- Record.extract_all(from_lib: "opentelemetry_api/include/opentelemetry.hrl") do
|
||||||
|
Record.defrecord(name, spec)
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule Local do
|
||||||
|
use Nebulex.Cache,
|
||||||
|
otp_app: :opentelemetry_nebulex,
|
||||||
|
adapter: Nebulex.Adapters.Local
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule Partitioned do
|
||||||
|
use Nebulex.Cache,
|
||||||
|
otp_app: :opentelemetry_nebulex,
|
||||||
|
adapter: Nebulex.Adapters.Partitioned
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule Multilevel do
|
||||||
|
use Nebulex.Cache,
|
||||||
|
otp_app: :opentelemetry_nebulex,
|
||||||
|
adapter: Nebulex.Adapters.Multilevel
|
||||||
|
|
||||||
|
defmodule L1 do
|
||||||
|
use Nebulex.Cache,
|
||||||
|
otp_app: :opentelemetry_nebulex,
|
||||||
|
adapter: Nebulex.Adapters.Local
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule L2 do
|
||||||
|
use Nebulex.Cache,
|
||||||
|
otp_app: :opentelemetry_nebulex,
|
||||||
|
adapter: Nebulex.Adapters.Partitioned
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do
|
||||||
|
:otel_simple_processor.set_exporter(:otel_exporter_pid, self())
|
||||||
|
|
||||||
|
OpenTelemetry.Tracer.start_span("test")
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
OpenTelemetry.Tracer.end_span()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "local cache commands" do
|
||||||
|
OpentelemetryNebulex.setup([:opentelemetry_nebulex_test, :local])
|
||||||
|
|
||||||
|
start_supervised!(Local)
|
||||||
|
|
||||||
|
# miss
|
||||||
|
Local.get(:my_key)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex get", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.action": :miss,
|
||||||
|
"nebulex.backend": :ets,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Local
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
# write
|
||||||
|
Local.put(:my_key, 42)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex put", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.backend": :ets,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Local
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
# hit
|
||||||
|
Local.get(:my_key)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex get", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.action": :hit,
|
||||||
|
"nebulex.backend": :ets,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Local
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "partitioned cache commands" do
|
||||||
|
OpentelemetryNebulex.setup([:opentelemetry_nebulex_test, :partitioned])
|
||||||
|
OpentelemetryNebulex.setup([:opentelemetry_nebulex_test, :partitioned, :primary])
|
||||||
|
|
||||||
|
start_supervised!(Partitioned)
|
||||||
|
|
||||||
|
# miss
|
||||||
|
Partitioned.get(:my_key)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex get", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.action": :miss,
|
||||||
|
"nebulex.backend": :ets,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Partitioned.Primary
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex get", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.action": :miss,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Partitioned,
|
||||||
|
"nebulex.keyslot": Nebulex.Adapters.Partitioned
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
# write
|
||||||
|
Partitioned.put(:my_key, 42)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex put", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.backend": :ets,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Partitioned.Primary
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex put", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Partitioned,
|
||||||
|
"nebulex.keyslot": Nebulex.Adapters.Partitioned
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
# hit
|
||||||
|
Partitioned.get(:my_key)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex get", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.action": :hit,
|
||||||
|
"nebulex.backend": :ets,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Partitioned.Primary
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex get", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.action": :hit,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Partitioned,
|
||||||
|
"nebulex.keyslot": Nebulex.Adapters.Partitioned
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "multi-level cache commands" do
|
||||||
|
OpentelemetryNebulex.setup([:opentelemetry_nebulex_test, :multilevel])
|
||||||
|
OpentelemetryNebulex.setup([:opentelemetry_nebulex_test, :multilevel, :l1])
|
||||||
|
OpentelemetryNebulex.setup([:opentelemetry_nebulex_test, :multilevel, :l2])
|
||||||
|
|
||||||
|
start_supervised!(
|
||||||
|
{Multilevel,
|
||||||
|
[
|
||||||
|
levels: [
|
||||||
|
{Multilevel.L1, []},
|
||||||
|
{Multilevel.L2, []}
|
||||||
|
]
|
||||||
|
]}
|
||||||
|
)
|
||||||
|
|
||||||
|
# write
|
||||||
|
Multilevel.put(:my_key, 42)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex put", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.backend": :ets,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Multilevel.L1
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex put", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Multilevel.L2,
|
||||||
|
"nebulex.keyslot": Nebulex.Adapters.Partitioned
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex put", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Multilevel
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
# hit
|
||||||
|
Multilevel.get(:my_key)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex get", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.action": :hit,
|
||||||
|
"nebulex.backend": :ets,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Multilevel.L1
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
|
||||||
|
assert_receive {:span, span(name: "nebulex get", kind: :internal, attributes: attributes)}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"nebulex.action": :hit,
|
||||||
|
"nebulex.cache": OpentelemetryNebulexTest.Multilevel,
|
||||||
|
"nebulex.model": :inclusive
|
||||||
|
} = :otel_attributes.map(attributes)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start(capture_log: true)
|
Loading…
Reference in New Issue