├── .formatter.exs ├── .gitignore ├── test ├── support │ ├── models │ │ ├── post.ex │ │ └── user.ex │ └── test_repo.ex ├── test_helper.exs └── opentelemetry_ecto_test.exs ├── docker-compose.yml ├── priv └── test_repo │ └── migrations │ └── 1_setup_tables.exs ├── config ├── test.exs └── config.exs ├── .github └── workflows │ └── test.yml ├── mix.exs ├── README.md ├── lib └── opentelemetry_ecto.ex ├── mix.lock └── LICENSE /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], 4 | line_length: 120 5 | ] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /cover 3 | /deps 4 | /doc 5 | /.fetch 6 | erl_crash.dump 7 | *.ez 8 | *.beam 9 | /config/*.secret.exs 10 | .elixir_ls/ 11 | .rebar3 12 | -------------------------------------------------------------------------------- /test/support/models/post.ex: -------------------------------------------------------------------------------- 1 | defmodule OpentelemetryEcto.TestModels.Post do 2 | use Ecto.Schema 3 | 4 | schema "posts" do 5 | field(:body, :string) 6 | belongs_to(:user, OpentelemetryEcto.TestModels.User) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/support/test_repo.ex: -------------------------------------------------------------------------------- 1 | defmodule OpentelemetryEcto.TestRepo do 2 | use Ecto.Repo, 3 | otp_app: :opentelemetry_ecto, 4 | adapter: Ecto.Adapters.Postgres, 5 | telemetry_prefix: [:opentelemetry_ecto, :test_repo] 6 | end 7 | -------------------------------------------------------------------------------- /test/support/models/user.ex: -------------------------------------------------------------------------------- 1 | defmodule OpentelemetryEcto.TestModels.User do 2 | use Ecto.Schema 3 | 4 | schema "users" do 5 | field(:email, :string) 6 | 7 | has_many(:posts, OpentelemetryEcto.TestModels.Post) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | postgres: 5 | image: circleci/postgres:11.3-alpine-ram 6 | environment: 7 | - POSTGRES_USER=postgres 8 | - POSTGRES_DB=opentelemetry_ecto_test 9 | ports: 10 | - 5432:5432 11 | -------------------------------------------------------------------------------- /priv/test_repo/migrations/1_setup_tables.exs: -------------------------------------------------------------------------------- 1 | defmodule OpentelemetryEcto.TestRepo.Migrations.SetupTables do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:users) do 6 | add :email, :string 7 | end 8 | 9 | create table(:posts) do 10 | add :body, :text 11 | add :user_id, references(:users) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /config/test.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | config :opentelemetry_ecto, 4 | ecto_repos: [OpentelemetryEcto.TestRepo] 5 | 6 | config :opentelemetry_ecto, OpentelemetryEcto.TestRepo, 7 | hostname: "localhost", 8 | username: "postgres", 9 | database: "opentelemetry_ecto_test", 10 | pool: Ecto.Adapters.SQL.Sandbox 11 | 12 | config :opentelemetry, 13 | sampler: {:always_on, %{}}, 14 | tracer: :otel_tracer_default, 15 | processors: [{:otel_batch_processor, %{scheduled_delay_ms: 1}}] 16 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | :application.load(:opentelemetry) 2 | :application.set_env(:opentelemetry, :tracer, :otel_tracer_default) 3 | 4 | :application.set_env(:opentelemetry, :processors, [ 5 | {:otel_batch_processor, %{scheduled_delay_ms: 1}} 6 | ]) 7 | 8 | Application.ensure_all_started(:opentelemetry) 9 | Application.ensure_all_started(:telemetry) 10 | OpentelemetryEcto.TestRepo.start_link() 11 | 12 | ExUnit.start() 13 | 14 | Ecto.Adapters.SQL.Sandbox.mode(OpentelemetryEcto.TestRepo, :manual) 15 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Common Test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - 'master' 7 | push: 8 | branches: 9 | - 'master' 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | services: 16 | postgres: 17 | image: circleci/postgres:11.3-alpine-ram 18 | ports: ['5432:5432'] 19 | options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 20 | env: 21 | POSTGRES_USER: postgres 22 | POSTGRES_DB: opentelemetry_ecto_test 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: erlef/setup-elixir@v1 27 | with: 28 | otp-version: '22.x' 29 | elixir-version: '1.9.x' 30 | - name: Fetch deps 31 | run: mix deps.get 32 | - name: Run tests 33 | run: mix test 34 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # third-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure your application as: 12 | # 13 | # config :opentelemetry_ecto, key: :value 14 | # 15 | # and access this configuration in your application as: 16 | # 17 | # Application.get_env(:opentelemetry_ecto, :key) 18 | # 19 | # You can also configure a third-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | try do 31 | import_config "#{Mix.env()}.exs" 32 | rescue 33 | _ -> :ok 34 | end 35 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule OpentelemetryEcto.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :opentelemetry_ecto, 7 | description: description(), 8 | version: "1.0.0-rc.1", 9 | elixir: "~> 1.9", 10 | start_permanent: Mix.env() == :prod, 11 | deps: deps(), 12 | aliases: aliases(), 13 | elixirc_paths: elixirc_paths(Mix.env()), 14 | package: package(), 15 | source_url: "https://github.com/opentelemetry-beam/opentelemetry_ecto" 16 | ] 17 | end 18 | 19 | defp description do 20 | "Trace Ecto queries with OpenTelemetry." 21 | end 22 | 23 | defp package do 24 | [ 25 | licenses: ["Apache-2"], 26 | links: %{ 27 | "GitHub" => "https://github.com/opentelemetry-beam/opentelemetry_ecto", 28 | "OpenTelemetry Erlang" => "https://github.com/open-telemetry/opentelemetry-erlang", 29 | "OpenTelemetry.io" => "https://opentelemetry.io" 30 | } 31 | ] 32 | end 33 | 34 | def application do 35 | [] 36 | end 37 | 38 | defp elixirc_paths(:test), do: ["lib", "test/support"] 39 | defp elixirc_paths(_), do: ["lib"] 40 | 41 | defp aliases() do 42 | [test: ["ecto.drop -q", "ecto.create -q", "ecto.migrate --quiet", "test"]] 43 | end 44 | 45 | defp deps do 46 | [ 47 | {:telemetry, "~> 0.4.2"}, 48 | {:opentelemetry_api, "~> 1.0.0-rc"}, 49 | {:opentelemetry, "~> 1.0.0-rc"}, 50 | {:ex_doc, "~> 0.24.0", only: [:dev], runtime: false}, 51 | {:ecto_sql, ">= 3.0.0", only: [:test]}, 52 | {:postgrex, ">= 0.15.0", only: [:test]} 53 | ] 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpentelemetryEcto 2 | 3 | ## This project has moved! 4 | 5 | **This project now lives at https://github.com/open-telemetry/opentelemetry-erlang-contrib.** 6 | 7 | Telemetry handler that creates Opentelemetry spans from Ecto query events. Because 8 | Ecto emits telemetry events only after queries have finished, OpentelemetryEcto 9 | estimates the start time of the span by subtracting the reported total duration 10 | from the current timestamp. 11 | 12 | After installing, setup the handler in your application behaviour before your 13 | top-level supervisor starts. 14 | 15 | ```elixir 16 | OpentelemetryEcto.setup([:blog, :repo]) 17 | ``` 18 | 19 | See the documentation for `OpentelemetryEcto.setup/2` for additional options that 20 | may be supplied. 21 | 22 | ## Installation 23 | 24 | If [available in Hex](https://hex.pm/docs/publish), the package can be installed 25 | by adding `opentelemetry_ecto` to your list of dependencies in `mix.exs`: 26 | 27 | ```elixir 28 | def deps do 29 | [ 30 | {:opentelemetry_ecto, "~> 1.0.0-rc", allow_pre: true} 31 | ] 32 | end 33 | ``` 34 | 35 | ## Compatibility Matrix 36 | 37 | | OpentelemetryEcto Version | Otel Version | Notes | 38 | | :------------------------ | :----------- | :---- | 39 | | | | | 40 | | v0.1.0 | <= v.0.5.0 | | 41 | | v1.0.0-rc.1 | v1.0.0-rc.1 | | 42 | 43 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 44 | and published on [HexDocs](https://hexdocs.pm). Once published, the docs can 45 | be found at [https://hexdocs.pm/opentelemetry_ecto](https://hexdocs.pm/opentelemetry_ecto). 46 | 47 | -------------------------------------------------------------------------------- /test/opentelemetry_ecto_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OpentelemetryEctoTest do 2 | alias OpentelemetryEcto.TestRepo, as: Repo 3 | alias OpentelemetryEcto.TestModels.{User, Post} 4 | require OpenTelemetry.Tracer 5 | use ExUnit.Case 6 | 7 | @event_name [:opentelemetry_ecto, :test_repo] 8 | 9 | require Record 10 | 11 | for {name, spec} <- Record.extract_all(from_lib: "opentelemetry/include/otel_span.hrl") do 12 | Record.defrecord(name, spec) 13 | end 14 | 15 | setup do 16 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(OpentelemetryEcto.TestRepo) 17 | :otel_batch_processor.set_exporter(:otel_exporter_pid, self()) 18 | 19 | OpenTelemetry.Tracer.start_span("test") 20 | 21 | on_exit(fn -> 22 | OpenTelemetry.Tracer.end_span() 23 | end) 24 | end 25 | 26 | test "captures basic query events" do 27 | attach_handler() 28 | 29 | Repo.all(User) 30 | 31 | assert_receive {:span, 32 | span( 33 | name: "opentelemetry_ecto.test_repo.query:users", 34 | attributes: list 35 | )} 36 | 37 | assert [ 38 | "db.instance": "opentelemetry_ecto_test", 39 | "db.statement": "SELECT u0.\"id\", u0.\"email\" FROM \"users\" AS u0", 40 | "db.type": :sql, 41 | "db.url": "ecto://localhost", 42 | decode_time_microseconds: _, 43 | query_time_microseconds: _, 44 | queue_time_microseconds: _, 45 | source: "users", 46 | total_time_microseconds: _ 47 | ] = List.keysort(list, 0) 48 | end 49 | 50 | test "changes the time unit" do 51 | attach_handler(time_unit: :millisecond) 52 | 53 | Repo.all(Post) 54 | 55 | assert_receive {:span, 56 | span( 57 | name: "opentelemetry_ecto.test_repo.query:posts", 58 | attributes: list 59 | )} 60 | 61 | assert [ 62 | "db.instance": "opentelemetry_ecto_test", 63 | "db.statement": "SELECT p0.\"id\", p0.\"body\", p0.\"user_id\" FROM \"posts\" AS p0", 64 | "db.type": :sql, 65 | "db.url": "ecto://localhost", 66 | decode_time_milliseconds: _, 67 | query_time_milliseconds: _, 68 | queue_time_milliseconds: _, 69 | source: "posts", 70 | total_time_milliseconds: _ 71 | ] = List.keysort(list, 0) 72 | end 73 | 74 | test "changes the span name prefix" do 75 | attach_handler(span_prefix: "Ecto") 76 | 77 | Repo.all(User) 78 | 79 | assert_receive {:span, span(name: "Ecto:users")} 80 | end 81 | 82 | test "collects multiple spans" do 83 | user = Repo.insert!(%User{email: "opentelemetry@erlang.org"}) 84 | Repo.insert!(%Post{body: "We got traced!", user: user}) 85 | 86 | attach_handler() 87 | 88 | User 89 | |> Repo.all() 90 | |> Repo.preload([:posts]) 91 | 92 | assert_receive {:span, span(name: "opentelemetry_ecto.test_repo.query:users")} 93 | assert_receive {:span, span(name: "opentelemetry_ecto.test_repo.query:posts")} 94 | end 95 | 96 | def attach_handler(config \\ []) do 97 | # For now setup the handler manually in each test 98 | handler = {__MODULE__, self()} 99 | 100 | :telemetry.attach(handler, @event_name ++ [:query], &OpentelemetryEcto.handle_event/4, config) 101 | 102 | on_exit(fn -> 103 | :telemetry.detach(handler) 104 | end) 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/opentelemetry_ecto.ex: -------------------------------------------------------------------------------- 1 | defmodule OpentelemetryEcto do 2 | @moduledoc """ 3 | Telemetry handler for creating OpenTelemetry Spans from Ecto query events. 4 | """ 5 | 6 | require OpenTelemetry.Tracer 7 | 8 | @doc """ 9 | Attaches the OpentelemetryEcto handler to your repo events. This should be called 10 | from your application behaviour on startup. 11 | 12 | Example: 13 | 14 | OpentelemetryEcto.setup([:blog, :repo]) 15 | 16 | You may also supply the following options in the second argument: 17 | 18 | * `:time_unit` - a time unit used to convert the values of query phase 19 | timings, defaults to `:microsecond`. See `System.convert_time_unit/3` 20 | 21 | * `:span_prefix` - the first part of the span name, as a `String.t`, 22 | defaults to the concatenation of the event name with periods, e.g. 23 | `"blog.repo.query"`. This will always be followed with a colon and the 24 | source (the table name for SQL adapters). 25 | """ 26 | def setup(event_prefix, config \\ []) do 27 | # register the tracer. just re-registers if called for multiple repos 28 | _ = OpenTelemetry.register_application_tracer(:opentelemetry_ecto) 29 | 30 | event = event_prefix ++ [:query] 31 | :telemetry.attach({__MODULE__, event}, event, &__MODULE__.handle_event/4, config) 32 | end 33 | 34 | @doc false 35 | def handle_event( 36 | event, 37 | measurements, 38 | %{query: query, source: source, result: query_result, repo: repo, type: type}, 39 | config 40 | ) do 41 | # Doing all this even if the span isn't sampled so the sampler 42 | # could technically use the attributes to decide if it should sample or not 43 | 44 | total_time = measurements.total_time 45 | end_time = :opentelemetry.timestamp() 46 | start_time = end_time - total_time 47 | database = repo.config()[:database] 48 | 49 | url = 50 | case repo.config()[:url] do 51 | nil -> 52 | # TODO: add port 53 | URI.to_string(%URI{scheme: "ecto", host: repo.config()[:hostname]}) 54 | 55 | url -> 56 | url 57 | end 58 | 59 | span_name = 60 | case Keyword.fetch(config, :span_prefix) do 61 | {:ok, prefix} -> prefix 62 | :error -> Enum.join(event, ".") 63 | end <> ":#{source}" 64 | 65 | time_unit = Keyword.get(config, :time_unit, :microsecond) 66 | 67 | db_type = 68 | case type do 69 | :ecto_sql_query -> :sql 70 | _ -> type 71 | end 72 | 73 | result = 74 | case query_result do 75 | {:ok, _} -> [] 76 | _ -> [error: true] 77 | end 78 | 79 | # TODO: need connection information to complete the required attributes 80 | # net.peer.name or net.peer.ip and net.peer.port 81 | base_attributes = 82 | Keyword.merge(result, 83 | "db.type": db_type, 84 | "db.statement": query, 85 | source: source, 86 | "db.instance": database, 87 | "db.url": url, 88 | "total_time_#{time_unit}s": System.convert_time_unit(total_time, :native, time_unit) 89 | ) 90 | 91 | attributes = 92 | measurements 93 | |> Enum.into(%{}) 94 | |> Map.take(~w(decode_time query_time queue_time)a) 95 | |> Enum.reject(&is_nil(elem(&1, 1))) 96 | |> Enum.map(fn {k, v} -> 97 | {String.to_atom("#{k}_#{time_unit}s"), System.convert_time_unit(v, :native, time_unit)} 98 | end) 99 | 100 | s = OpenTelemetry.Tracer.start_span(span_name, %{start_time: start_time, attributes: attributes ++ base_attributes}) 101 | 102 | OpenTelemetry.Span.end_span(s) 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, 3 | "db_connection": {:hex, :db_connection, "2.4.0", "d04b1b73795dae60cead94189f1b8a51cc9e1f911c234cc23074017c43c031e5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad416c21ad9f61b3103d254a71b63696ecadb6a917b36f563921e0de00d7d7c8"}, 4 | "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, 5 | "earmark": {:hex, :earmark, "1.4.15", "2c7f924bf495ec1f65bd144b355d0949a05a254d0ec561740308a54946a67888", [:mix], [{:earmark_parser, ">= 1.4.13", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "3b1209b85bc9f3586f370f7c363f6533788fb4e51db23aa79565875e7f9999ee"}, 6 | "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, 7 | "ecto": {:hex, :ecto, "3.6.1", "7bb317e3fd0179ad725069fd0fe8a28ebe48fec6282e964ea502e4deccb0bd0f", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cbb3294a990447b19f0725488a749f8cf806374e0d9d0dffc45d61e7aeaf6553"}, 8 | "ecto_sql": {:hex, :ecto_sql, "3.6.1", "8774dc3fc0ff7b6be510858b99883640f990c0736b8ab54588f9a0c91807f909", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "66f35c3f2d5978b6bffebd1e6351ab8c9d6b68650d62abd1ab8d149de40e0779"}, 9 | "ex_doc": {:hex, :ex_doc, "0.24.2", "e4c26603830c1a2286dae45f4412a4d1980e1e89dc779fcd0181ed1d5a05c8d9", [:mix], [{:earmark_parser, "~> 1.4.0", [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", "e134e1d9e821b8d9e4244687fb2ace58d479b67b282de5158333b0d57c6fb7da"}, 10 | "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, 11 | "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, 12 | "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, 13 | "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, 14 | "opentelemetry": {:hex, :opentelemetry, "1.0.0-rc.2", "d3e1fd9debfd73e00b0241cac464be7cd6ca6ac2bd38ab2ebe0c92401c76a342", [:rebar3], [{:opentelemetry_api, "~> 1.0.0-rc.2", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "2f810e2eed70a9ea0c9b6943969b59e37f96a2f9e10920045a6c7676c2ab8181"}, 15 | "opentelemetry_api": {:hex, :opentelemetry_api, "1.0.0-rc.2", "a0ec5b242bb7ce7563b4891e77dcfa529defc9e42c19a5a702574c5ac3d0c6e7", [:mix, :rebar3], [], "hexpm", "426a969c8ee2afa8ab55b58e6e40e81c1f934c064459a1acb530f54042f9a9a3"}, 16 | "postgrex": {:hex, :postgrex, "0.15.9", "46f8fe6f25711aeb861c4d0ae09780facfdf3adbd2fb5594ead61504dd489bda", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "610719103e4cb2223d4ab78f9f0f3e720320eeca6011415ab4137ddef730adee"}, 17 | "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------