Skip to content

Elixir Library: AWS X-Ray client (managed by gaterraform)

License

Notifications You must be signed in to change notification settings

GameAnalytics/aws_ex_ray

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AwsExRay

NOT STABLE YET

Please wait version 1.0.0 released.

Installation

If available in Hex, the package can be installed by adding aws_ex_ray to your list of dependencies in mix.exs:

def application do
  [
    extra_applications: [
      :logger,
      :aws_ex_ray
      # ...
    ],
    mod {MyApp.Supervisor, []}
  ]
end

def deps do
  [
    {:aws_ex_ray, "~> 0.1.16"},

    # add support libraries as you like
    {:aws_ex_ray_plug, "~> 0.1"},
    {:aws_ex_ray_ecto, "~> 0.1"},
    {:aws_ex_ray_httpoison, "~> 0.1"},
    {:aws_ex_ray_ex_aws, "~> 0.1"}
  ]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/aws_ex_ray.

Preparation

Setup your AWS environment.

Run xray daemon on an EC2 instance which you want your application run on.

https://docs.aws.amazon.com/xray/latest/devguide/xray-daemon.html

USAGE

trace = Trace.new()
segment = AwsExRay.start_tracing(trace, "root_segment_name")
do_your_job()
AwsExRay.finish_tracing(segment)
def do_your_job() do

  current_trace = AwsExRay.start_subsegment("subsegment-name")

  do_some_work()

  case current_trace do
    {:ok, subsegment} ->
      AwsExRay.finish_subsegment(subsegment)

    {:error, :out_of_xray} -> :ok # you need to do nothing.
  end

end

Multi Processes

Following example doesn't work. start_subsegment returns always {:error, :out_of_xray}. Because the subsegment is not on the process which start tracing.

Pay attention when you use Task.Supervisor or GenServer.

segment = AwsExRay.start_tracing(trace, "root_segment_name")

Task.Supervisor.start_child(MyTaskSupervisor, fn ->

  ####################################################################
  # this function is executed on different process as root-segment!!!
  ####################################################################

  current_trace = AwsExRay.start_subsegment("subsegment-name")

  do_some_work()

  case current_trace do
    {:ok, subsegment} ->
      AwsExRay.finish_subsegment(subsegment)

    {:error, :out_of_xray} -> :ok
  end

end)

The solution.

Call AwsExRay.Process.keep_tracing(process_which_starts_tracing) like following

segment = AwsExRay.start_tracing(trace, "root_segment_name")

tracing_pid = self()

Task.Supervisor.start_child(MyTaskSupervisor, fn ->

  AwsExRay.Process.keep_tracing(tracing_pid)

  current_trace = AwsExRay.start_subsegment("subsegment-name")

  do_some_work()

  case current_trace do
    {:ok, subsegment} ->
      AwsExRay.finish_subsegment(subsegment)

    {:error, :out_of_xray} -> :ok
  end

end)

Multi Servers

[client] --> [1: front_server] --> [2: internal_api or job_worker]

You can tracking Trace including (2) not only (1). If (2) server is HTTP server. You can put X-Amzn-Trace-Id into your requests HTTP headers.

calling internal api on (1)

If you use AwsExRay.HTTPoison, it's easy. all you have to do is to set :traced option.

options = [traced: true]
result = AwsExRay.HTTPoison.get(url, headers, options)

received internal request on (2)

If you setup AwsExRay.Plug, it automatically takes over tracing.

defmodule MyInternalAPIRouter do

  use Plug.Router

  plug AwsExRay.Plug, name: "my-internal-api"

WITHOUT SUPPORT LIBRARIES

You can directory pass Trace value

case AwsExRay.start_subsegment("internal-api-request", namespace: :remote) do

  {:error, :out_of_xray} ->
    pass_job_in_some_way(%{
      your_job_data: ...
    })

  {:ok, subsegment} ->
    pass_job_in_some_way(%{
      your_job_data: ...
      trace_value: Subsegment.generate_trace_value(subsegment)
    })
    AwsExRay.finish_subsegment(subsegment)

end

And job worker side, it can take over the Trace

job = receive_job_in_some_way()

case AwsExRay.Trace.parse(job.trace_value) do
  {:ok, trace}
    AwsExRay.start_tracing(trace, "internal-job-name")
    :ok

  {:error, :not_found} ->
    :ok
end

More Simple Interface

If you don't need to put detailed parameters into segment/subsegment, You can do like following

Segment

trace = Trace.new()
result = AwsExRay.trace(trace, "root_segment_name", fn ->
  do_your_job()
end)

This is same as,

trace = Trace.new()
segment = AwsExRay.start_tracing(trace, "root_segment_name")
result = do_your_job()
AwsExRay.finish_tracing(segment)
result

