Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding iframe configuration to allow Observer Web to run with embedded pages #10

Merged
merged 3 commits into from
Mar 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 10 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# CHANGELOG (v0.1.5)
# CHANGELOG (v0.1.X)

## 0.1.5 (2025-02-26)
## 0.1.6 ()

### Backwards incompatible changes for 0.1.4
### Backwards incompatible changes for 0.1.5
* None

### Bug fixes
* None

### Enhancements
* [[`PR-8`](https://github.com/thiagoesteves/observer_web/pull/8)] Adding Metrics using telemetry
* [[`PR-9`](https://github.com/thiagoesteves/observer_web/pull/9)] Add support to multiple Endpoints
* [[`PR-10`](https://github.com/thiagoesteves/observer_web/pull/10)] Adding iframe configuration to allow Observer Web to run with embedded pages

# Previous Releases
* [0.1.4 🚀 (2025-02-11)](https://github.com/thiagoesteves/observer_web/blob/v0.1.4/CHANGELOG.md)
* [0.1.3 🚀 (2025-02-08)](https://github.com/thiagoesteves/observer_web/blob/v0.1.3/CHANGELOG.md)
* [0.1.2 🚀 (2025-02-08)](https://github.com/thiagoesteves/observer_web/blob/v0.1.2/CHANGELOG.md)
* [0.1.0 🚀 (2025-01-06)](https://github.com/thiagoesteves/observer_web/blob/v0.1.0/CHANGELOG.md)
# 🚀 Previous Releases
* [0.1.5 (2025-02-26)](https://github.com/thiagoesteves/observer_web/blob/v0.1.5/CHANGELOG.md)
* [0.1.4 (2025-02-11)](https://github.com/thiagoesteves/observer_web/blob/v0.1.4/CHANGELOG.md)
* [0.1.3 (2025-02-08)](https://github.com/thiagoesteves/observer_web/blob/v0.1.3/CHANGELOG.md)
* [0.1.2 (2025-02-08)](https://github.com/thiagoesteves/observer_web/blob/v0.1.2/CHANGELOG.md)
* [0.1.0 (2025-01-06)](https://github.com/thiagoesteves/observer_web/blob/v0.1.0/CHANGELOG.md)
68 changes: 68 additions & 0 deletions guides/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,74 @@ config :observer_web, ObserverWeb.Telemetry,
data_retention_period: :timer.minutes(5)
```

### Embedding Observer Web in your app page

In some cases, you may prefer to run the Observer in the same page as your app rather than in
a separate page. This embedded approach is possible by setting the iframe flag. To implement
this, your router must add a root path. Here's an example for your application's `router.ex`:

```elixir
# lib/my_app_web/router.ex
use MyAppWeb, :router

import Observer.Web.Router

...

scope "/" do
pipe_through :browser

live_session :require_authenticated_user,
on_mount: [{MyAppWeb.UserAuth, :ensure_authenticated}] do
...
live "/embedded-observer", ObserverLive, :index
...
end

observer_dashboard "/observer"
end
```

Next, create an ObserverLive file at `live/observer/index.ex`:

```elixir
defmodule MyAppWeb.ObserverLive do
@moduledoc """
"""
use MyAppWeb, :live_view

@impl true
def render(assigns) do
~H"""
<div>
<iframe src={~p"/observer/tracing?iframe=true"} class="min-h-screen" width="100%" height="100%" title="Observer Web">
</iframe>
</div>
"""
end

@impl true
def mount(_params, _session, socket) do
{:ok, socket}
end

@impl true
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end

defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "Observer Web")
end
end
```

You can still access the Observer as a separate page by navigating directly to the
path `/observer"`. However, using the iframe approach allows you to display
your application's information alongside the Observer in your main page,
providing a more integrated monitoring experience.

### Usage with Web and Clustering

The Observer Web provides observer ability for the local application as well as any other that is
Expand Down
7 changes: 5 additions & 2 deletions guides/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ library is part of the [DeployEx][dye] project.
## Features

- **🐦‍🔥 Embedded LiveView** - Mount the dashboard directly in your application without any
external dependencies.
external dependencies.

- **🔍 Real Time Tracing** - Trace any function within your application, capturing parameters passed
and also function callers, as many other possibilities.

- **🔬 Process/Port Inspection** - View processes and ports details as well as their status and
connectivity (and much more).

- **📊 Realtime VM Metrics** - - **📊 Realtime VM Metrics** - Powered by ets table and OTP
- **📊 Realtime VM Metrics** - Powered by ets table and OTP
distribution, vm memory statistics are stored and easily filtered.

- **🖼️ Embedded Mode** - Observer Web can be run using iframes, seamlessly integrating the
observability experience within your application.

## Installation

See the [installation guide](installation.md) for details on installing and configuring Observer Web
Expand Down
11 changes: 7 additions & 4 deletions lib/web/components/layouts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ defmodule Observer.Web.Layouts do

def logo(assigns) do
~H"""
<a href={observer_path(:tracing)} class="flex" title="Observer Web">
<a href={observer_path(:tracing, @params)} class="flex" title="Observer Web">
<div>
<Icons.content name={:logo} />
</div>
Expand All @@ -49,11 +49,11 @@ defmodule Observer.Web.Layouts do
~H"""
<nav class="flex space-x-1">
<.link
:for={page <- [:root, :tracing, :applications, :metrics]}
:for={page <- list_pages_by_params(@params)}
class={link_class(@page, page)}
data-shortcut={JS.navigate(observer_path(page))}
data-shortcut={JS.navigate(observer_path(page, @params))}
id={"nav-#{page}"}
navigate={observer_path(page)}
navigate={observer_path(page, @params)}
title={"Navigate to #{String.capitalize(to_string(page))}"}
>
<Icons.content name={page} />
Expand Down Expand Up @@ -107,4 +107,7 @@ defmodule Observer.Web.Layouts do
base <> " hover:text-white hover:bg-indigo-500"
end
end

defp list_pages_by_params(%{"iframe" => "true"}), do: [:tracing, :applications, :metrics]
defp list_pages_by_params(_params), do: [:root, :tracing, :applications, :metrics]
end
4 changes: 2 additions & 2 deletions lib/web/components/layouts/live.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
<main class="p-4 min-h-screen flex flex-col">
<header class="flex items-center mb-2">
<div class="md:w-84 mr-3">
<.logo />
<.logo params={@params} />
</div>

<.nav socket={@socket} page={@page.name} />
<.nav socket={@socket} page={@page.name} params={@params} />
</header>
{@inner_content}
<.footer />
Expand Down
5 changes: 5 additions & 0 deletions lib/web/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ defmodule Observer.Web.Helpers do
|> observer_path(params)
end

# NOTE: Filter page to avoid conflicts with the route path
def observer_path(route, %{"page" => _name} = params) do
observer_path(route, Map.delete(params, "page"))
end

def observer_path(route, params) do
params =
params
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule ObserverWeb.MixProject do
use Mix.Project

@source_url "https://github.com/thiagoesteves/observer_web"
@version "0.1.5"
@version "0.1.6"

def project do
[
Expand Down
30 changes: 30 additions & 0 deletions test/observer_web/web/live/index.test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,34 @@ defmodule Observer.Web.IndexLiveTest do
assert {:error, {:redirect, redirect}} = live(conn, "/observer-limited")
assert %{to: "/", flash: %{"error" => "Access forbidden"}} = redirect
end

test "Check iframe OFF allows root buttom", %{conn: conn} do
ObserverWeb.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)

ObserverWeb.TelemetryMock
|> stub(:push_data, fn _event -> :ok end)

{:ok, _index_live, html} = live(conn, "/observer/tracing")

assert html =~ "Live Tracing"
assert html =~ "ROOT"
end

test "Check iframe ON doesn't allow root buttom", %{conn: conn} do
ObserverWeb.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)

ObserverWeb.TelemetryMock
|> stub(:push_data, fn _event -> :ok end)

{:ok, _index_live, html} = live(conn, "/observer/tracing?iframe=true")

assert html =~ "Live Tracing"
refute html =~ "ROOT"
end
end