Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
cc0139c
initialized dashboard
kagux Sep 30, 2017
dbf5b9a
use dashboard_layout
kagux Sep 30, 2017
dac4be0
installed adminlte package
kagux Sep 30, 2017
8a9b6f7
updated brunch config to include dashboard
kagux Sep 30, 2017
c1fbe49
simple dashboard controller test
kagux Oct 8, 2017
66445c2
dashboard assets
kagux Oct 8, 2017
168e0c6
working demo layout
kagux Oct 8, 2017
1573bc8
added offline google fonts
kagux Oct 8, 2017
1962e53
removed unused elements from layout
kagux Oct 9, 2017
a58b1a5
added .DS_Store to .gitignore
kagux Oct 9, 2017
23a001b
don't track favicon by default
kagux Oct 10, 2017
f5505ff
added counts to info boxes
kagux Oct 10, 2017
4c02757
wip chart
kagux Oct 11, 2017
883e6b6
show only requests in dashboard
kagux Oct 12, 2017
d757e28
limit maximum number of requests
kagux Oct 18, 2017
a70a7a0
requests count
kagux Oct 18, 2017
824b778
use genserver to store requests instead of mnesia
kagux Oct 26, 2017
56a5f20
cleaned up unused history helpers
kagux Nov 4, 2017
934b77a
wip extract broadcast
kagux Nov 5, 2017
e89a042
Merge branch 'master' into feature/dashboard
kagux Nov 13, 2017
0d5d7e1
only broadcast stopped request
kagux Nov 25, 2017
43f2498
renamed request:ready into request:created event
kagux Nov 25, 2017
54f2c45
test that toolbar channel correctly handles request:created event
kagux Nov 25, 2017
da7c8dc
created dashboard channel that handles join, request:created and
kagux Nov 25, 2017
7ab4f0b
broadcast request:created to both dashboard and toolbar
kagux Nov 25, 2017
629fe35
broadcast deleted requests in janitor
kagux Nov 25, 2017
7419de1
hooked up dashboard to the channel
kagux Nov 27, 2017
03a4760
fixed janitor test
kagux Dec 6, 2017
5e67b53
for now removed notifications from dashboard layout
kagux Dec 6, 2017
860b8fb
renamed `:max_requests` to `:requests_limit` and updated janitor test to
kagux Dec 6, 2017
2545092
do not render requests in progress
kagux Dec 6, 2017
ec7becc
correctly print log for new request in dashboard
kagux Dec 6, 2017
4ae4cbd
moved request stop into request repo
kagux Dec 6, 2017
d896d2b
bump elixir version in travis
kagux Jan 5, 2018
b32fd38
renamed dashboard controller into dashboard/request_controller
kagux Jan 5, 2018
f4759ed
simple single request page
kagux Jan 5, 2018
76cd0bc
simple mock up of single request page
kagux Jan 5, 2018
39809e7
added pointer cursor to dashbaord table
kagux Jan 19, 2018
d2d8b85
updated tab icons
kagux Jan 19, 2018
b7143d9
mock up request header
kagux Jan 19, 2018
d0f3c2c
do not add ecto queries from other processes into timeline
kagux Jan 20, 2018
9e5d953
updated packages and made phoenix 1.3 requirement
kagux Jan 20, 2018
f72e66a
updated npm packages
kagux Jan 20, 2018
25e3a98
compiled assets
kagux Jan 20, 2018
395de6d
moved controller time breakdown from view to timeline
kagux Jan 20, 2018
8d54453
extract ecto related function from view into lib
kagux Jan 21, 2018
8848127
moved breakpoint related code from view to Breakpoint
kagux Jan 21, 2018
69046e9
display reuqest duration grouped by event types
kagux Feb 4, 2018
00c4f74
use dot in template events to separate event group
kagux Feb 4, 2018
e0f45c3
improve grouped duration rendering
kagux Feb 5, 2018
8129ea6
improved color cycles
kagux Feb 5, 2018
5968663
merged timings and conn details tabs of toolbar
kagux Feb 17, 2018
49c9ba5
encoder for Refernece
kagux Feb 21, 2018
106f57c
Merge branch 'master' into feature/dashboard
kagux Feb 22, 2018
9c51728
updated toolbar overview to show simple duration breakdown and link to
kagux Mar 11, 2018
fcad56e
show some parameters in overview tab
kagux Mar 12, 2018
ff3bd60
updated toolbar view test to always assign uuid to request
kagux Mar 12, 2018
f721b8a
Merge branch 'master' into feature/dashboard
kagux Apr 3, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ erl_crash.dump
/config/prod.secret.exs
/tmp
/doc
.DS_Store
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ language: elixir
elixir:
- 1.6.4
- 1.5.3
- 1.4.5
otp_release:
- 20.0
- 19.3.6
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ To change configuration, update `:ex_debug_toolbar` config key in your `config/d
| remove_glob_params | boolean | true | `Plug.Router` adds `glob` params to `conn.params` and `conn.path_params` on `forward`. This option removes them |
| ignore_paths | list | [~r{^/images/}, ~r{^/css/}, ~r{^/js/}, ~r{^/phoenix/live_reload/}] | A list of paths that should not be recorded by toolbar. Each item can be either string for exact match or a Regex. |
| debug | boolean | false | Toggles debug logs. Requires recompilation for some logs |
| requests_limit | integer | 30 | Number of requests to keep in history |


# Troubleshooting
Expand Down
16 changes: 12 additions & 4 deletions brunch-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ exports.config = {
// See http://brunch.io/#documentation for docs.
files: {
javascripts: {
joinTo: {
"js/toolbar.js": /^(web\/static\/js\/toolbar)|^node_modules/,
// build output file based on required dependencies in entrypoint
entryPoints: {
"web/static/js/dashboard.js": "js/dashboard.js",
"web/static/js/toolbar.js": "js/toolbar.js",
}
},
stylesheets: {
joinTo: {
"css/toolbar.css": /^(web\/static\/css\/toolbar)|^node_modules/,
"css/dashboard.css": /^(web\/static\/css\/dashboard)|^node_modules/,
},
order: {
after: /prism/
Expand Down Expand Up @@ -45,7 +48,10 @@ exports.config = {
ignore: [/web\/static\/vendor/]
},
copycat:{
"fonts" : ["node_modules/bootstrap-sass/assets/fonts"],
"fonts" : [
"node_modules/bootstrap-sass/assets/fonts",
"node_modules/font-awesome/fonts"
],
verbose : true, //shows each file that is copied to the destination directory
onlyChanged: true //only copy a file if it's modified time has changed (only effective when using brunch watch)
},
Expand All @@ -54,6 +60,7 @@ exports.config = {
mode: 'ruby',
includePaths: [
'node_modules/bootstrap-sass/assets/stylesheets/',
'node_modules/font-awesome/scss/',
'node_modules/css-reset-and-normalize-sass/scss/',
'node_modules/'
],
Expand All @@ -66,7 +73,8 @@ exports.config = {

modules: {
autoRequire: {
"js/toolbar.js": ["web/static/js/toolbar"]
"js/toolbar.js": ["web/static/js/toolbar"],
"js/dashboard.js": ["web/static/js/dashboard"]
}
},

Expand Down
3 changes: 2 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ config :ex_debug_toolbar,
iex_shell_cmd: "stty echo\n",
breakpoints_limit: 3,
remove_glob_params: true,
debug: true
debug: true,
requests_limit: 10

config :ex_debug_toolbar, ExDebugToolbar.Fixtures.Endpoint,
instrumenters: [ExDebugToolbar.Collector.InstrumentationCollector],
Expand Down
23 changes: 13 additions & 10 deletions lib/ex_debug_toolbar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,31 @@ defmodule ExDebugToolbar do
@spec stop_request(id) :: ok
@decorate noop_when_toolbar_disabled()
def stop_request(id) do
:ok = RequestRepo.update(id, &(%{&1 | stopped?: true}), async: false)
:ok = RequestRepo.stop(id)
end

@doc """
Deletes request from repository
"""
@spec delete_request(uuid) :: ok
@decorate noop_when_toolbar_disabled()
def delete_request(uuid) do
RequestRepo.delete(uuid)
end
defdelegate delete_request(uuid), to: RequestRepo, as: :delete

@doc """
Returns total number of tracked requests

*Note*: this count excludes requests in progress
"""
@spec get_requests_count :: integer
@decorate noop_when_toolbar_disabled(0)
defdelegate get_requests_count, to: RequestRepo, as: :stopped_count

@doc """
Returns request matching provided `id`, which defaults to `self()`
"""
@spec get_request(id) :: Request.t()
@decorate noop_when_toolbar_disabled()
def get_request(id \\ self()) do
RequestRepo.get(id)
end
defdelegate get_request(id \\ self()), to: RequestRepo, as: :get

@doc """
Returns the breakpoint
Expand All @@ -74,9 +79,7 @@ defmodule ExDebugToolbar do
"""
@spec get_all_requests() :: [Request.t()]
@decorate noop_when_toolbar_disabled([])
def get_all_requests do
RequestRepo.all
end
defdelegate get_all_requests, to: RequestRepo, as: :all

@doc """
Starts a timeline event `name` in request identified by `id`, which defaults to `self()`
Expand Down
4 changes: 3 additions & 1 deletion lib/ex_debug_toolbar/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ defmodule ExDebugToolbar.Application do
children = [
# Start the endpoint when the application starts
supervisor(ExDebugToolbar.Endpoint, []),
supervisor(ExDebugToolbar.Database.Supervisor, []),
worker(ExDebugToolbar.Database.RequestRepo, []),
worker(:exec, [[env: [{'SHELL', Config.get_iex_shell()}, {'MIX_ENV', to_charlist(Mix.env)}]]]),
]
janitor = worker(ExDebugToolbar.Database.Janitor, [])
children = if Mix.env == :test, do: children, else: [janitor | children]

# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
Expand Down
19 changes: 19 additions & 0 deletions lib/ex_debug_toolbar/breakpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule ExDebugToolbar.Breakpoint do
@moduledoc false

alias ExDebugToolbar.Breakpoint.{IEx.Server, Pry}
alias ExDebugToolbar.{Breakpoint, Request}

defstruct [
:id,
Expand Down Expand Up @@ -34,4 +35,22 @@ defmodule ExDebugToolbar.Breakpoint do
term -> raise ArgumentError, "Expected string to be base64 encoded %Breakpoint{}, but got #{inspect(term)}"
end
end

def get_code_snippet_start_line(%Breakpoint{code_snippet: code_snippet}) do
code_snippet |> hd |> Tuple.to_list |> List.last
end

def get_sorted_binding(%Breakpoint{binding: binding}) do
binding |> Keyword.keys |> Enum.sort
end

def get_relative_line(%Breakpoint{code_snippet: code_snippet, line: line}) do
code_snippet
|> Enum.find_index(fn {_, n} -> n == line end)
|> Kernel.+(1)
end

def get_uuid(%Request{uuid: request_id}, %Breakpoint{id: id}) do
%Breakpoint.UUID{request_id: request_id, breakpoint_id: id}
end
end
4 changes: 3 additions & 1 deletion lib/ex_debug_toolbar/collector/ecto_collector.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ if Code.ensure_compiled?(Ecto) do
def log(%LogEntry{} = original_entry) do
entry = original_entry |> remove_result_rows |> cast_params
{id, duration, type} = parse_entry(entry)
ExDebugToolbar.add_finished_event(id, "ecto.query", duration)
if type == :inline do
ExDebugToolbar.add_finished_event(id, "ecto.query", duration)
end
ExDebugToolbar.add_data(id, :ecto, {entry, duration, type})
original_entry
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule ExDebugToolbar.Collector.InstrumentationCollector do
end
def ex_debug_toolbar(:stop, _, _) do
ExDebugToolbar.stop_request(self())
ExDebugToolbar.ToolbarChannel.broadcast_request
ExDebugToolbar.Request.Broadcast.request_created()
end

def phoenix_controller_call(:start, _, _) do
Expand Down
2 changes: 1 addition & 1 deletion lib/ex_debug_toolbar/collector/template_collector.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule ExDebugToolbar.Collector.TemplateCollector do
def compile(path, name) do
compiled_template = unquote(opts[:engine]).compile(path, name)
quote do
ExDebugToolbar.record_event("template##{unquote(path)}", fn ->
ExDebugToolbar.record_event("template.#{unquote(path)}", fn ->
unquote(compiled_template)
end)
end
Expand Down
9 changes: 9 additions & 0 deletions lib/ex_debug_toolbar/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Code.compiler_options(ignore_module_conflict: true)

defmodule ExDebugToolbar.Config do
@breakpoints_limit 10
@requests_limit 30

def get(key, default) do
Application.get_env(:ex_debug_toolbar, key, default)
Expand Down Expand Up @@ -32,6 +33,14 @@ defmodule ExDebugToolbar.Config do
Application.get_env(:ex_debug_toolbar, :breakpoints_limit, @breakpoints_limit)
end

def get_requests_limit do
Application.get_env(:ex_debug_toolbar, :requests_limit, @requests_limit)
end

def set_requests_limit(limit) do
Application.put_env(:ex_debug_toolbar, :requests_limit, limit)
end

def update do
config = Application.get_env(:ex_debug_toolbar, ExDebugToolbar.Endpoint, [])
|> Keyword.put(:pubsub, [name: ExDebugToolbar.PubSub, adapter: Phoenix.PubSub.PG2])
Expand Down
50 changes: 47 additions & 3 deletions lib/ex_debug_toolbar/data/timeline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule ExDebugToolbar.Data.Timeline do

defstruct [
name: nil,
own_duration: 0,
duration: 0,
started_at: nil,
events: [],
Expand All @@ -32,16 +33,20 @@ defmodule ExDebugToolbar.Data.Timeline do
def finish_event(timeline, name, opts \\ [])
def finish_event(%Timeline{queue: [%{name: name} = event]} = timeline, name, opts) do
events = timeline.events
finished_event = set_duration(event, opts)
finished_event = event |> set_duration(opts) |> set_own_duration()
%{timeline |
queue: [],
events: [finished_event | events],
duration: finished_event.duration + timeline.duration
}
end

def finish_event(%Timeline{queue: [%{name: name} = event | [parent | rest]]} = timeline, name, opts) do
finished_event = set_duration(event, opts)
new_parent = %{parent | events: [finished_event | parent.events]}
finished_event = event |> set_duration(opts) |> set_own_duration()
new_parent =
parent
|> Map.update!(:events, &([finished_event | &1]))
|> Map.update!(:own_duration, &(&1 - finished_event.duration))
%{timeline | queue: [new_parent | rest]}
end
def finish_event(_timeline, name, _opts), do: raise "the event #{name} is not open"
Expand All @@ -55,6 +60,41 @@ defmodule ExDebugToolbar.Data.Timeline do
Enum.flat_map(events, &([&1 | get_all_events(&1)]))
end

def group_own_durations(%Timeline{} = timeline) do
timeline
|> Timeline.get_all_events
|> Enum.group_by(
fn event -> event.name |> String.split(".", parts: 2) |> hd end,
&Map.get(&1, :own_duration)
)
|> Stream.map(fn {k, v} -> {k, Enum.sum(v)} end)
|> Map.new
end

def breakdown_templates_duration(%Timeline{} = timeline) do
timeline
|> Timeline.get_all_events
|> Stream.filter(&String.starts_with?(&1.name, "template#"))
|> Enum.reduce(%{}, fn event, acc ->
Map.update(
acc,
event.name,
%{count: 1, durations: [event.duration], min: 0, max: 0, avg: 0, total: 0},
&(%{&1 | count: &1.count + 1, durations: [event.duration | &1.durations]})
)
end)
|> Stream.map(fn {name, stats} ->
{name, %{stats |
min: Enum.min(stats.durations),
max: Enum.max(stats.durations),
total: Enum.sum(stats.durations),
avg: div(Enum.sum(stats.durations), Enum.count(stats.durations))
}}
end)
|> Stream.map(fn {name, stats} -> {String.trim_leading(name, "template#"), stats} end)
|> Enum.sort_by(fn {_, stats} -> -stats.total end)
end

defp set_duration(event, opts) do
duration = case {opts[:duration], opts[:timestamp]} do
{nil, nil} -> 0
Expand All @@ -63,6 +103,10 @@ defmodule ExDebugToolbar.Data.Timeline do
end
%{event | duration: duration}
end

defp set_own_duration(event) do
event |> Map.update!(:own_duration, &(&1 + event.duration))
end
end

alias ExDebugToolbar.Data.{Collection, Timeline}
Expand Down
39 changes: 0 additions & 39 deletions lib/ex_debug_toolbar/database.ex

This file was deleted.

41 changes: 41 additions & 0 deletions lib/ex_debug_toolbar/database/janitor.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule ExDebugToolbar.Database.Janitor do
@moduledoc false

alias ExDebugToolbar.Database.RequestRepo
alias ExDebugToolbar.{Logger, Config}
alias ExDebugToolbar.Request.Broadcast

use GenServer

@doc "deletes requests from repository once they reach a limit"
def cleanup_requests do
limit = Config.get_requests_limit()
extra = max(0, RequestRepo.count() - limit)
if extra > 0 do
Logger.debug "Cleaning up #{extra} requests"
extra |> RequestRepo.pop |> Enum.each(&Broadcast.request_deleted/1)
end
end

@tick 500

def start_link do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end

def init(_) do
schedule_tick()
{:ok, nil}
end

def handle_info(:tick, state) do
cleanup_requests()
schedule_tick()

{:noreply, state}
end

defp schedule_tick do
Process.send_after(self(), :tick, @tick)
end
end
Loading