This way supports just only annotations

trace = Trace.new()
result = AwsExRay.trace(trace, "root_segment_name", %{"MyAnnotationKey" => "MyAnnotationValue"}, fn ->
  do_your_job()
end)

Subsegment

opts = [namespace: :none]
result = AwsExRay.subsegment("name", opts, fn trace_value ->
  # trace_value is an empty string if this context is out of trace
  do_your_job()
end)

This is same as,

current_trace = AwsExRay.start_subsegment("subsegment-name")

result = do_some_work()

case current_trace do
  {:ok, subsegment} ->
    AwsExRay.finish_subsegment(subsegment)

  {:error, :out_of_xray} -> :ok # you need to do nothing.
end

result

This way supports just only annotations

opts = [namespace: :none]
result = AwsExRay.subsegment("name", %{"MyAnnotationKey" => "MyAnnotationValue"}, opts, fn ->
  do_your_job()
end)

Configuration

config :aws_ex_ray,
  rules_module: AwsExRay.Rules.SamplingRate,
  sampling_rate: 0.1,
  default_annotation: %{foo: "bar"},
  default_metadata: %{bar: "buz"}
key default description
rules_module AwsExRay.Rules.SamplingRate see below
sampling_rate 0.1 set number between 0.0 - 1.0. recommended that set 0.0 for 'test' environment
default_annotation %{} annotation parameters automatically put into segment/subsegment
default_metadata %{} metadata parameters automatically put into segment/subsegment
daemon_address 127.0.0.1 your xray daemon's host name. typically, you don't need to customize this.
daemon_port 2000 your xray daemon's port. typically, you don't need to customize this.
default_client_pool_size 10 number of UDP client which connects to xray daemon
default_client_pool_overflow 100 overflow capacity size of UDP client
default_store_monitor_pool_size 10 number of tracing-process-monitor

Sampling rules

There are different ways to configure how to decide which requests to sample. The default rules_module is AwsExRay.Rules.SamplingRate. It uses the sampling_rate setting to determine how many requests to sample. For example, if sampling_rate is set to the default of 0.1, every request has a 10% chance of being sampled.

You can also set rules_module to AwsExRay.Rules.OnlineRules. With this module, the sampling_rate setting is ignored. Instead, it fetches sampling rules from AWS X-Ray, as described here: https://docs.aws.amazon.com/xray/latest/devguide/xray-console-sampling.html

Trace.new expects a map with the following keys:

  • service_name
  • service_type
  • http_method
  • host
  • url_path
  • resource_arn

It compares the values in the map with the rules configured in AWS X-Ray. If a value is missing from the map, it matches only if the rule has a wildcard for that field.

The highest-priority rule that matches the values in the map is picked, and the sampling rate and reservoir size are used to determine whether the request should be sampled.

Support Libraries

Plug Support

https://github.com/lyokato/aws_ex_ray_plug

In your router, set AwsExRay.Plug.

defmodule MyPlugRouter do

  use Plug.Router

  plug AwsExRay.Plug, name: "my-xray", skip: [{:get, "/bar"}]

  plug :match
  plug :dispatch

  get "/foo" do
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(200, Poison.encode!(%{body: "Hello, Foo"}))
  end

  get "/bar" do
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(200, Poison.encode!(%{body: "Hello, Bar"}))
  end

end

Then automatically start tracing segment if the request is not included skip setting.

Ecto Support

https://github.com/lyokato/aws_ex_ray_ecto

In your config file, put AwsExRay.Ecto.Logger into Ecto's :loggers setting.

config :my_app, MyApp.EctoRepo,
  adapter: Ecto.Adapters.MySQL,
  hostname: "example.org",
  port:     "3306",
  database: "my_db",
  username: "foo",
  password: "bar",
  loggers:  [Ecto.LogEntry, AwsExRay.Ecto.Logger]

Then automatically record subsegment if queries called on the tracing process.

HTTPoison Support

https://github.com/lyokato/aws_ex_ray_httpoison

use AwsExRay.HTTPoison instead of HTTPoison

result = AwsExRay.HTTPoison.get! "https://example.org/"

Then automatically record subsegment if HTTP request called on the tracing process.

ExAws Support

https://github.com/lyokato/aws_ex_ray_ex_aws

In your config file, put AwsExRay.ExAws.HTTPClient to :http_client setting.

config :ex_aws,
  http_client: AwsExRay.ExAws.HTTPClient

Then automatically record subsegment if HTTP request toward AWS-Services called on the tracing process.

LICENSE

MIT-LICENSE

Author

Lyo Kaot <lyo.kato at gmail.com>

About

Elixir Library: AWS X-Ray client (managed by gaterraform)

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Elixir 100.0%