Packages
sentry
13.1.0
13.3.0
13.2.0
13.1.0
13.0.1
13.0.0
12.0.3
12.0.2
12.0.1
12.0.0
11.0.4
11.0.3
11.0.2
11.0.1
11.0.0
10.10.0
10.9.0
10.8.1
10.8.0
10.7.1
10.7.0
10.6.2
10.6.1
10.6.0
10.5.0
10.4.0
10.3.0
10.2.1
10.2.0
10.2.0-rc.2
10.2.0-rc.1
10.1.0
10.0.3
10.0.2
10.0.1
10.0.0
9.1.0
9.0.0
8.1.0
8.0.6
8.0.5
8.0.4
8.0.3
8.0.2
8.0.1
8.0.0
8.0.0-rc.2
8.0.0-rc.1
8.0.0-rc.0
retired
7.2.5
7.2.4
7.2.3
7.2.2
7.2.1
7.2.0
7.1.0
7.0.6
7.0.5
7.0.4
7.0.3
7.0.2
7.0.1
7.0.0
6.4.2
6.4.1
6.4.0
6.3.0
6.2.1
6.2.0
6.1.0
6.0.5
6.0.4
6.0.3
6.0.2
6.0.1
6.0.0
5.0.1
5.0.0
4.0.3
4.0.2
4.0.1
4.0.0
3.0.0
2.2.0
2.1.0
2.0.2
2.0.1
2.0.0
1.1.2
1.1.1
1.1.0
1.0.0
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
The Official Elixir client for Sentry
Current section
Files
Jump to
Current section
Files
lib/sentry/envelope.ex
defmodule Sentry.Envelope do
@moduledoc false
# https://develop.sentry.dev/sdk/envelopes/
alias Sentry.{
Attachment,
CheckIn,
ClientReport,
Config,
Event,
LogBatch,
LogEvent,
Metric,
MetricBatch,
Transaction,
UUID
}
@type t() :: %__MODULE__{
event_id: UUID.t(),
items: [
Attachment.t()
| CheckIn.t()
| ClientReport.t()
| Event.t()
| LogBatch.t()
| MetricBatch.t()
| Transaction.t(),
...
]
}
@enforce_keys [:event_id, :items]
defstruct [:event_id, :items]
@doc """
Creates a new envelope containing the given event and all of its attachments.
"""
@spec from_event(Event.t()) :: t()
def from_event(%Event{event_id: event_id} = event) do
%__MODULE__{
event_id: event_id,
items: [event] ++ event.attachments
}
end
@doc """
Creates a new envelope containing the given check-in.
"""
@spec from_check_in(CheckIn.t()) :: t()
def from_check_in(%CheckIn{} = check_in) do
%__MODULE__{
event_id: check_in.check_in_id,
items: [check_in]
}
end
@doc """
Creates a new envelope containing the client report.
"""
@doc since: "10.8.0"
@spec from_client_report(ClientReport.t()) :: t()
def from_client_report(%ClientReport{} = client_report) do
%__MODULE__{
event_id: UUID.uuid4_hex(),
items: [client_report]
}
end
@doc """
Creates a new envelope containing a transaction with spans.
"""
@spec from_transaction(Transaction.t()) :: t()
def from_transaction(%Transaction{} = transaction) do
%__MODULE__{
event_id: transaction.event_id,
items: [transaction]
}
end
@doc """
Creates a new envelope containing log events.
According to the Sentry Logs Protocol, log events are sent in batches
within a single envelope item with content type `application/vnd.sentry.items.log+json`.
All log events are wrapped in a single item with `{ items: [...] }`.
"""
@doc since: "12.0.0"
@spec from_log_events([LogEvent.t()]) :: t()
def from_log_events(log_events) when is_list(log_events) do
# Create a single log batch item that wraps all log events
log_batch = %LogBatch{log_events: log_events}
%__MODULE__{
event_id: UUID.uuid4_hex(),
items: [log_batch]
}
end
@doc """
Creates a new envelope containing metric events.
According to the Sentry Metrics Protocol, metrics are sent in batches
within a single envelope item with content type `application/vnd.sentry.items.trace-metric+json`.
All metric events are wrapped in a single item with `{ items: [...] }`.
"""
@doc since: "13.0.0"
@spec from_metric_events([Metric.t()]) :: t()
def from_metric_events(metrics) when is_list(metrics) do
# Create a single metric batch item that wraps all metrics
metric_batch = %MetricBatch{metrics: metrics}
%__MODULE__{
event_id: UUID.uuid4_hex(),
items: [metric_batch]
}
end
@doc """
Returns the "data category" of the envelope's contents (to be used in client reports and more).
"""
@doc since: "10.8.0"
@spec get_data_category(
Attachment.t()
| CheckIn.t()
| ClientReport.t()
| Event.t()
| LogBatch.t()
| MetricBatch.t()
| Transaction.t()
) ::
String.t()
def get_data_category(%Attachment{}), do: "attachment"
def get_data_category(%Transaction{}), do: "transaction"
def get_data_category(%CheckIn{}), do: "monitor"
def get_data_category(%ClientReport{}), do: "internal"
def get_data_category(%Event{}), do: "error"
def get_data_category(%LogBatch{}), do: "log_item"
def get_data_category(%MetricBatch{}), do: "trace_metric"
@doc """
Returns the total number of payload items in the envelope.
For log and metric envelopes, this counts individual items within the batch.
For other envelope types, each item counts as 1.
"""
@spec item_count(t()) :: non_neg_integer()
def item_count(%__MODULE__{items: items}) do
Enum.reduce(items, 0, fn
%LogBatch{log_events: log_events}, acc -> acc + length(log_events)
%MetricBatch{metrics: metrics}, acc -> acc + length(metrics)
_other, acc -> acc + 1
end)
end
@doc """
Encodes the envelope into its binary representation.
For now, we support only envelopes with a single event and any number of attachments
in them.
"""
@spec to_binary(t()) :: {:ok, binary()} | {:error, any()}
def to_binary(%__MODULE__{} = envelope) do
json_library = Config.json_library()
headers_iodata =
case envelope.event_id do
nil -> "{{}}\n"
event_id -> ~s({"event_id":"#{event_id}"}\n)
end
items_iodata = Enum.map(envelope.items, &item_to_binary(json_library, &1))
{:ok, IO.iodata_to_binary([headers_iodata, items_iodata])}
catch
{:error, _reason} = error -> error
end
defp item_to_binary(json_library, %Event{} = event) do
case event |> Sentry.Client.render_event() |> Sentry.JSON.encode(json_library) do
{:ok, encoded_event} ->
header = ~s({"type":"event","length":#{byte_size(encoded_event)}})
[header, ?\n, encoded_event, ?\n]
{:error, _reason} = error ->
throw(error)
end
end
defp item_to_binary(json_library, %Attachment{} = attachment) do
header = %{"type" => "attachment", "length" => byte_size(attachment.data)}
header =
for {key, value} <- Map.take(attachment, [:filename, :content_type, :attachment_type]),
not is_nil(value),
into: header,
do: {Atom.to_string(key), value}
{:ok, header_iodata} = Sentry.JSON.encode(header, json_library)
[header_iodata, ?\n, attachment.data, ?\n]
end
defp item_to_binary(json_library, %CheckIn{} = check_in) do
case check_in |> CheckIn.to_map() |> Sentry.JSON.encode(json_library) do
{:ok, encoded_check_in} ->
header = ~s({"type":"check_in","length":#{byte_size(encoded_check_in)}})
[header, ?\n, encoded_check_in, ?\n]
{:error, _reason} = error ->
throw(error)
end
end
defp item_to_binary(json_library, %ClientReport{} = client_report) do
case client_report |> Map.from_struct() |> Sentry.JSON.encode(json_library) do
{:ok, encoded_client_report} ->
header = ~s({"type":"client_report","length":#{byte_size(encoded_client_report)}})
[header, ?\n, encoded_client_report, ?\n]
{:error, _reason} = error ->
throw(error)
end
end
defp item_to_binary(json_library, %Transaction{} = transaction) do
case transaction |> Sentry.Client.render_transaction() |> Sentry.JSON.encode(json_library) do
{:ok, encoded_transaction} ->
header = ~s({"type":"transaction","length":#{byte_size(encoded_transaction)}})
[header, ?\n, encoded_transaction, ?\n]
{:error, _reason} = error ->
throw(error)
end
end
defp item_to_binary(json_library, %LogBatch{log_events: log_events}) do
items = Enum.map(log_events, &LogEvent.to_map/1)
payload = %{items: items}
case Sentry.JSON.encode(payload, json_library) do
{:ok, encoded_payload} ->
header = %{
"type" => "log",
"item_count" => length(items),
"content_type" => "application/vnd.sentry.items.log+json"
}
{:ok, encoded_header} = Sentry.JSON.encode(header, json_library)
[encoded_header, ?\n, encoded_payload, ?\n]
{:error, _reason} = error ->
throw(error)
end
end
defp item_to_binary(json_library, %MetricBatch{metrics: metrics}) do
items = Enum.map(metrics, &Metric.to_map/1)
payload = %{items: items}
case Sentry.JSON.encode(payload, json_library) do
{:ok, encoded_payload} ->
header = %{
"type" => "trace_metric",
"item_count" => length(items),
"content_type" => "application/vnd.sentry.items.trace-metric+json"
}
{:ok, encoded_header} = Sentry.JSON.encode(header, json_library)
[encoded_header, ?\n, encoded_payload, ?\n]
{:error, _reason} = error ->
throw(error)
end
end
end