aws_ex_ray v0.1.12 AwsExRay

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,

AwsExRay.finish_tracing(segment)
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)

Link to this section Summary

Link to this section Functions

Link to this function finish_subsegment(subsegment, end_time \\ Util.now())
finish_subsegment(subsegment :: AwsExRay.Subsegment.t(), end_time :: number()) ::
  :ok
Link to this function finish_tracing(segment)
finish_tracing(segment :: AwsExRay.Segment.t()) :: :ok
Link to this function start_subsegment(name, opts \\ [])
start_subsegment(name :: String.t(), opts :: keyword()) ::
  {:ok, AwsExRay.Subsegment.t()} | {:error, :out_of_xray}
Link to this function start_tracing(name)
start_tracing(name :: String.t()) :: AwsExRay.Segment.t()
Link to this function start_tracing(trace, name)
start_tracing(trace :: AwsExRay.Trace.t(), name :: String.t()) ::
  AwsExRay.Segment.t()
Link to this function subsegment(name, func)
subsegment(name :: String.t(), func :: (... -> any())) :: :ok
Link to this function subsegment(name, opts, func)
subsegment(name :: String.t(), opts :: keyword(), func :: (... -> any())) ::
  :ok
Link to this function subsegment(name, annotations, opts, func)
subsegment(
  name :: String.t(),
  annotations :: map(),
  opts :: keyword(),
  func :: (... -> any())
) :: :ok
Link to this function trace(name, func)
trace(name :: String.t(), func :: (... -> any())) :: any()
Link to this function trace(trace, name, func)
trace(trace :: AwsExRay.Trace.t(), name :: String.t(), func :: (... -> any())) ::
  any()
Link to this function trace(trace, name, annotations, func)
trace(
  trace :: AwsExRay.Trace.t(),
  name :: String.t(),
  annotations :: map(),
  func :: (... -> any())
) :: any()