mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-03-13 23:22:40 +00:00
Telemetry WIP
This commit is contained in:
parent
63663ac88b
commit
ba3280fc16
41 changed files with 1121 additions and 198 deletions
|
@ -132,18 +132,20 @@ config :pleroma, Pleroma.Web.Endpoint,
|
|||
]
|
||||
|
||||
# Configures Elixir's Logger
|
||||
config :logger, backends: [:console]
|
||||
config :logger, backends: [:console, RingLogger]
|
||||
|
||||
config :logger, :console,
|
||||
level: :debug,
|
||||
format: "\n$time $metadata[$level] $message\n",
|
||||
metadata: [:actor, :path, :type, :user]
|
||||
metadata: []
|
||||
|
||||
config :logger, RingLogger, max_size: 1024
|
||||
|
||||
config :logger, :ex_syslogger,
|
||||
level: :debug,
|
||||
ident: "pleroma",
|
||||
format: "$metadata[$level] $message",
|
||||
metadata: [:actor, :path, :type, :user]
|
||||
metadata: []
|
||||
|
||||
config :mime, :types, %{
|
||||
"application/xml" => ["xml"],
|
||||
|
@ -950,6 +952,8 @@ config :pleroma, Pleroma.Search.QdrantSearch,
|
|||
vectors: %{size: 384, distance: "Cosine"}
|
||||
}
|
||||
|
||||
config :pleroma, Pleroma.Telemetry, phoenix_logs: true
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env()}.exs"
|
||||
|
|
|
@ -24,6 +24,8 @@ config :logger, Logger.Backends.Console, level: :info
|
|||
config :logger, :console, level: :info
|
||||
config :logger, :ex_syslogger, level: :info
|
||||
|
||||
config :pleroma, Pleroma.Telemetry, phoenix_logs: false
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
# To get SSL working, you will need to add the `https` key
|
||||
|
|
|
@ -76,7 +76,7 @@ defmodule Mix.Tasks.Pleroma.Uploads do
|
|||
|> Task.async_stream(
|
||||
fn {upload, root_path} ->
|
||||
case Upload.store(upload, uploader: uploader, filters: [], size_limit: nil) do
|
||||
{:ok, _} ->
|
||||
{:ok, _, _} ->
|
||||
if delete?, do: File.rm_rf!(root_path)
|
||||
Logger.debug("uploaded: #{inspect(upload.path)} #{inspect(upload)}")
|
||||
:ok
|
||||
|
|
|
@ -46,7 +46,7 @@ defmodule Pleroma.Application do
|
|||
# Disable warnings_as_errors at runtime, it breaks Phoenix live reload
|
||||
# due to protocol consolidation warnings
|
||||
Code.compiler_options(warnings_as_errors: false)
|
||||
Pleroma.Telemetry.Logger.attach()
|
||||
Pleroma.Telemetry.Logger.setup()
|
||||
Config.Holder.save_default()
|
||||
Pleroma.HTML.compile_scrubbers()
|
||||
Pleroma.Config.Oban.warn()
|
||||
|
@ -93,6 +93,8 @@ defmodule Pleroma.Application do
|
|||
# Define workers and child supervisors to be supervised
|
||||
children =
|
||||
[
|
||||
Pleroma.Web.Telemetry,
|
||||
{Pleroma.Web.MetricsStorage, Pleroma.Web.Telemetry.metrics()},
|
||||
Pleroma.PromEx,
|
||||
Pleroma.LDAP,
|
||||
Pleroma.Repo,
|
||||
|
|
|
@ -36,7 +36,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
|
|||
|> round
|
||||
|> max(1)
|
||||
|
||||
:telemetry.execute([:pleroma, :connection_pool, :reclaim, :start], %{}, %{
|
||||
:telemetry.execute([:pleroma, :connection_pool, :reclaim, :start], %{count: 1}, %{
|
||||
max_connections: max_connections,
|
||||
reclaim_max: reclaim_max
|
||||
})
|
||||
|
@ -56,7 +56,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
|
|||
[] ->
|
||||
:telemetry.execute(
|
||||
[:pleroma, :connection_pool, :reclaim, :stop],
|
||||
%{reclaimed_count: 0},
|
||||
%{count: 0},
|
||||
%{
|
||||
max_connections: max_connections
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
|
|||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :connection_pool, :reclaim, :stop],
|
||||
%{reclaimed_count: Enum.count(reclaimed)},
|
||||
%{count: Enum.count(reclaimed)},
|
||||
%{max_connections: max_connections}
|
||||
)
|
||||
|
||||
|
|
|
@ -71,8 +71,8 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :connection_pool, :client, :add],
|
||||
%{client_pid: client_pid, clients: used_by},
|
||||
%{key: state.key, protocol: protocol}
|
||||
%{count: 1, start_time: time},
|
||||
%{client_pid: client_pid, clients: used_by, key: state.key, protocol: protocol}
|
||||
)
|
||||
|
||||
state =
|
||||
|
@ -115,23 +115,27 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
def handle_info(:idle_close, state) do
|
||||
# Gun monitors the owner process, and will close the connection automatically
|
||||
# when it's terminated
|
||||
update_telemetry_worker_count()
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:gun_up, _pid, _protocol}, state) do
|
||||
update_telemetry_worker_count()
|
||||
{:noreply, state, :hibernate}
|
||||
end
|
||||
|
||||
# Gracefully shutdown if the connection got closed without any streams left
|
||||
@impl true
|
||||
def handle_info({:gun_down, _pid, _protocol, _reason, []}, state) do
|
||||
update_telemetry_worker_count()
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
|
||||
# Otherwise, wait for retry
|
||||
@impl true
|
||||
def handle_info({:gun_down, _pid, _protocol, _reason, _killed_streams}, state) do
|
||||
update_telemetry_worker_count()
|
||||
{:noreply, state, :hibernate}
|
||||
end
|
||||
|
||||
|
@ -139,10 +143,12 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
def handle_info({:DOWN, _ref, :process, pid, reason}, state) do
|
||||
:telemetry.execute(
|
||||
[:pleroma, :connection_pool, :client, :dead],
|
||||
%{client_pid: pid, reason: reason},
|
||||
%{key: state.key}
|
||||
%{count: 1},
|
||||
%{client_pid: pid, reason: reason, key: state.key}
|
||||
)
|
||||
|
||||
update_telemetry_worker_count()
|
||||
|
||||
handle_cast({:remove_client, pid}, state)
|
||||
end
|
||||
|
||||
|
@ -150,4 +156,12 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
defp crf(time_delta, prev_crf) do
|
||||
1 + :math.pow(0.5, 0.0001 * time_delta) * prev_crf
|
||||
end
|
||||
|
||||
def update_telemetry_worker_count do
|
||||
:telemetry.execute(
|
||||
[:pleroma, :connection_pool, :worker],
|
||||
%{count: Registry.count(Pleroma.Gun.ConnectionPool)},
|
||||
%{}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,7 +26,10 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do
|
|||
def start_worker(opts, true) do
|
||||
case DynamicSupervisor.start_child(__MODULE__, {Worker, opts}) do
|
||||
{:error, :max_children} ->
|
||||
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts})
|
||||
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{count: 1}, %{
|
||||
opts: opts
|
||||
})
|
||||
|
||||
{:error, :pool_full}
|
||||
|
||||
res ->
|
||||
|
|
|
@ -70,7 +70,9 @@ defmodule Pleroma.HTTP do
|
|||
|
||||
extra_middleware = options[:tesla_middleware] || []
|
||||
|
||||
client = Tesla.client(adapter_middlewares(adapter, extra_middleware), adapter)
|
||||
middlewares = [Tesla.Middleware.Telemetry] ++ extra_middleware ++ adapter_middlewares(adapter)
|
||||
|
||||
client = Tesla.client(middlewares, adapter)
|
||||
|
||||
maybe_limit(
|
||||
fn ->
|
||||
|
@ -104,21 +106,20 @@ defmodule Pleroma.HTTP do
|
|||
fun.()
|
||||
end
|
||||
|
||||
defp adapter_middlewares(Tesla.Adapter.Gun, extra_middleware) do
|
||||
[Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool] ++
|
||||
extra_middleware
|
||||
defp adapter_middlewares(Tesla.Adapter.Gun) do
|
||||
[Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool]
|
||||
end
|
||||
|
||||
defp adapter_middlewares({Tesla.Adapter.Finch, _}, extra_middleware) do
|
||||
[Tesla.Middleware.FollowRedirects] ++ extra_middleware
|
||||
defp adapter_middlewares({Tesla.Adapter.Finch, _}) do
|
||||
[Tesla.Middleware.FollowRedirects]
|
||||
end
|
||||
|
||||
defp adapter_middlewares(_, extra_middleware) do
|
||||
defp adapter_middlewares(_) do
|
||||
if Pleroma.Config.get(:env) == :test do
|
||||
# Emulate redirects in test env, which are handled by adapters in other environments
|
||||
[Tesla.Middleware.FollowRedirects]
|
||||
else
|
||||
extra_middleware
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -97,9 +97,33 @@ defmodule Pleroma.Instances.Instance do
|
|||
def reachable?(url_or_host) when is_binary(url_or_host), do: true
|
||||
|
||||
def set_reachable(url_or_host) when is_binary(url_or_host) do
|
||||
%Instance{host: host(url_or_host)}
|
||||
|> changeset(%{unreachable_since: nil})
|
||||
|> Repo.insert(on_conflict: {:replace, [:unreachable_since]}, conflict_target: :host)
|
||||
host = host(url_or_host)
|
||||
|
||||
existing_record = Repo.get_by(Instance, %{host: host})
|
||||
|
||||
cond do
|
||||
is_nil(existing_record) ->
|
||||
%Instance{}
|
||||
|> changeset(%{host: host})
|
||||
|> Repo.insert()
|
||||
|
||||
is_nil(existing_record.unreachable_since) ->
|
||||
{:ok, existing_record}
|
||||
|
||||
true ->
|
||||
result =
|
||||
%Instance{host: host}
|
||||
|> changeset(%{unreachable_since: nil})
|
||||
|> Repo.insert(on_conflict: {:replace, [:unreachable_since]}, conflict_target: :host)
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :instance, :reachable],
|
||||
%{count: 1},
|
||||
%{host: host, instance: existing_record}
|
||||
)
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def set_reachable(_), do: {:error, nil}
|
||||
|
@ -113,21 +137,30 @@ defmodule Pleroma.Instances.Instance do
|
|||
|
||||
changes = %{unreachable_since: unreachable_since}
|
||||
|
||||
cond do
|
||||
is_nil(existing_record) ->
|
||||
%Instance{}
|
||||
|> changeset(Map.put(changes, :host, host))
|
||||
|> Repo.insert()
|
||||
result =
|
||||
cond do
|
||||
is_nil(existing_record) ->
|
||||
%Instance{}
|
||||
|> changeset(Map.put(changes, :host, host))
|
||||
|> Repo.insert()
|
||||
|
||||
existing_record.unreachable_since &&
|
||||
NaiveDateTime.compare(existing_record.unreachable_since, unreachable_since) != :gt ->
|
||||
{:ok, existing_record}
|
||||
existing_record.unreachable_since &&
|
||||
NaiveDateTime.compare(existing_record.unreachable_since, unreachable_since) != :gt ->
|
||||
{:ok, existing_record}
|
||||
|
||||
true ->
|
||||
existing_record
|
||||
|> changeset(changes)
|
||||
|> Repo.update()
|
||||
end
|
||||
true ->
|
||||
existing_record
|
||||
|> changeset(changes)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :instance, :unreachable],
|
||||
%{count: 1},
|
||||
%{host: host, instance: existing_record}
|
||||
)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def set_unreachable(_, _), do: {:error, nil}
|
||||
|
|
|
@ -11,6 +11,8 @@ defmodule Pleroma.Stats do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
require Logger
|
||||
|
||||
@interval :timer.seconds(60)
|
||||
|
||||
def start_link(_) do
|
||||
|
@ -55,6 +57,14 @@ defmodule Pleroma.Stats do
|
|||
peers
|
||||
end
|
||||
|
||||
@doc "Returns Oban queue counts for available, executing, and retryable states"
|
||||
@spec get_oban() :: map()
|
||||
def get_oban do
|
||||
%{oban: oban_counts} = GenServer.call(__MODULE__, :get_state)
|
||||
|
||||
oban_counts
|
||||
end
|
||||
|
||||
@spec calculate_stat_data() :: %{
|
||||
peers: list(),
|
||||
stats: %{
|
||||
|
@ -63,6 +73,11 @@ defmodule Pleroma.Stats do
|
|||
user_count: non_neg_integer()
|
||||
}
|
||||
}
|
||||
@doc """
|
||||
Calculates stat data and exports some metrics through telemetry.
|
||||
|
||||
Executes automatically on a 60 second interval by default.
|
||||
"""
|
||||
def calculate_stat_data do
|
||||
peers =
|
||||
from(
|
||||
|
@ -87,6 +102,21 @@ defmodule Pleroma.Stats do
|
|||
|
||||
user_count = Repo.aggregate(users_query, :count, :id)
|
||||
|
||||
# Logs Oban counts
|
||||
get_oban_counts()
|
||||
|
||||
# Logs / Telemetry for local visibility counts
|
||||
get_status_visibility_count()
|
||||
|
||||
# Telemetry for local stats
|
||||
%{domains: domain_count, notes: status_count, users: user_count}
|
||||
|> Enum.each(fn {k, v} ->
|
||||
:telemetry.execute(
|
||||
[:pleroma, :stats, :local, k],
|
||||
%{count: v}
|
||||
)
|
||||
end)
|
||||
|
||||
%{
|
||||
peers: peers,
|
||||
stats: %{
|
||||
|
@ -97,13 +127,19 @@ defmodule Pleroma.Stats do
|
|||
}
|
||||
end
|
||||
|
||||
@spec get_status_visibility_count(String.t() | nil) :: map()
|
||||
def get_status_visibility_count(instance \\ nil) do
|
||||
if is_nil(instance) do
|
||||
CounterCache.get_sum()
|
||||
else
|
||||
CounterCache.get_by_instance(instance)
|
||||
end
|
||||
@spec get_status_visibility_count :: map()
|
||||
def get_status_visibility_count do
|
||||
result = CounterCache.get_sum()
|
||||
|
||||
Enum.each(result, fn {k, v} ->
|
||||
:telemetry.execute(
|
||||
[:pleroma, :stats, :global, :activities],
|
||||
%{count: v},
|
||||
%{visibility: k}
|
||||
)
|
||||
end)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
@ -134,4 +170,32 @@ defmodule Pleroma.Stats do
|
|||
Process.send_after(self(), :run_update, @interval)
|
||||
{:noreply, new_stats}
|
||||
end
|
||||
|
||||
def get_oban_counts do
|
||||
result =
|
||||
from(j in Oban.Job,
|
||||
where: j.state in ^["executing", "available", "retryable"],
|
||||
group_by: [j.queue, j.state],
|
||||
select: {j.queue, j.state, count(j.id)}
|
||||
)
|
||||
|> Pleroma.Repo.all()
|
||||
|> Enum.group_by(fn {key, _, _} -> key end)
|
||||
|> Enum.reduce(%{}, fn {k, v}, acc ->
|
||||
queues = Map.new(v, fn {_, state, count} -> {state, count} end)
|
||||
|
||||
Map.put(acc, k, queues)
|
||||
end)
|
||||
|
||||
for {queue, states} <- result do
|
||||
queue_text =
|
||||
for {state, count} <- states do
|
||||
"#{state}: #{count}"
|
||||
end
|
||||
|> Enum.join(" || ")
|
||||
|
||||
Logger.info("Oban Queue Stats for :#{queue} || #{queue_text}")
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
|
|
55
lib/pleroma/telemetry.ex
Normal file
55
lib/pleroma/telemetry.ex
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Telemetry do
|
||||
@phoenix_handlers %{
|
||||
[:phoenix, :endpoint, :start] => &Phoenix.Logger.phoenix_endpoint_start/4,
|
||||
[:phoenix, :endpoint, :stop] => &Phoenix.Logger.phoenix_endpoint_stop/4,
|
||||
[:phoenix, :router_dispatch, :start] => &Phoenix.Logger.phoenix_router_dispatch_start/4,
|
||||
[:phoenix, :error_rendered] => &Phoenix.Logger.phoenix_error_rendered/4,
|
||||
[:phoenix, :socket_connected] => &Phoenix.Logger.phoenix_socket_connected/4,
|
||||
[:phoenix, :channel_joined] => &Phoenix.Logger.phoenix_channel_joined/4,
|
||||
[:phoenix, :channel_handled_in] => &Phoenix.Logger.phoenix_channel_handled_in/4
|
||||
}
|
||||
|
||||
@live_view_handlers %{
|
||||
[:phoenix, :live_view, :mount, :start] => &Phoenix.LiveView.Logger.lv_mount_start/4,
|
||||
[:phoenix, :live_view, :mount, :stop] => &Phoenix.LiveView.Logger.lv_mount_stop/4,
|
||||
[:phoenix, :live_view, :handle_params, :start] =>
|
||||
&Phoenix.LiveView.Logger.lv_handle_params_start/4,
|
||||
[:phoenix, :live_view, :handle_params, :stop] =>
|
||||
&Phoenix.LiveView.Logger.lv_handle_params_stop/4,
|
||||
[:phoenix, :live_view, :handle_event, :start] =>
|
||||
&Phoenix.LiveView.Logger.lv_handle_event_start/4,
|
||||
[:phoenix, :live_view, :handle_event, :stop] =>
|
||||
&Phoenix.LiveView.Logger.lv_handle_event_stop/4,
|
||||
[:phoenix, :live_component, :handle_event, :start] =>
|
||||
&Phoenix.LiveView.Logger.lc_handle_event_start/4,
|
||||
[:phoenix, :live_component, :handle_event, :stop] =>
|
||||
&Phoenix.LiveView.Logger.lc_handle_event_stop/4
|
||||
}
|
||||
|
||||
def disable_phoenix_logs() do
|
||||
:telemetry.list_handlers([])
|
||||
|> Enum.filter(fn x ->
|
||||
match?(%{id: {Phoenix.Logger, _}}, x) or match?(%{id: {Phoenix.LiveView.Logger, _}}, x)
|
||||
end)
|
||||
|> Enum.map(& &1.id)
|
||||
|> Enum.each(&:telemetry.detach(&1))
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def enable_phoenix_logs() do
|
||||
for {key, fun} <- @phoenix_handlers do
|
||||
:telemetry.attach({Phoenix.Logger, key}, key, fun, :ok)
|
||||
end
|
||||
|
||||
for {key, fun} <- @live_view_handlers do
|
||||
:telemetry.attach({Phoenix.LiveView.Logger, key}, key, fun, :ok)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
|
@ -8,23 +8,55 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
require Logger
|
||||
|
||||
@events [
|
||||
[:oban, :job, :exception],
|
||||
[:pleroma, :activity, :mrf, :filter],
|
||||
[:pleroma, :activity, :mrf, :pass],
|
||||
[:pleroma, :activity, :mrf, :reject],
|
||||
[:pleroma, :activitypub, :inbox],
|
||||
[:pleroma, :activitypub, :publisher],
|
||||
[:pleroma, :instance, :reachable],
|
||||
[:pleroma, :instance, :unreachable],
|
||||
[:pleroma, :connection_pool, :client, :add],
|
||||
[:pleroma, :connection_pool, :client, :dead],
|
||||
[:pleroma, :connection_pool, :provision_failure],
|
||||
[:pleroma, :connection_pool, :reclaim, :start],
|
||||
[:pleroma, :connection_pool, :reclaim, :stop],
|
||||
[:pleroma, :connection_pool, :provision_failure],
|
||||
[:pleroma, :connection_pool, :client, :dead],
|
||||
[:pleroma, :connection_pool, :client, :add]
|
||||
[:pleroma, :upload, :success],
|
||||
[:pleroma, :user, :account, :confirm],
|
||||
[:pleroma, :user, :account, :password_reset],
|
||||
[:pleroma, :user, :account, :register],
|
||||
[:pleroma, :user, :o_auth, :failure],
|
||||
[:pleroma, :user, :o_auth, :revoke],
|
||||
[:pleroma, :user, :o_auth, :success],
|
||||
[:pleroma, :user, :create],
|
||||
[:pleroma, :user, :delete],
|
||||
[:pleroma, :user, :update],
|
||||
[:tesla, :request, :exception],
|
||||
[:tesla, :request, :stop]
|
||||
]
|
||||
def attach do
|
||||
:telemetry.attach_many("pleroma-logger", @events, &handle_event/4, [])
|
||||
def setup do
|
||||
if not Pleroma.Config.get([Pleroma.Telemetry, :phoenix_logs]) do
|
||||
Pleroma.Telemetry.disable_phoenix_logs()
|
||||
end
|
||||
|
||||
Enum.each(@events, fn event ->
|
||||
:telemetry.attach({__MODULE__, event}, event, &__MODULE__.handle_event/4, [])
|
||||
end)
|
||||
end
|
||||
|
||||
# Passing anonymous functions instead of strings to logger is intentional,
|
||||
# that way strings won't be concatenated if the message is going to be thrown
|
||||
# out anyway due to higher log level configured
|
||||
|
||||
def handle_event([:oban, :job, :exception], _measure, meta, _) do
|
||||
Logger.error(fn ->
|
||||
"[Oban] #{meta.worker} error: #{inspect(meta.error)} args: #{inspect(meta.args)}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :connection_pool, :reclaim, :start],
|
||||
_,
|
||||
_measurements,
|
||||
%{max_connections: max_connections, reclaim_max: reclaim_max},
|
||||
_
|
||||
) do
|
||||
|
@ -35,8 +67,8 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
|
||||
def handle_event(
|
||||
[:pleroma, :connection_pool, :reclaim, :stop],
|
||||
%{reclaimed_count: 0},
|
||||
_,
|
||||
%{count: 0},
|
||||
_measurements,
|
||||
_
|
||||
) do
|
||||
Logger.debug(fn ->
|
||||
|
@ -46,8 +78,8 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
|
||||
def handle_event(
|
||||
[:pleroma, :connection_pool, :reclaim, :stop],
|
||||
%{reclaimed_count: reclaimed_count},
|
||||
_,
|
||||
%{count: reclaimed_count},
|
||||
_measurements,
|
||||
_
|
||||
) do
|
||||
Logger.debug(fn -> "Connection pool cleaned up #{reclaimed_count} idle connections" end)
|
||||
|
@ -55,8 +87,8 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
|
||||
def handle_event(
|
||||
[:pleroma, :connection_pool, :provision_failure],
|
||||
_measurements,
|
||||
%{opts: [key | _]},
|
||||
_,
|
||||
_
|
||||
) do
|
||||
Logger.debug(fn ->
|
||||
|
@ -66,8 +98,8 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
|
||||
def handle_event(
|
||||
[:pleroma, :connection_pool, :client, :dead],
|
||||
%{client_pid: client_pid, reason: reason},
|
||||
%{key: key},
|
||||
_measurements,
|
||||
%{client_pid: client_pid, key: key, reason: reason},
|
||||
_
|
||||
) do
|
||||
Logger.debug(fn ->
|
||||
|
@ -77,8 +109,8 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
|
||||
def handle_event(
|
||||
[:pleroma, :connection_pool, :client, :add],
|
||||
%{clients: [_, _ | _] = clients},
|
||||
%{key: key, protocol: :http},
|
||||
_measurements,
|
||||
%{clients: [_, _ | _] = clients, key: key, protocol: :http},
|
||||
_
|
||||
) do
|
||||
Logger.debug(fn ->
|
||||
|
@ -87,4 +119,254 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
end
|
||||
|
||||
def handle_event([:pleroma, :connection_pool, :client, :add], _, _, _), do: :ok
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :activitypub, :inbox],
|
||||
_measurements,
|
||||
%{ap_id: ap_id, type: type},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"Inbox: received #{type} of #{ap_id}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :activity, :mrf, :pass],
|
||||
_measurements,
|
||||
%{activity: activity, mrf: mrf},
|
||||
_
|
||||
) do
|
||||
type = activity["type"]
|
||||
ap_id = activity["id"]
|
||||
|
||||
Logger.debug(fn ->
|
||||
"#{mrf}: passed #{inspect(type)} of #{inspect(ap_id)}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :activity, :mrf, :filter],
|
||||
_measurements,
|
||||
%{activity: activity, mrf: mrf},
|
||||
_
|
||||
) do
|
||||
type = activity["type"]
|
||||
ap_id = activity["id"]
|
||||
|
||||
Logger.debug(fn ->
|
||||
"#{mrf}: filtered #{inspect(type)} of #{inspect(ap_id)}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :activity, :mrf, :reject],
|
||||
_measurements,
|
||||
%{activity: activity, mrf: mrf, reason: reason},
|
||||
_
|
||||
) do
|
||||
type = activity["type"]
|
||||
ap_id = activity["id"]
|
||||
|
||||
Logger.info(fn ->
|
||||
"#{mrf}: rejected #{inspect(type)} of #{inspect(ap_id)}: #{inspect(reason)}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :upload, :success],
|
||||
%{size: size, duration: duration} = _measurements,
|
||||
%{nickname: nickname, filename: filename},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
upload_size = (size / 1_024_000) |> :erlang.float_to_binary(decimals: 2)
|
||||
seconds = duration / 1000
|
||||
"Upload: #{nickname} uploaded #{filename} [#{upload_size}MB in #{seconds}s]"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :create],
|
||||
_measurements,
|
||||
%{nickname: nickname},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"User: created #{nickname}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :delete],
|
||||
_measurements,
|
||||
%{nickname: nickname},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"User: deleted #{nickname}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :disable],
|
||||
_measurements,
|
||||
%{nickname: nickname},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"User: disabled #{nickname}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :update],
|
||||
_measurements,
|
||||
%{nickname: nickname},
|
||||
_
|
||||
) do
|
||||
Logger.debug(fn ->
|
||||
"User: updated #{nickname}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :instance, :reachable],
|
||||
_measurements,
|
||||
%{host: host, instance: instance},
|
||||
_
|
||||
) do
|
||||
unreachable_since = get_in(instance, [Access.key(:unreachable_since)]) || "UNDEFINED"
|
||||
|
||||
Logger.info(fn ->
|
||||
"Instance: #{host} is set reachable (was unreachable since: #{unreachable_since})"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :instance, :unreachable],
|
||||
_measurements,
|
||||
%{host: host, instance: _instance},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"Instance: #{host} is set unreachable"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :o_auth, :failure],
|
||||
_measurements,
|
||||
%{ip: ip, nickname: name},
|
||||
_
|
||||
) do
|
||||
Logger.error(fn ->
|
||||
"OAuth: authentication failure for #{name} from #{ip}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :o_auth, :revoke],
|
||||
_measurements,
|
||||
%{ip: ip, nickname: name},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"OAuth: token revoked for #{name} from #{ip}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :o_auth, :success],
|
||||
_measurements,
|
||||
%{ip: ip, nickname: name},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"OAuth: authentication success for #{name} from #{ip}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :account, :confirm],
|
||||
_measurements,
|
||||
%{user: user},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"Account: #{user.nickname} account confirmed"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :account, :password_reset],
|
||||
_measurements,
|
||||
%{for: for, ip: ip},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"Account: password reset requested for #{for} from #{ip}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :user, :account, :register],
|
||||
_measurements,
|
||||
%{ip: ip, user: user},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"Account: #{user.nickname} registered from #{ip}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :activitypub, :publisher],
|
||||
%{sum: sum},
|
||||
%{nickname: nickname, type: type},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"Publisher: delivering #{type} for #{nickname} to #{sum} inboxes"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event([:tesla, :request, :exception], _measure, meta, _) do
|
||||
Logger.error(fn -> "Pleroma.HTTP exception #{inspect(meta.stacktrace)}" end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:tesla, :request, :stop],
|
||||
_measurements,
|
||||
%{env: %Tesla.Env{method: method, url: url}, error: error} = _meta,
|
||||
_
|
||||
) do
|
||||
Logger.warning(fn ->
|
||||
"Pleroma.HTTP :#{method} failed for #{url} with error #{inspect(error)}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:tesla, :request, :stop],
|
||||
_measurements,
|
||||
%{env: %Tesla.Env{method: method, status: status, url: url}} = _meta,
|
||||
_
|
||||
) do
|
||||
cond do
|
||||
status in 200..299 ->
|
||||
:ok
|
||||
|
||||
true ->
|
||||
Logger.warning(fn -> "Pleroma.HTTP :#{method} status #{status} for #{url}" end)
|
||||
end
|
||||
end
|
||||
|
||||
# Catchall
|
||||
def handle_event(event, measurements, metadata, _) do
|
||||
Logger.debug(fn ->
|
||||
"Unhandled telemetry event #{inspect(event)} with measurements #{inspect(measurements)} and metadata #{inspect(metadata)}"
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
@ -87,7 +87,7 @@ defmodule Pleroma.Upload do
|
|||
end
|
||||
end
|
||||
|
||||
@spec store(source, options :: [option()]) :: {:ok, map()} | {:error, any()}
|
||||
@spec store(source, options :: [option()]) :: {:ok, Upload.t(), map()} | {:error, any()}
|
||||
@doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct."
|
||||
def store(upload, opts \\ []) do
|
||||
opts = get_opts(opts)
|
||||
|
@ -100,7 +100,7 @@ defmodule Pleroma.Upload do
|
|||
{:description_limit,
|
||||
String.length(description) <= Pleroma.Config.get([:instance, :description_limit])},
|
||||
{:ok, url_spec} <- Pleroma.Uploaders.Uploader.put_file(opts.uploader, upload) do
|
||||
{:ok,
|
||||
{:ok, upload,
|
||||
%{
|
||||
"id" => Utils.generate_object_id(),
|
||||
"type" => opts.activity_type,
|
||||
|
|
|
@ -986,7 +986,7 @@ defmodule Pleroma.User do
|
|||
|
||||
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
||||
def register(%Ecto.Changeset{} = changeset) do
|
||||
with {:ok, user} <- Repo.insert(changeset) do
|
||||
with {:ok, user} <- create(changeset) do
|
||||
post_register_action(user)
|
||||
end
|
||||
end
|
||||
|
@ -1276,6 +1276,12 @@ defmodule Pleroma.User do
|
|||
|> Oban.insert()
|
||||
end
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :update],
|
||||
%{count: 1},
|
||||
%{nickname: user.nickname, user: user}
|
||||
)
|
||||
|
||||
set_cache(user)
|
||||
end
|
||||
end
|
||||
|
@ -1952,6 +1958,12 @@ defmodule Pleroma.User do
|
|||
def confirm(%User{is_confirmed: false} = user) do
|
||||
with chg <- confirmation_changeset(user, set_confirmation: true),
|
||||
{:ok, user} <- update_and_set_cache(chg) do
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :account, :confirm],
|
||||
%{count: 1},
|
||||
%{user: user}
|
||||
)
|
||||
|
||||
post_register_action(user)
|
||||
{:ok, user}
|
||||
end
|
||||
|
@ -1983,6 +1995,20 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
@spec create(Ecto.Changeset.t()) :: {:ok, User.t()} | {:error, any()}
|
||||
def create(%Ecto.Changeset{} = changeset) do
|
||||
with {:ok, %User{} = user} <- Repo.insert(changeset),
|
||||
{:ok, user} <- set_cache(user) do
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :create],
|
||||
%{count: 1},
|
||||
%{nickname: user.nickname, user: user}
|
||||
)
|
||||
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
@spec purge_user_changeset(User.t()) :: Ecto.Changeset.t()
|
||||
def purge_user_changeset(user) do
|
||||
# "Right to be forgotten"
|
||||
|
@ -2040,6 +2066,12 @@ defmodule Pleroma.User do
|
|||
# Purge the user immediately
|
||||
purge(user)
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :delete],
|
||||
%{count: 1},
|
||||
%{nickname: user.nickname, user: user}
|
||||
)
|
||||
|
||||
DeleteWorker.new(%{"op" => "delete_user", "user_id" => user.id})
|
||||
|> Oban.insert()
|
||||
end
|
||||
|
@ -2275,8 +2307,7 @@ defmodule Pleroma.User do
|
|||
|> change
|
||||
|> put_private_key()
|
||||
|> unique_constraint(:nickname)
|
||||
|> Repo.insert()
|
||||
|> set_cache()
|
||||
|> create()
|
||||
end
|
||||
|
||||
def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do
|
||||
|
|
|
@ -12,7 +12,7 @@ defmodule Pleroma.User.Import do
|
|||
|
||||
require Logger
|
||||
|
||||
@spec perform(atom(), User.t(), String.t()) :: :ok | {:error, any()}
|
||||
@spec perform(atom(), User.t(), String.t()) :: {:ok, User.t()} | {:error, any()}
|
||||
def perform(:mute_import, %User{} = user, actor) do
|
||||
with {:ok, %User{} = muted_user} <- User.get_or_fetch(actor),
|
||||
{_, false} <- {:existing_mute, User.mutes_user?(user, muted_user)},
|
||||
|
|
|
@ -1539,8 +1539,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
|
||||
def upload(file, opts \\ []) do
|
||||
with {:ok, data} <- Upload.store(sanitize_upload_file(file), opts) do
|
||||
start_time = :erlang.monotonic_time(:millisecond)
|
||||
|
||||
with {:ok, upload, data} <- Upload.store(sanitize_upload_file(file), opts) do
|
||||
obj_data = Maps.put_if_present(data, "actor", opts[:actor])
|
||||
stop_time = :erlang.monotonic_time(:millisecond)
|
||||
|
||||
nickname =
|
||||
with true <- is_binary(opts[:actor]),
|
||||
%User{nickname: nickname} <- User.get_cached_by_ap_id(opts[:actor]) do
|
||||
nickname
|
||||
else
|
||||
_ -> "UNDEFINED"
|
||||
end
|
||||
|
||||
{:ok, %{size: upload_size}} = File.stat(upload.tempfile)
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :upload, :success],
|
||||
%{count: 1, duration: stop_time - start_time, size: upload_size},
|
||||
%{nickname: nickname, filename: upload.path}
|
||||
)
|
||||
|
||||
Repo.insert(%Object{data: obj_data})
|
||||
end
|
||||
|
@ -1860,8 +1879,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
data
|
||||
|> User.remote_user_changeset()
|
||||
|> Repo.insert()
|
||||
|> User.set_cache()
|
||||
|> User.create()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -52,7 +52,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
when action in [:activity, :object]
|
||||
)
|
||||
|
||||
plug(:log_inbox_metadata when action in [:inbox])
|
||||
plug(:inbox_telemetry when action in [:inbox])
|
||||
plug(:set_requester_reachable when action in [:inbox])
|
||||
plug(:relay_active? when action in [:relay])
|
||||
|
||||
|
@ -529,12 +529,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
conn
|
||||
end
|
||||
|
||||
defp log_inbox_metadata(%{params: %{"actor" => actor, "type" => type}} = conn, _) do
|
||||
Logger.metadata(actor: actor, type: type)
|
||||
defp inbox_telemetry(%{params: %{"actor" => actor, "id" => ap_id, "type" => type}} = conn, _) do
|
||||
actor_host = URI.parse(actor).host
|
||||
Logger.metadata(actor: actor, ap_id: ap_id, type: type, actor_host: actor_host)
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :activitypub, :inbox],
|
||||
%{count: 1},
|
||||
%{host: actor_host, ap_id: ap_id, type: type}
|
||||
)
|
||||
|
||||
conn
|
||||
end
|
||||
|
||||
defp log_inbox_metadata(conn, _), do: conn
|
||||
defp inbox_telemetry(conn, _), do: conn
|
||||
|
||||
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <-
|
||||
|
|
|
@ -306,6 +306,18 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
|> Instances.filter_reachable()
|
||||
end)
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :activitypub, :publisher],
|
||||
%{count: 1, sum: Enum.count(inboxes)},
|
||||
%{
|
||||
activity: activity,
|
||||
actor: actor,
|
||||
nickname: Map.get(actor, :nickname),
|
||||
inboxes: inboxes,
|
||||
type: Map.get(activity, :data)["type"]
|
||||
}
|
||||
)
|
||||
|
||||
Repo.checkout(fn ->
|
||||
Enum.each(inboxes, fn inboxes ->
|
||||
Enum.each(inboxes, fn {inbox, unreachable_since} ->
|
||||
|
@ -348,6 +360,18 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
|
||||
inboxes = inboxes -- priority_inboxes
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :activitypub, :publisher],
|
||||
%{count: 1, sum: Enum.count(inboxes ++ priority_inboxes)},
|
||||
%{
|
||||
activity: activity,
|
||||
actor: actor,
|
||||
nickname: Map.get(actor, :nickname),
|
||||
inboxes: inboxes ++ priority_inboxes,
|
||||
type: Map.get(activity, :data)["type"]
|
||||
}
|
||||
)
|
||||
|
||||
[{priority_inboxes, 0}, {inboxes, 1}]
|
||||
|> Enum.each(fn {inboxes, priority} ->
|
||||
inboxes
|
||||
|
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
alias Pleroma.Config
|
||||
alias Pleroma.MFA
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.Stats
|
||||
alias Pleroma.CounterCache
|
||||
alias Pleroma.User
|
||||
alias Pleroma.User.Backup
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
@ -423,7 +423,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
|
||||
def stats(conn, params) do
|
||||
counters = Stats.get_status_visibility_count(params["instance"])
|
||||
counters = CounterCache.get_by_instance(params["instance"])
|
||||
|
||||
json(conn, %{"status_visibility" => counters})
|
||||
end
|
||||
|
|
|
@ -20,9 +20,26 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
|||
def get_user(%Plug.Conn{} = conn) do
|
||||
with {:ldap, true} <- {:ldap, Pleroma.Config.get([:ldap, :enabled])},
|
||||
{:ok, {name, password}} <- fetch_credentials(conn),
|
||||
%User{} = user <- LDAP.bind_user(name, password) do
|
||||
{:checkpw, %User{} = user} <- {:checkpw, LDAP.bind_user(name, password)} do
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :o_auth, :success],
|
||||
%{count: 1},
|
||||
%{ip: :inet.ntoa(conn.remote_ip), nickname: name}
|
||||
)
|
||||
|
||||
{:ok, user}
|
||||
else
|
||||
{:checkpw, _} = e ->
|
||||
{:ok, {name, _password}} = fetch_credentials(conn)
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :o_auth, :failure],
|
||||
%{count: 1},
|
||||
%{ip: :inet.ntoa(conn.remote_ip), nickname: name}
|
||||
)
|
||||
|
||||
{:error, e}
|
||||
|
||||
{:ldap, _} ->
|
||||
@base.get_user(conn)
|
||||
|
||||
|
|
|
@ -18,10 +18,27 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
{_, %User{} = user} <- {:user, fetch_user(name)},
|
||||
{_, true} <- {:checkpw, AuthenticationPlug.checkpw(password, user.password_hash)},
|
||||
{:ok, user} <- AuthenticationPlug.maybe_update_password(user, password) do
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :o_auth, :success],
|
||||
%{count: 1},
|
||||
%{ip: :inet.ntoa(conn.remote_ip), nickname: name}
|
||||
)
|
||||
|
||||
{:ok, user}
|
||||
else
|
||||
{:error, _reason} = error -> error
|
||||
error -> {:error, error}
|
||||
{:checkpw, false} ->
|
||||
oauth_telemetry(conn)
|
||||
{:error, :invalid_password}
|
||||
|
||||
{:user, nil} ->
|
||||
oauth_telemetry(conn)
|
||||
{:error, :invalid_user}
|
||||
|
||||
{:error, _reason} = error ->
|
||||
error
|
||||
|
||||
error ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -121,4 +138,14 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
end
|
||||
|
||||
def change_password(_, _, _, _), do: {:error, :password_confirmation}
|
||||
|
||||
defp oauth_telemetry(conn) do
|
||||
{:ok, {name, _password}} = fetch_credentials(conn)
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :o_auth, :failure],
|
||||
%{count: 1},
|
||||
%{ip: :inet.ntoa(conn.remote_ip), nickname: name}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -136,7 +136,6 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
plug(Pleroma.Web.Plugs.TrailingFormatPlug)
|
||||
plug(Plug.RequestId)
|
||||
plug(Plug.Logger, log: :debug)
|
||||
|
||||
plug(Plug.Parsers,
|
||||
parsers: [:urlencoded, Pleroma.Web.Multipart, :json],
|
||||
|
|
|
@ -118,9 +118,9 @@ defmodule Pleroma.Web.Federator do
|
|||
Logger.debug("Already had #{params["id"]}")
|
||||
{:error, :already_present}
|
||||
|
||||
{:actor, e} ->
|
||||
{:actor, {:error, _} = e} ->
|
||||
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
|
||||
{:error, e}
|
||||
e
|
||||
|
||||
e ->
|
||||
# Just drop those for now
|
||||
|
|
|
@ -113,6 +113,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
with :ok <- validate_email_param(params),
|
||||
:ok <- TwitterAPI.validate_captcha(app, params),
|
||||
{:ok, user} <- TwitterAPI.register_user(params),
|
||||
:ok <-
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :account, :register],
|
||||
%{count: 1},
|
||||
%{ip: conn.remote_ip, nickname: user.nickname}
|
||||
),
|
||||
{_, {:ok, token}} <-
|
||||
{:login, OAuthController.login(user, app, app.scopes)} do
|
||||
OAuthController.after_token_exchange(conn, %{user: user, token: token})
|
||||
|
|
|
@ -17,6 +17,12 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
def password_reset(conn, params) do
|
||||
nickname_or_email = params["email"] || params["nickname"]
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :account, :password_reset],
|
||||
%{count: 1},
|
||||
%{for: nickname_or_email, ip: :inet.ntoa(conn.remote_ip)}
|
||||
)
|
||||
|
||||
TwitterAPI.password_reset(nickname_or_email)
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
|
|
70
lib/pleroma/web/metrics_storage.ex
Normal file
70
lib/pleroma/web/metrics_storage.ex
Normal file
|
@ -0,0 +1,70 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MetricsStorage do
|
||||
use GenServer
|
||||
|
||||
@history_buffer_size 50
|
||||
|
||||
def metrics_history(metric) do
|
||||
GenServer.call(__MODULE__, {:data, metric})
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(metrics) do
|
||||
Process.flag(:trap_exit, true)
|
||||
|
||||
metric_histories_map =
|
||||
metrics
|
||||
|> Enum.map(fn metric ->
|
||||
attach_handler(metric)
|
||||
{metric, CircularBuffer.new(@history_buffer_size)}
|
||||
end)
|
||||
|> Map.new()
|
||||
|
||||
{:ok, metric_histories_map}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def terminate(_, metrics) do
|
||||
for metric <- metrics do
|
||||
:telemetry.detach({__MODULE__, metric, self()})
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp attach_handler(%{event_name: name_list} = metric) do
|
||||
:telemetry.attach(
|
||||
{__MODULE__, metric, self()},
|
||||
name_list,
|
||||
&__MODULE__.handle_event/4,
|
||||
metric
|
||||
)
|
||||
end
|
||||
|
||||
def handle_event(_event_name, data, metadata, metric) do
|
||||
if data = Phoenix.LiveDashboard.extract_datapoint_for_metric(metric, data, metadata) do
|
||||
GenServer.cast(__MODULE__, {:telemetry_metric, data, metric})
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast({:telemetry_metric, data, metric}, state) do
|
||||
{:noreply, update_in(state[metric], &CircularBuffer.insert(&1, data))}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:data, metric}, _from, state) do
|
||||
if history = state[metric] do
|
||||
{:reply, CircularBuffer.to_list(history), state}
|
||||
else
|
||||
{:reply, [], state}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -381,6 +381,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
def token_revoke(%Plug.Conn{} = conn, %{"token" => token}) do
|
||||
with {:ok, %Token{} = oauth_token} <- Token.get_by_token(token),
|
||||
{:ok, oauth_token} <- RevokeToken.revoke(oauth_token) do
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :o_auth, :revoke],
|
||||
%{count: 1},
|
||||
%{ip: :inet.ntoa(conn.remote_ip), nickname: oauth_token.user.nickname}
|
||||
)
|
||||
|
||||
conn =
|
||||
with session_token = AuthHelper.get_session_token(conn),
|
||||
%Token{token: ^session_token} <- oauth_token do
|
||||
|
|
|
@ -36,6 +36,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
def get_by_token(token) do
|
||||
token
|
||||
|> Query.get_by_token()
|
||||
|> Query.preload([:user])
|
||||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
|
@ -44,6 +45,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
def get_by_token(%App{id: app_id} = _app, token) do
|
||||
Query.get_by_app(app_id)
|
||||
|> Query.get_by_token(token)
|
||||
|> Query.preload([:user])
|
||||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
|
|
|
@ -996,7 +996,12 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
scope "/" do
|
||||
pipe_through([:pleroma_html, :authenticate, :require_admin])
|
||||
live_dashboard("/phoenix/live_dashboard", additional_pages: [oban: Oban.LiveDashboard])
|
||||
|
||||
live_dashboard("/phoenix/live_dashboard",
|
||||
metrics: Pleroma.Web.Telemetry,
|
||||
metrics_history: {Pleroma.Web.MetricsStorage, :metrics_history, []},
|
||||
additional_pages: [oban: Oban.LiveDashboard]
|
||||
)
|
||||
end
|
||||
|
||||
# Test-only routes needed to test action dispatching and plug chain execution
|
||||
|
|
201
lib/pleroma/web/telemetry.ex
Normal file
201
lib/pleroma/web/telemetry.ex
Normal file
|
@ -0,0 +1,201 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Telemetry do
|
||||
use Supervisor
|
||||
import Telemetry.Metrics
|
||||
|
||||
def start_link(arg) do
|
||||
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(_arg) do
|
||||
children = [
|
||||
# Telemetry poller will execute the given period measurements
|
||||
# every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
|
||||
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
|
||||
# Add reporters as children of your supervision tree.
|
||||
# {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
|
||||
]
|
||||
|
||||
Supervisor.init(children, strategy: :one_for_one)
|
||||
end
|
||||
|
||||
def metrics do
|
||||
[
|
||||
# Phoenix Metrics
|
||||
summary("phoenix.endpoint.start.system_time",
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.endpoint.stop.duration",
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.router_dispatch.start.system_time",
|
||||
tags: [:route],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.router_dispatch.exception.duration",
|
||||
tags: [:route],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.router_dispatch.stop.duration",
|
||||
tags: [:route],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.socket_connected.duration",
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.channel_joined.duration",
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.channel_handled_in.duration",
|
||||
tags: [:event],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
|
||||
# Database Metrics
|
||||
summary("pleroma.repo.query.total_time",
|
||||
reporter_options: [nav: "Repo"],
|
||||
unit: {:native, :millisecond},
|
||||
description: "The sum of the other measurements"
|
||||
),
|
||||
summary("pleroma.repo.query.decode_time",
|
||||
reporter_options: [nav: "Repo"],
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent decoding the data received from the database"
|
||||
),
|
||||
summary("pleroma.repo.query.query_time",
|
||||
reporter_options: [nav: "Repo"],
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent executing the query"
|
||||
),
|
||||
summary("pleroma.repo.query.queue_time",
|
||||
reporter_options: [nav: "Repo"],
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent waiting for a database connection"
|
||||
),
|
||||
summary("pleroma.repo.query.idle_time",
|
||||
reporter_options: [nav: "Repo"],
|
||||
unit: {:native, :millisecond},
|
||||
description:
|
||||
"The time the connection spent waiting before being checked out for the query"
|
||||
),
|
||||
|
||||
# VM Metrics
|
||||
summary("vm.memory.total", unit: {:byte, :megabyte}),
|
||||
summary("vm.total_run_queue_lengths.total"),
|
||||
summary("vm.total_run_queue_lengths.cpu"),
|
||||
summary("vm.total_run_queue_lengths.io"),
|
||||
|
||||
# Pleroma Metrics
|
||||
# ActivityPub
|
||||
sum("pleroma.activitypub.inbox.count",
|
||||
tags: [:type],
|
||||
reporter_options: [nav: "ActivityPub"],
|
||||
description: "Sum of activities received by type"
|
||||
),
|
||||
sum("pleroma.activitypub.publisher.sum",
|
||||
tags: [:nickname],
|
||||
reporter_options: [nav: "ActivityPub"],
|
||||
description: "Sum of published activities by actor"
|
||||
),
|
||||
sum("pleroma.activitypub.publisher.sum",
|
||||
tags: [:type],
|
||||
reporter_options: [nav: "ActivityPub"],
|
||||
description: "Sum of published activities by type"
|
||||
),
|
||||
counter("pleroma.activitypub.publisher.count",
|
||||
tags: [:nickname],
|
||||
reporter_options: [nav: "ActivityPub"],
|
||||
description: "Counter of publishing events by activity actor"
|
||||
),
|
||||
counter("pleroma.activitypub.publisher.count",
|
||||
tags: [:type],
|
||||
reporter_options: [nav: "ActivityPub"],
|
||||
description: "Counter of publishing events by activity type"
|
||||
),
|
||||
|
||||
# OAuth
|
||||
sum("pleroma.user.o_auth.success.count",
|
||||
tags: [:nickname],
|
||||
reporter_options: [nav: "OAuth"],
|
||||
description: "Sum of successful login attempts by user"
|
||||
),
|
||||
# this graph is at risk of high cardinality as even incorrect
|
||||
# usernames are passed through as the nickname
|
||||
sum("pleroma.user.o_auth.failure.count",
|
||||
tags: [:nickname],
|
||||
reporter_options: [nav: "OAuth"],
|
||||
description: "Sum of failed login attempts by user"
|
||||
),
|
||||
|
||||
# Gun
|
||||
last_value("pleroma.connection_pool.worker.count",
|
||||
reporter_options: [nav: "Gun"],
|
||||
description: "Number of connection pool workers"
|
||||
),
|
||||
counter("pleroma.connection_pool.client.add.count",
|
||||
reporter_options: [nav: "Gun"],
|
||||
description: "Counter of new connection events"
|
||||
),
|
||||
counter("pleroma.connection_pool.client.dead.count",
|
||||
reporter_options: [nav: "Gun"],
|
||||
description: "Counter of dead connection events"
|
||||
),
|
||||
counter("pleroma.connection_pool.provision_failure.count",
|
||||
reporter_options: [nav: "Gun"],
|
||||
description: "Counter of connection provisioning failure events"
|
||||
),
|
||||
counter("pleroma.connection_pool.reclaim.stop.count",
|
||||
reporter_options: [nav: "Gun"],
|
||||
description: "Counter of idle connection reclaimation events"
|
||||
),
|
||||
|
||||
# Uploads
|
||||
last_value("pleroma.upload.success.size",
|
||||
reporter_options: [nav: "Uploads"],
|
||||
description: "Size of media uploads in bytes",
|
||||
unit: {:byte, :megabyte}
|
||||
),
|
||||
counter("pleroma.upload.success.count",
|
||||
reporter_options: [nav: "Uploads"],
|
||||
description: "Count of media uploads by nickname",
|
||||
tags: [:nickname]
|
||||
),
|
||||
summary("pleroma.upload.success.duration",
|
||||
reporter_options: [nav: "Uploads"],
|
||||
description: "Time spent receiving and processing uploads",
|
||||
unit: {:millisecond, :second}
|
||||
),
|
||||
|
||||
# Stats
|
||||
last_value("pleroma.stats.global.activities.count",
|
||||
reporter_options: [nav: "Stats"],
|
||||
description: "Total number of known activities per visibility type",
|
||||
tags: [:visibility]
|
||||
),
|
||||
last_value("pleroma.stats.local.domains.count",
|
||||
reporter_options: [nav: "Stats"],
|
||||
description: "Total number of known domains"
|
||||
),
|
||||
last_value("pleroma.stats.local.notes.count",
|
||||
reporter_options: [nav: "Stats"],
|
||||
description: "Total number of notes by local users"
|
||||
),
|
||||
last_value("pleroma.stats.local.users.count",
|
||||
reporter_options: [nav: "Stats"],
|
||||
description: "Total number of local user accounts"
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
defp periodic_measurements do
|
||||
[
|
||||
# A module, function and arguments to be invoked periodically.
|
||||
# This function must call :telemetry.execute/3 and a metric must be added above.
|
||||
# {Pleroma.Web, :count_users, []}
|
||||
]
|
||||
end
|
||||
end
|
|
@ -24,6 +24,12 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
|||
def request(conn, params) do
|
||||
nickname_or_email = params["email"] || params["nickname"]
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :user, :account, :password_reset],
|
||||
%{count: 1},
|
||||
%{for: nickname_or_email, ip: :inet.ntoa(conn.remote_ip)}
|
||||
)
|
||||
|
||||
TwitterAPI.password_reset(nickname_or_email)
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
|
|
|
@ -9,7 +9,11 @@ defmodule Pleroma.Workers.UserRefreshWorker do
|
|||
|
||||
@impl true
|
||||
def perform(%Job{args: %{"ap_id" => ap_id}}) do
|
||||
User.fetch_by_ap_id(ap_id)
|
||||
case User.fetch_by_ap_id(ap_id) do
|
||||
{:error, :not_found} -> {:cancel, :not_found}
|
||||
{:error, :forbidden} -> {:cancel, :forbidden}
|
||||
result -> result
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
5
mix.exs
5
mix.exs
|
@ -207,6 +207,8 @@ defmodule Pleroma.Mixfile do
|
|||
{:oban_live_dashboard, "~> 0.1.1"},
|
||||
{:multipart, "~> 0.4.0", optional: true},
|
||||
{:argon2_elixir, "~> 4.0"},
|
||||
{:ring_logger, "~> 0.11.3"},
|
||||
{:circular_buffer, "~> 0.4.0"},
|
||||
|
||||
## dev & test
|
||||
{:phoenix_live_reload, "~> 1.3.3", only: :dev},
|
||||
|
@ -220,7 +222,8 @@ defmodule Pleroma.Mixfile do
|
|||
{:mox, "~> 1.0", only: :test},
|
||||
{:websockex, "~> 0.4.3", only: :test},
|
||||
{:benchee, "~> 1.0", only: :benchmark},
|
||||
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}
|
||||
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
|
||||
{:telemetry_test, "~> 0.1.0", only: :test}
|
||||
] ++ oauth_deps() ++ logger_deps()
|
||||
end
|
||||
|
||||
|
|
3
mix.lock
3
mix.lock
|
@ -14,6 +14,7 @@
|
|||
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
|
||||
"cc_precompiler": {:hex, :cc_precompiler, "0.1.9", "e8d3364f310da6ce6463c3dd20cf90ae7bbecbf6c5203b98bf9b48035592649b", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9dcab3d0f3038621f1601f13539e7a9ee99843862e66ad62827b0c42b2f58a54"},
|
||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||
"circular_buffer": {:hex, :circular_buffer, "0.4.1", "477f370fd8cfe1787b0a1bade6208bbd274b34f1610e41f1180ba756a7679839", [:mix], [], "hexpm", "633ef2e059dde0d7b89bbab13b1da9d04c6685e80e68fbdf41282d4fae746b72"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
"comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"},
|
||||
"concurrent_limiter": {:hex, :concurrent_limiter, "0.1.1", "43ae1dc23edda1ab03dd66febc739c4ff710d047bb4d735754909f9a474ae01c", [:mix], [{:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "53968ff238c0fbb4d7ed76ddb1af0be6f3b2f77909f6796e249e737c505a16eb"},
|
||||
|
@ -127,6 +128,7 @@
|
|||
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
||||
"recon": {:hex, :recon, "2.5.4", "05dd52a119ee4059fa9daa1ab7ce81bc7a8161a2f12e9d42e9d551ffd2ba901c", [:mix, :rebar3], [], "hexpm", "e9ab01ac7fc8572e41eb59385efeb3fb0ff5bf02103816535bacaedf327d0263"},
|
||||
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},
|
||||
"ring_logger": {:hex, :ring_logger, "0.11.3", "08a423e5d088d5bf41bf1bdf23b804f96279624ef09ca6b5b5012c32014b38a8", [:mix], [{:circular_buffer, "~> 0.4.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}], "hexpm", "b870a23b8f8329aeadcbee3429e5e43942e4134d810a71e9a5a2f0567b9ce78d"},
|
||||
"rustler": {:hex, :rustler, "0.30.0", "cefc49922132b072853fa9b0ca4dc2ffcb452f68fb73b779042b02d545e097fb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "9ef1abb6a7dda35c47cfc649e6a5a61663af6cf842a55814a554a84607dee389"},
|
||||
"sleeplocks": {:hex, :sleeplocks, "1.1.2", "d45aa1c5513da48c888715e3381211c859af34bee9b8290490e10c90bb6ff0ca", [:rebar3], [], "hexpm", "9fe5d048c5b781d6305c1a3a0f40bb3dfc06f49bf40571f3d2d0c57eaa7f59a5"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
|
||||
|
@ -139,6 +141,7 @@
|
|||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
||||
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.0", "b583c3f18508f5c5561b674d16cf5d9afd2ea3c04505b7d92baaeac93c1b8260", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "9cba950e1c4733468efbe3f821841f34ac05d28e7af7798622f88ecdbbe63ea3"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
||||
"telemetry_test": {:hex, :telemetry_test, "0.1.2", "122d927567c563cf57773105fa8104ae4299718ec2cbdddcf6776562c7488072", [:mix], [{:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7bd41a49ecfd33ecd82d2c7edae19a5736f0d2150206d0ee290dcf3885d0e14d"},
|
||||
"tesla": {:hex, :tesla, "1.11.0", "81b2b10213dddb27105ec6102d9eb0cc93d7097a918a0b1594f2dfd1a4601190", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "b83ab5d4c2d202e1ea2b7e17a49f788d49a699513d7c4f08f2aef2c281be69db"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"},
|
||||
"timex": {:hex, :timex, "3.7.7", "3ed093cae596a410759104d878ad7b38e78b7c2151c6190340835515d4a46b8a", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "0ec4b09f25fe311321f9fc04144a7e3affe48eb29481d7a5583849b6c4dfa0a7"},
|
||||
|
|
113
test/pleroma/counter_cache_test.exs
Normal file
113
test/pleroma/counter_cache_test.exs
Normal file
|
@ -0,0 +1,113 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.CounterCacheTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.CounterCache
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
@local_instance Pleroma.Web.Endpoint.url() |> String.split("//") |> Enum.at(1)
|
||||
|
||||
describe "status visibility sum count" do
|
||||
test "on new status" do
|
||||
instance2 = "instance2.tld"
|
||||
user = insert(:user)
|
||||
other_user = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
|
||||
|
||||
CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
|
||||
Enum.each(0..1, fn _ ->
|
||||
CommonAPI.post(user, %{
|
||||
visibility: "unlisted",
|
||||
status: "hey"
|
||||
})
|
||||
end)
|
||||
|
||||
Enum.each(0..2, fn _ ->
|
||||
CommonAPI.post(user, %{
|
||||
visibility: "direct",
|
||||
status: "hey @#{other_user.nickname}"
|
||||
})
|
||||
end)
|
||||
|
||||
Enum.each(0..3, fn _ ->
|
||||
CommonAPI.post(user, %{
|
||||
visibility: "private",
|
||||
status: "hey"
|
||||
})
|
||||
end)
|
||||
|
||||
assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} =
|
||||
CounterCache.get_by_instance(@local_instance)
|
||||
end
|
||||
|
||||
test "on status delete" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
assert %{"public" => 1} = CounterCache.get_by_instance(@local_instance)
|
||||
CommonAPI.delete(activity.id, user)
|
||||
assert %{"public" => 0} = CounterCache.get_by_instance(@local_instance)
|
||||
end
|
||||
|
||||
test "on status visibility update" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
assert %{"public" => 1, "private" => 0} = CounterCache.get_by_instance(@local_instance)
|
||||
{:ok, _} = CommonAPI.update_activity_scope(activity.id, %{visibility: "private"})
|
||||
assert %{"public" => 0, "private" => 1} = CounterCache.get_by_instance(@local_instance)
|
||||
end
|
||||
|
||||
test "doesn't count unrelated activities" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
_ = CommonAPI.follow(other_user, user)
|
||||
CommonAPI.favorite(activity.id, other_user)
|
||||
CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 0} =
|
||||
CounterCache.get_by_instance(@local_instance)
|
||||
end
|
||||
end
|
||||
|
||||
describe "status visibility by instance count" do
|
||||
test "single instance" do
|
||||
instance2 = "instance2.tld"
|
||||
user1 = insert(:user)
|
||||
user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
|
||||
|
||||
CommonAPI.post(user1, %{visibility: "public", status: "hey"})
|
||||
|
||||
Enum.each(1..5, fn _ ->
|
||||
CommonAPI.post(user1, %{
|
||||
visibility: "unlisted",
|
||||
status: "hey"
|
||||
})
|
||||
end)
|
||||
|
||||
Enum.each(1..10, fn _ ->
|
||||
CommonAPI.post(user1, %{
|
||||
visibility: "direct",
|
||||
status: "hey @#{user2.nickname}"
|
||||
})
|
||||
end)
|
||||
|
||||
Enum.each(1..20, fn _ ->
|
||||
CommonAPI.post(user2, %{
|
||||
visibility: "private",
|
||||
status: "hey"
|
||||
})
|
||||
end)
|
||||
|
||||
assert %{"direct" => 10, "private" => 0, "public" => 1, "unlisted" => 5} =
|
||||
CounterCache.get_by_instance(@local_instance)
|
||||
|
||||
assert %{"direct" => 0, "private" => 20, "public" => 0, "unlisted" => 0} =
|
||||
CounterCache.get_by_instance(instance2)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,13 +2,12 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.StatsTest do
|
||||
defmodule Pleroma.CounterCacheTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Stats
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
describe "user count" do
|
||||
test "it ignores internal users" do
|
||||
|
@ -19,104 +18,4 @@ defmodule Pleroma.StatsTest do
|
|||
assert match?(%{stats: %{user_count: 1}}, Stats.calculate_stat_data())
|
||||
end
|
||||
end
|
||||
|
||||
describe "status visibility sum count" do
|
||||
test "on new status" do
|
||||
instance2 = "instance2.tld"
|
||||
user = insert(:user)
|
||||
other_user = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
|
||||
|
||||
CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
|
||||
Enum.each(0..1, fn _ ->
|
||||
CommonAPI.post(user, %{
|
||||
visibility: "unlisted",
|
||||
status: "hey"
|
||||
})
|
||||
end)
|
||||
|
||||
Enum.each(0..2, fn _ ->
|
||||
CommonAPI.post(user, %{
|
||||
visibility: "direct",
|
||||
status: "hey @#{other_user.nickname}"
|
||||
})
|
||||
end)
|
||||
|
||||
Enum.each(0..3, fn _ ->
|
||||
CommonAPI.post(user, %{
|
||||
visibility: "private",
|
||||
status: "hey"
|
||||
})
|
||||
end)
|
||||
|
||||
assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} =
|
||||
Stats.get_status_visibility_count()
|
||||
end
|
||||
|
||||
test "on status delete" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
assert %{"public" => 1} = Stats.get_status_visibility_count()
|
||||
CommonAPI.delete(activity.id, user)
|
||||
assert %{"public" => 0} = Stats.get_status_visibility_count()
|
||||
end
|
||||
|
||||
test "on status visibility update" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
assert %{"public" => 1, "private" => 0} = Stats.get_status_visibility_count()
|
||||
{:ok, _} = CommonAPI.update_activity_scope(activity.id, %{visibility: "private"})
|
||||
assert %{"public" => 0, "private" => 1} = Stats.get_status_visibility_count()
|
||||
end
|
||||
|
||||
test "doesn't count unrelated activities" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
_ = CommonAPI.follow(other_user, user)
|
||||
CommonAPI.favorite(activity.id, other_user)
|
||||
CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 0} =
|
||||
Stats.get_status_visibility_count()
|
||||
end
|
||||
end
|
||||
|
||||
describe "status visibility by instance count" do
|
||||
test "single instance" do
|
||||
local_instance = Pleroma.Web.Endpoint.url() |> String.split("//") |> Enum.at(1)
|
||||
instance2 = "instance2.tld"
|
||||
user1 = insert(:user)
|
||||
user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
|
||||
|
||||
CommonAPI.post(user1, %{visibility: "public", status: "hey"})
|
||||
|
||||
Enum.each(1..5, fn _ ->
|
||||
CommonAPI.post(user1, %{
|
||||
visibility: "unlisted",
|
||||
status: "hey"
|
||||
})
|
||||
end)
|
||||
|
||||
Enum.each(1..10, fn _ ->
|
||||
CommonAPI.post(user1, %{
|
||||
visibility: "direct",
|
||||
status: "hey @#{user2.nickname}"
|
||||
})
|
||||
end)
|
||||
|
||||
Enum.each(1..20, fn _ ->
|
||||
CommonAPI.post(user2, %{
|
||||
visibility: "private",
|
||||
status: "hey"
|
||||
})
|
||||
end)
|
||||
|
||||
assert %{"direct" => 10, "private" => 0, "public" => 1, "unlisted" => 5} =
|
||||
Stats.get_status_visibility_count(local_instance)
|
||||
|
||||
assert %{"direct" => 0, "private" => 20, "public" => 0, "unlisted" => 0} =
|
||||
Stats.get_status_visibility_count(instance2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,7 +58,7 @@ defmodule Pleroma.UploadTest do
|
|||
test "it returns file" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
assert {:ok, result} = Upload.store(@upload_file)
|
||||
assert {:ok, %Upload{}, result} = Upload.store(@upload_file)
|
||||
|
||||
assert result ==
|
||||
%{
|
||||
|
@ -140,7 +140,7 @@ defmodule Pleroma.UploadTest do
|
|||
filename: "image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
{:ok, %Upload{}, data} = Upload.store(file)
|
||||
|
||||
assert %{"url" => [%{"href" => url}]} = data
|
||||
|
||||
|
@ -159,7 +159,7 @@ defmodule Pleroma.UploadTest do
|
|||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe])
|
||||
{:ok, %Upload{}, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe])
|
||||
|
||||
assert List.first(data["url"])["href"] ==
|
||||
Path.join([Pleroma.Upload.base_url(), expected_path])
|
||||
|
@ -174,7 +174,7 @@ defmodule Pleroma.UploadTest do
|
|||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
{:ok, %Upload{}, data} = Upload.store(file)
|
||||
assert data["name"] == "an [image.jpg"
|
||||
end
|
||||
|
||||
|
@ -183,7 +183,7 @@ defmodule Pleroma.UploadTest do
|
|||
img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(params)
|
||||
{:ok, %Upload{}, data} = Upload.store(params)
|
||||
assert hd(data["url"])["mediaType"] == "image/jpeg"
|
||||
end
|
||||
|
||||
|
@ -194,7 +194,7 @@ defmodule Pleroma.UploadTest do
|
|||
img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(params)
|
||||
{:ok, %Upload{}, data} = Upload.store(params)
|
||||
assert String.ends_with?(data["name"], ".jpg")
|
||||
end
|
||||
|
||||
|
@ -207,7 +207,8 @@ defmodule Pleroma.UploadTest do
|
|||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.AnonymizeFilename])
|
||||
{:ok, %Upload{}, data} =
|
||||
Upload.store(file, filters: [Pleroma.Upload.Filter.AnonymizeFilename])
|
||||
|
||||
refute data["name"] == "an [image.jpg"
|
||||
end
|
||||
|
@ -221,7 +222,7 @@ defmodule Pleroma.UploadTest do
|
|||
filename: "an… image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
{:ok, %Upload{}, data} = Upload.store(file)
|
||||
[attachment_url | _] = data["url"]
|
||||
|
||||
assert Path.basename(attachment_url["href"]) == "an%E2%80%A6%20image.jpg"
|
||||
|
@ -236,7 +237,7 @@ defmodule Pleroma.UploadTest do
|
|||
filename: ":?#[]@!$&\\'()*+,;=.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
{:ok, %Upload{}, data} = Upload.store(file)
|
||||
[attachment_url | _] = data["url"]
|
||||
|
||||
assert Path.basename(attachment_url["href"]) ==
|
||||
|
@ -260,7 +261,7 @@ defmodule Pleroma.UploadTest do
|
|||
filename: "image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file, base_url: base_url)
|
||||
{:ok, %Upload{}, data} = Upload.store(file, base_url: base_url)
|
||||
|
||||
assert %{"url" => [%{"href" => url}]} = data
|
||||
|
||||
|
|
|
@ -1036,13 +1036,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
test "status visibility count", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
instance = Pleroma.Web.Endpoint.url() |> String.split("//") |> Enum.at(1)
|
||||
CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
|
||||
CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/pleroma/admin/stats")
|
||||
|> get("/api/pleroma/admin/stats", %{"instance" => instance})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
|
||||
|
|
|
@ -37,7 +37,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do
|
|||
params = %{"authorization" => %{"name" => name, "password" => "password"}}
|
||||
res = PleromaAuthenticator.get_user(%Plug.Conn{params: params})
|
||||
|
||||
assert {:error, {:checkpw, false}} == res
|
||||
assert {:error, :invalid_password} == res
|
||||
end
|
||||
|
||||
test "get_user/grant_type_password", %{user: user, name: name, password: password} do
|
||||
|
|
|
@ -22,6 +22,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
|||
|
||||
import Mox
|
||||
import Pleroma.Factory
|
||||
import TelemetryTest
|
||||
|
||||
setup [:telemetry_listen]
|
||||
|
||||
setup do: clear_config([:instance, :federating])
|
||||
setup do: clear_config([:instance, :allow_relay])
|
||||
|
@ -200,6 +203,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
|||
|> json_response_and_validate_schema(422)
|
||||
end
|
||||
|
||||
@tag telemetry_listen: [:pleroma, :upload, :success]
|
||||
test "posting an undefined status with an attachment", %{user: user, conn: conn} do
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
|
@ -209,6 +213,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
|||
|
||||
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
|
||||
|
||||
nickname = user.nickname
|
||||
|
||||
assert_receive {:telemetry_event,
|
||||
%{
|
||||
event: [:pleroma, :upload, :success],
|
||||
measurements: %{count: 1, duration: _, size: _},
|
||||
metadata: %{filename: _, nickname: ^nickname}
|
||||
}}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do
|
|||
filename: "nice_tf.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
{:ok, %Upload{}, data} = Upload.store(file)
|
||||
[%{"href" => attachment_url} | _] = data["url"]
|
||||
[attachment_url: attachment_url]
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue