diff --git a/config/config.exs b/config/config.exs
index 07e98011d..6fe292b16 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -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"
diff --git a/config/prod.exs b/config/prod.exs
index 2d252bf02..625f7db9c 100644
--- a/config/prod.exs
+++ b/config/prod.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
diff --git a/lib/mix/tasks/pleroma/uploads.ex b/lib/mix/tasks/pleroma/uploads.ex
index bf02912fa..c9b7c2d4a 100644
--- a/lib/mix/tasks/pleroma/uploads.ex
+++ b/lib/mix/tasks/pleroma/uploads.ex
@@ -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
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 3f199c002..5bdaef5c7 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -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,
diff --git a/lib/pleroma/gun/connection_pool/reclaimer.ex b/lib/pleroma/gun/connection_pool/reclaimer.ex
index 3580d38f5..d02f7c61c 100644
--- a/lib/pleroma/gun/connection_pool/reclaimer.ex
+++ b/lib/pleroma/gun/connection_pool/reclaimer.ex
@@ -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}
)
diff --git a/lib/pleroma/gun/connection_pool/worker.ex b/lib/pleroma/gun/connection_pool/worker.ex
index 38527ec1d..beaf54bb5 100644
--- a/lib/pleroma/gun/connection_pool/worker.ex
+++ b/lib/pleroma/gun/connection_pool/worker.ex
@@ -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
diff --git a/lib/pleroma/gun/connection_pool/worker_supervisor.ex b/lib/pleroma/gun/connection_pool/worker_supervisor.ex
index b9dedf61e..1dcb8302c 100644
--- a/lib/pleroma/gun/connection_pool/worker_supervisor.ex
+++ b/lib/pleroma/gun/connection_pool/worker_supervisor.ex
@@ -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 ->
diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex
index c11317850..50d963b72 100644
--- a/lib/pleroma/http.ex
+++ b/lib/pleroma/http.ex
@@ -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
diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex
index 33f1229d0..199d3812f 100644
--- a/lib/pleroma/instances/instance.ex
+++ b/lib/pleroma/instances/instance.ex
@@ -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}
diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex
index 47b30b951..58e607057 100644
--- a/lib/pleroma/stats.ex
+++ b/lib/pleroma/stats.ex
@@ -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
diff --git a/lib/pleroma/telemetry.ex b/lib/pleroma/telemetry.ex
new file mode 100644
index 000000000..942516e79
--- /dev/null
+++ b/lib/pleroma/telemetry.ex
@@ -0,0 +1,55 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors
+# 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
diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex
index 31ce3cc20..24ec2e591 100644
--- a/lib/pleroma/telemetry/logger.ex
+++ b/lib/pleroma/telemetry/logger.ex
@@ -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
diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex
index b0aef2592..cd52f7a1a 100644
--- a/lib/pleroma/upload.ex
+++ b/lib/pleroma/upload.ex
@@ -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,
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index d9da9ede1..cf2443f7d 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -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
diff --git a/lib/pleroma/user/import.ex b/lib/pleroma/user/import.ex
index ab6bdb8d4..7626c0c94 100644
--- a/lib/pleroma/user/import.ex
+++ b/lib/pleroma/user/import.ex
@@ -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)},
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 62c7a7b31..24b2b3537 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -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
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 7ac0bbab4..5135c86b6 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -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} <-
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index 0de3a0d43..b76d37e71 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -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
diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
index 0f22dd538..aabbebefe 100644
--- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
@@ -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
diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex
index ec6601fb9..230cbdb14 100644
--- a/lib/pleroma/web/auth/ldap_authenticator.ex
+++ b/lib/pleroma/web/auth/ldap_authenticator.ex
@@ -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)
diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex
index 0da3f19fc..7bdc13776 100644
--- a/lib/pleroma/web/auth/pleroma_authenticator.ex
+++ b/lib/pleroma/web/auth/pleroma_authenticator.ex
@@ -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
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index bab3c9fd0..76163c647 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -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],
diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex
index 58260afa8..ff72ead1b 100644
--- a/lib/pleroma/web/federator.ex
+++ b/lib/pleroma/web/federator.ex
@@ -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
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index 68157b0c4..21d353117 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -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})
diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
index fbb54a171..0aba01dc8 100644
--- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
@@ -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, "")
diff --git a/lib/pleroma/web/metrics_storage.ex b/lib/pleroma/web/metrics_storage.ex
new file mode 100644
index 000000000..4113495a6
--- /dev/null
+++ b/lib/pleroma/web/metrics_storage.ex
@@ -0,0 +1,70 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors
+# 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
diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex
index 0b3de5481..3f0f44391 100644
--- a/lib/pleroma/web/o_auth/o_auth_controller.ex
+++ b/lib/pleroma/web/o_auth/o_auth_controller.ex
@@ -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
diff --git a/lib/pleroma/web/o_auth/token.ex b/lib/pleroma/web/o_auth/token.ex
index d96425094..3f5e38a6c 100644
--- a/lib/pleroma/web/o_auth/token.ex
+++ b/lib/pleroma/web/o_auth/token.ex
@@ -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
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index ca76427ac..14002436b 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -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
diff --git a/lib/pleroma/web/telemetry.ex b/lib/pleroma/web/telemetry.ex
new file mode 100644
index 000000000..de2cb10bc
--- /dev/null
+++ b/lib/pleroma/web/telemetry.ex
@@ -0,0 +1,201 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors
+# 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
diff --git a/lib/pleroma/web/twitter_api/controllers/password_controller.ex b/lib/pleroma/web/twitter_api/controllers/password_controller.ex
index e5482de9d..8f735893d 100644
--- a/lib/pleroma/web/twitter_api/controllers/password_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/password_controller.ex
@@ -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, "")
diff --git a/lib/pleroma/workers/user_refresh_worker.ex b/lib/pleroma/workers/user_refresh_worker.ex
index ee276774b..881a522e2 100644
--- a/lib/pleroma/workers/user_refresh_worker.ex
+++ b/lib/pleroma/workers/user_refresh_worker.ex
@@ -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
diff --git a/mix.exs b/mix.exs
index d8b7c1e2f..07c4079c2 100644
--- a/mix.exs
+++ b/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
diff --git a/mix.lock b/mix.lock
index 9b53ede62..599e260d1 100644
--- a/mix.lock
+++ b/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"},
diff --git a/test/pleroma/counter_cache_test.exs b/test/pleroma/counter_cache_test.exs
new file mode 100644
index 000000000..90e287f35
--- /dev/null
+++ b/test/pleroma/counter_cache_test.exs
@@ -0,0 +1,113 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors
+# 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
diff --git a/test/pleroma/stats_test.exs b/test/pleroma/stats_test.exs
index c70603ab9..e446a0452 100644
--- a/test/pleroma/stats_test.exs
+++ b/test/pleroma/stats_test.exs
@@ -2,13 +2,12 @@
# Copyright © 2017-2022 Pleroma Authors
# 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
diff --git a/test/pleroma/upload_test.exs b/test/pleroma/upload_test.exs
index 5fd62fa43..2f808cc2b 100644
--- a/test/pleroma/upload_test.exs
+++ b/test/pleroma/upload_test.exs
@@ -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
diff --git a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs
index 6614d1409..faa164008 100644
--- a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs
+++ b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs
@@ -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} =
diff --git a/test/pleroma/web/auth/pleroma_authenticator_test.exs b/test/pleroma/web/auth/pleroma_authenticator_test.exs
index 7f6d63ff3..b8e20b9c9 100644
--- a/test/pleroma/web/auth/pleroma_authenticator_test.exs
+++ b/test/pleroma/web/auth/pleroma_authenticator_test.exs
@@ -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
diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
index a4bca6cf9..f77a6beab 100644
--- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
@@ -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")
diff --git a/test/pleroma/web/plugs/uploaded_media_plug_test.exs b/test/pleroma/web/plugs/uploaded_media_plug_test.exs
index 6a9366e28..fad847fc2 100644
--- a/test/pleroma/web/plugs/uploaded_media_plug_test.exs
+++ b/test/pleroma/web/plugs/uploaded_media_plug_test.exs
@@ -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