Packages
timber
2.5.2
3.1.2
3.1.1
3.1.0
3.0.0
3.0.0-alpha.3
3.0.0-alpha.2
3.0.0-alpha.1
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.0
2.6.1
2.6.0
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.1
2.3.0
2.2.1
2.2.0
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
2.0.0-rc7
2.0.0-rc6
2.0.0-rc5
2.0.0-rc4
2.0.0-rc3
2.0.0-rc2
2.0.0-rc1
1.1.18
1.1.17
1.1.16
1.1.15
1.1.14
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
🌲 Great Elixir Logging Made Easy. Official Timber.io Integration.
Current section
Files
Jump to
Current section
Files
lib/timber/utils/http_events.ex
defmodule Timber.Utils.HTTPEvents do
@moduledoc false
alias Timber.Config
alias Timber.Utils.JSON
@multi_header_delimiter ","
@header_keys_to_sanitize ["authorization", "x-amz-security-token"]
@header_value_byte_limit 256
@sanitized_value "[sanitized]"
def format_time_ms(time_ms) when is_integer(time_ms),
do: [Integer.to_string(time_ms), "ms"]
def format_time_ms(time_ms) when is_float(time_ms) and time_ms >= 1,
do: [:erlang.float_to_binary(time_ms, decimals: 2), "ms"]
def format_time_ms(time_ms) when is_float(time_ms) and time_ms < 1,
do: [:erlang.float_to_binary(time_ms * 1000, decimals: 0), "µs"]
@doc false
# Constructs a full path from the given parts
def full_path(path, query_string) do
%URI{path: path, query: query_string}
|> URI.to_string()
end
@doc false
# Constructs a full path from the given parts
def full_url(scheme, host, path, port, query_string) do
%URI{scheme: scheme, host: host, path: path, port: port, query: query_string}
|> URI.to_string()
end
@doc false
# Attemps to grab the request ID from the headers
def get_request_id_from_headers(%{"x-request-id" => request_id}), do: request_id
def get_request_id_from_headers(%{"request-id" => request_id}), do: request_id
# Amazon uses their own *special* header
def get_request_id_from_headers(%{"x-amzn-requestid" => request_id}), do: request_id
def get_request_id_from_headers(_headers), do: nil
# Move `:headers` (Map.t) into `:headers_json` (String.t) so that all headers are no indexed
# within Timber by default.
@spec move_headers_to_headers_json(Keyword.t) :: Keyword.t
def move_headers_to_headers_json(opts) do
if opts[:headers] do
headers_json =
opts[:headers]
|> Timber.Utils.JSON.encode_to_iodata!()
|> to_string()
opts
|> Keyword.put(:headers_json, headers_json)
|> Keyword.delete(:headers)
else
opts
end
end
@doc false
# Normalizes the body into a truncated string
@spec normalize_body(any) :: String.t
def normalize_body(nil = body), do: body
def normalize_body("" = body), do: body
def normalize_body(body) when is_map(body) do
case JSON.encode_to_iodata!(body) do
{:ok, json} -> normalize_body(to_string(json))
_ -> nil
end
end
def normalize_body(body) when is_list(body) do
normalize_body(to_string(body))
end
def normalize_body(body) when is_binary(body) do
limit = Config.http_body_size_limit()
body
|> Timber.Utils.Logger.truncate_bytes(limit)
|> to_string()
end
@doc false
# Normalizes HTTP headers into a structure expected by the Timber API.
@spec normalize_headers(Keyword.t | Map.t) :: Map.t
def normalize_headers(headers) when is_list(headers) do
headers
|> List.flatten()
|> Enum.into(%{})
|> normalize_headers()
end
def normalize_headers(headers) when is_map(headers) do
headers
|> Enum.map(&normalize_header/1)
|> Enum.map(&sanitize_header/1)
|> Enum.into(%{})
end
def normalize_headers(headers), do: headers
@doc false
# Normalizes an individual header
@spec normalize_header({String.t, String.t}) :: {String.t, String.t}
# Normalizes headers with multiple values in a comma delimited string as defined by the
# HTTP spec RFC 2616
defp normalize_header({name, value}) when is_list(value) do
normalize_header({String.downcase(name), Enum.join(value, @multi_header_delimiter)})
end
defp normalize_header({name, value}) do
value =
value
|> Timber.Utils.Logger.truncate_bytes(@header_value_byte_limit)
|> to_string()
{String.downcase(name), value}
end
# Sanitizes sensitive headers
defp sanitize_header({key, _value}) when key in @header_keys_to_sanitize do
{key, @sanitized_value}
end
defp sanitize_header({key, _value} = header) do
if Enum.member?(Config.header_keys_to_sanitize(), key) do
{key, @sanitized_value}
else
header
end
end
@doc false
# Normalizes HTTP methods into a value expected by the Timber API.
def normalize_method(method) when is_atom(method) do
method
|> Atom.to_string()
|> normalize_method()
end
def normalize_method(method) when is_binary(method), do: String.upcase(method)
def normalize_method(method), do: method
@doc false
# Normalizes a URL into a Keyword.t that maps to our HTTP event fields.
def normalize_url(url) when is_binary(url) do
uri = URI.parse(url)
[
host: uri.authority,
path: uri.path,
port: uri.port,
query_string: uri.query,
scheme: uri.scheme
]
end
def normalize_url(_url), do: []
@doc false
# Convenience method that checks if the value is an atom and also converts it to a string.
def try_atom_to_string(nil), do: nil
def try_atom_to_string(val) when is_atom(val), do: Atom.to_string(val)
def try_atom_to_string(val), do: val
end