ObserverWeb.Logs (Observer Web v0.2.8)

View Source

Bounded, read-only access to the log files of a node - the web equivalent of observer_cli's log tail pane: read the last chunk of a logger file during an incident without flushing or locking anything.

Log sources are restricted to the file-backed :logger handlers configured on the target node (:logger.get_handler_config/0); free-form paths are never accepted from the caller, so the dashboard cannot be used to read arbitrary files.

The tail itself is a single stdlib-only RPC: a pre-parsed :erl_eval expression opens the file on the remote node, preads at most max_bytes from the end and closes it. Nothing is required on the target node beyond OTP itself, matching how ObserverWeb.SystemInfo keeps remote calls version-independent.

Making logs visible

Elixir applications log to the console by default, in which case there is nothing to tail. Add a file handler to the observed application (standard Elixir 1.15+ configuration):

# config/config.exs
config :my_app, :logger, [
  {:handler, :file_log, :logger_std_h,
   %{config: %{file: ~c"/var/log/my_app/my_app.log"}, formatter: Logger.Formatter.new()}}
]

# lib/my_app/application.ex - in start/2
Logger.add_handlers(:my_app)

Or attach one at runtime without restarting:

:logger.add_handler(:file_log, :logger_std_h, %{config: %{file: ~c"/var/log/my_app.log"}})

See the installation guide's "Logs" section for rotation options and Erlang release notes.

Summary

Functions

File-backed :logger handlers configured on the node.

Read at most max_bytes (capped at 1048576 bytes) from the end of file on node. The file must belong to one of the node's logger handlers - see list_handlers/1.

Types

handler()

@type handler() :: %{id: atom(), module: module(), file: String.t()}

tail()

@type tail() :: %{content: String.t(), size: non_neg_integer(), truncated?: boolean()}

Functions

list_handlers(node)

@spec list_handlers(node()) :: [handler()]

File-backed :logger handlers configured on the node.

Handlers without a file (e.g. console/standard_io) are skipped, as is anything with an unexpected config shape.

tail(node, file, max_bytes \\ 65536)

@spec tail(node(), String.t(), pos_integer()) :: {:ok, tail()} | {:error, term()}

Read at most max_bytes (capped at 1048576 bytes) from the end of file on node. The file must belong to one of the node's logger handlers - see list_handlers/1.

When the file is larger than the requested chunk, the (usually partial) first line is dropped and truncated? is set.