diff --git a/examples/basic_elixir/.dockerignore b/examples/basic_elixir/.dockerignore new file mode 100644 index 0000000..1a7dc98 --- /dev/null +++ b/examples/basic_elixir/.dockerignore @@ -0,0 +1,9 @@ +# The directory Mix will write compiled artifacts to. +_build/ + +# The directory Mix downloads your dependencies sources to. +deps/ + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + diff --git a/examples/basic_elixir/.formatter.exs b/examples/basic_elixir/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/examples/basic_elixir/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/examples/basic_elixir/.gitignore b/examples/basic_elixir/.gitignore new file mode 100644 index 0000000..c8e3622 --- /dev/null +++ b/examples/basic_elixir/.gitignore @@ -0,0 +1,24 @@ +# 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"). +basic_elixir-*.tar + diff --git a/examples/basic_elixir/Dockerfile b/examples/basic_elixir/Dockerfile new file mode 100644 index 0000000..7e03087 --- /dev/null +++ b/examples/basic_elixir/Dockerfile @@ -0,0 +1,13 @@ +FROM elixir:1.9-alpine as builder +RUN mix local.hex --force && mix local.rebar --force +WORKDIR /app +COPY . /app +RUN mix deps.get +RUN MIX_ENV=prod mix release + +FROM alpine:latest as app +RUN apk add bash openssl +WORKDIR /app +COPY --from=builder /app/_build/prod/rel/basic_elixir . +CMD bin/basic_elixir start + diff --git a/examples/basic_elixir/README.md b/examples/basic_elixir/README.md new file mode 100644 index 0000000..315e197 --- /dev/null +++ b/examples/basic_elixir/README.md @@ -0,0 +1,21 @@ +# Basic Elixir Example + +This is a sample repository that demo how to setup a basic Elixir application +with `opentelemetry-api` and `opentelemetry_exporter`. Here, we are using +`opentelemetry_exporter` to export the traces to [OpenTelemetry Collector][0]. +The collector in turn export the traces to [Zipkin][1] and [Jaeger][2] +respectively. + +## Getting Stated + +Assuming you already have Docker and Docker Compose installed: + +1. Run `docker-compose up` to start the application, OpenTelemetry Collector, + Zipkin and Jaeger. +2. Visit Zipkin at http://localhost:9411 and hit `Run Query` to look the the sample trace. +3. Visit Jaeger UI at http://localhost:16686 and click `Find Trace` to look at the sample + trace. + +[0]: https://github.com/open-telemetry/opentelemetry-collector/ +[1]: https://zipkin.io/ +[2]: https://www.jaegertracing.io/ diff --git a/examples/basic_elixir/config/releases.exs b/examples/basic_elixir/config/releases.exs new file mode 100644 index 0000000..96f4c2e --- /dev/null +++ b/examples/basic_elixir/config/releases.exs @@ -0,0 +1,12 @@ +import Config + +config :opentelemetry, + :processors, + otel_batch_processor: %{ + # Using `otel` here since we are starting through docker-compose where + # otel refer to the hostname of the OpenCollector, + # + # If you are running it locally, kindly change it to the correct + # hostname such as `localhost`, `0.0.0.0` and etc. + exporter: {:opentelemetry_exporter, %{endpoints: [{:http, 'otel', 55681, []}]}} + } diff --git a/examples/basic_elixir/docker-compose.yml b/examples/basic_elixir/docker-compose.yml new file mode 100644 index 0000000..268a875 --- /dev/null +++ b/examples/basic_elixir/docker-compose.yml @@ -0,0 +1,28 @@ +version: '3' +services: + elixir: + build: . + + otel: + image: otel/opentelemetry-collector-contrib-dev:latest + command: ["--config=/etc/otel-collector-config.yaml"] + ports: + - '55681:55681' + - '55680:55680' + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + + zipkin: + image: openzipkin/zipkin-slim + ports: + - '9411:9411' + + # Jaeger + jaeger-all-in-one: + image: jaegertracing/all-in-one:latest + ports: + - "16686:16686" + - "14268" + - "14250" + + diff --git a/examples/basic_elixir/lib/basic_elixir.ex b/examples/basic_elixir/lib/basic_elixir.ex new file mode 100644 index 0000000..4c3718e --- /dev/null +++ b/examples/basic_elixir/lib/basic_elixir.ex @@ -0,0 +1,18 @@ +defmodule BasicElixir do + @moduledoc """ + Documentation for BasicElixir. + """ + + @doc """ + Hello world. + + ## Examples + + iex> BasicElixir.hello() + :world + + """ + def hello do + :world + end +end diff --git a/examples/basic_elixir/lib/basic_elixir/application.ex b/examples/basic_elixir/lib/basic_elixir/application.ex new file mode 100644 index 0000000..e86afc6 --- /dev/null +++ b/examples/basic_elixir/lib/basic_elixir/application.ex @@ -0,0 +1,19 @@ +defmodule BasicElixir.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + def start(_type, _args) do + children = [ + # Starts a worker by calling: BasicElixir.Worker.start_link(arg) + {BasicElixir.Worker, []} + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: BasicElixir.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/examples/basic_elixir/lib/basic_elixir/worker.ex b/examples/basic_elixir/lib/basic_elixir/worker.ex new file mode 100644 index 0000000..82b0976 --- /dev/null +++ b/examples/basic_elixir/lib/basic_elixir/worker.ex @@ -0,0 +1,48 @@ +defmodule BasicElixir.Worker do + use GenServer + require Logger + require OpenTelemetry.Tracer, as: Tracer + require OpenTelemetry.Span + + # Client + def start_link(default) when is_list(default) do + Tracer.with_span "start_link" do + Tracer.add_event("Nice operation!", [{"bogons", 100}]) + Tracer.set_attributes([{:another_key, "yes"}]) + + Tracer.with_span "Sub operation..." do + Tracer.set_attributes([{:lemons_key, "five"}]) + Tracer.add_event("Sub span event!", []) + end + + GenServer.start_link(__MODULE__, default) + end + end + + def push(pid, element) do + GenServer.cast(pid, {:push, element}) + end + + def pop(pid) do + GenServer.call(pid, :pop) + end + + # Server (callbacks) + @impl true + def init(stack) do + Tracer.with_span "init" do + Logger.info("Starting #{__MODULE__}...") + {:ok, stack} + end + end + + @impl true + def handle_call(:pop, _from, [head | tail]) do + {:reply, head, tail} + end + + @impl true + def handle_cast({:push, element}, state) do + {:noreply, [element | state]} + end +end diff --git a/examples/basic_elixir/mix.exs b/examples/basic_elixir/mix.exs new file mode 100644 index 0000000..f396918 --- /dev/null +++ b/examples/basic_elixir/mix.exs @@ -0,0 +1,34 @@ +defmodule BasicElixir.MixProject do + use Mix.Project + + def project do + [ + app: :basic_elixir, + version: "0.1.0", + elixir: "~> 1.9", + start_permanent: Mix.env() == :prod, + deps: deps(), + releases: [ + basic_elixir: [ + applications: [opentelemetry: :temporary] + ] + ] + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger], + mod: {BasicElixir.Application, []} + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:opentelemetry, "~> 1.0.0-rc.3"}, + {:opentelemetry_exporter, "~> 1.0.0-rc.3"}, + ] + end +end diff --git a/examples/basic_elixir/mix.lock b/examples/basic_elixir/mix.lock new file mode 100644 index 0000000..5f88e2c --- /dev/null +++ b/examples/basic_elixir/mix.lock @@ -0,0 +1,11 @@ +%{ + "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"}, + "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"}, + "opentelemetry": {:hex, :opentelemetry, "1.0.0-rc.3", "d2698bee882c354274563ee85d097bb736a9adb8d8ed376a4deea0cd3a14bb31", [:rebar3], [{:opentelemetry_api, "~> 1.0.0-rc.3", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "c9105933df0d783d94cf08d79206eb8d6578abc0bcbd498d0b497ec62a4e30a8"}, + "opentelemetry_api": {:hex, :opentelemetry_api, "1.0.0-rc.3.2", "588ebf85fa7d18eba8db297b7d0a2a654f680f35c4466cd0045cd12a6fda27d7", [:mix, :rebar3], [], "hexpm", "1e0ba55db6718d5797e72c00e3821e60e02f7829e3fb64dc1a7c96f7c2776a58"}, + "opentelemetry_exporter": {:hex, :opentelemetry_exporter, "1.0.0-rc.3", "76f5657d4c94a12003d9ed2c8da1023c815e98f5553184dbb0cdaeec76db676d", [:rebar3], [{:grpcbox, ">= 0.0.0", [hex: :grpcbox, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.0.0-rc.3", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.0.0-rc.3", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "267f0e4c3f1f5557cc7ad6ac71d66b8eaf7b3b56fde942c21f8a0bc96174fe1e"}, +} diff --git a/examples/basic_elixir/otel-collector-config.yaml b/examples/basic_elixir/otel-collector-config.yaml new file mode 100644 index 0000000..f166a1a --- /dev/null +++ b/examples/basic_elixir/otel-collector-config.yaml @@ -0,0 +1,27 @@ +receivers: + otlp: + protocols: + grpc: + endpoint: "0.0.0.0:55680" + http: + endpoint: "0.0.0.0:55681" +processors: + batch: + send_batch_size: 1024 + timeout: 5s +exporters: + zipkin: + endpoint: "http://zipkin:9411/api/v2/spans" + jaeger: + endpoint: jaeger-all-in-one:14250 + insecure: true +extensions: + zpages: {} +service: + extensions: [zpages] + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [zipkin, jaeger] + diff --git a/examples/basic_elixir/test/basic_elixir_test.exs b/examples/basic_elixir/test/basic_elixir_test.exs new file mode 100644 index 0000000..7bac577 --- /dev/null +++ b/examples/basic_elixir/test/basic_elixir_test.exs @@ -0,0 +1,8 @@ +defmodule BasicElixirTest do + use ExUnit.Case + doctest BasicElixir + + test "greets the world" do + assert BasicElixir.hello() == :world + end +end diff --git a/examples/basic_elixir/test/test_helper.exs b/examples/basic_elixir/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/examples/basic_elixir/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start()