Packages
timber
2.0.0-rc4
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/events/exception_event.ex
defmodule Timber.Events.ExceptionEvent do
@moduledoc """
The `ExceptionEvent` is used to track exceptions.
Timber automatically tracks and structures exceptions in your application. Giving
you detailed stack traces, context, and exception data.
"""
@type stacktrace_entry :: {
module,
atom,
arity,
[file: IO.chardata, line: non_neg_integer] | []
}
@type backtrace_entry :: %{
app_name: String.t | nil,
function: String.t,
file: String.t | nil,
line: non_neg_integer | nil
}
@type t :: %__MODULE__{
backtrace: [backtrace_entry] | [],
name: String.t,
message: String.t,
}
@enforce_keys [:backtrace, :name, :message]
defstruct [:backtrace, :name, :message]
@app_name_limit 255
@file_limit 1_000
@function_limit 255
@message_limit 10_000
@name_limit 255
@doc """
Builds a new struct taking care to normalize data into a valid state. This should
be used, where possible, instead of creating the struct directly.
"""
@spec new(String.t) :: {:ok, t} | {:error, atom}
def new(log_message) do
lines =
log_message
|> String.split("\n")
|> Enum.map(&({&1, String.trim(&1)}))
case do_new({nil, "", []}, lines) do
{name, message, backtrace} when is_binary(name) and length(backtrace) > 0 ->
name = String.slice(name, 0..(@name_limit - 1))
message = String.slice(message, 0..(@message_limit - 1))
{:ok, %__MODULE__{name: name, message: message, backtrace: backtrace}}
_ ->
{:error, :could_not_parse_message}
end
end
# ** (exit) an exception was raised:
defp do_new({nil, message, [] = backtrace}, [{_raw_line, ("** (exit) " <> _suffix)} | lines]) do
do_new({nil, message, backtrace}, lines)
end
# ** (RuntimeError) my message
defp do_new({nil, _message, [] = backtrace}, [{_raw_line, ("** (" <> line_suffix)} | lines]) do
# Using split since it is more performance with binary scanning
[name, message] = String.split(line_suffix, ")", parts: 2)
do_new({name, message, backtrace}, lines)
end
# Ignore other leading messages
defp do_new({nil, _message, _backtrace} = acc, [_line | lines]), do: do_new(acc, lines)
# (odin_client_api) web/controllers/page_controller.ex:5: Odin.ClientAPI.PageController.index/2
defp do_new({name, message, backtrace}, [{_raw_line, ("(" <> line_suffix)} | lines]) when not is_nil(name) and not is_nil(message) do
# Using split since it is more performance with binary scanning
[app_name, line_suffix] = String.split(line_suffix, ")", parts: 2)
[file, line_suffix] = String.split(line_suffix, ":", parts: 2)
[line_number, function] = String.split(line_suffix, ":", parts: 2)
app_name = String.slice(app_name, 0..(@app_name_limit - 1))
function = String.slice(function, 0..(@function_limit - 1))
file = String.slice(file, 0..(@file_limit - 1))
line = %{
app_name: app_name,
function: String.trim(function),
file: String.trim(file),
line: parse_line_number(line_number)
}
do_new({name, message, [line | backtrace]}, lines)
end
# Ignore lines we don't recognize.
defp do_new(acc, [_line | lines]), do: do_new(acc, lines)
# Finish the iteration, reversing the backtrace for performance reasons.
defp do_new({name, message, backtrace}, []) do
{name, String.trim(message), Enum.reverse(backtrace)}
end
defp parse_line_number(line_str) do
case Integer.parse(line_str) do
{line, _unit} -> line
:error -> nil
end
end
@doc """
Message to be used when logging.
"""
@spec message(t) :: IO.chardata
def message(%__MODULE__{name: name, message: message}), do: [?(, name, ?), ?\s, message]
end