Skip to content

Commit

Permalink
Do not crash when Plug is not added to project dependencies
Browse files Browse the repository at this point in the history
Closes #118
  • Loading branch information
AndrewDryga committed May 21, 2024
1 parent be34c26 commit 09f8164
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 101 deletions.
6 changes: 3 additions & 3 deletions lib/logger_json/formatters/basic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule LoggerJSON.Formatters.Basic do
}
"""
import Jason.Helpers, only: [json_map: 1]
import LoggerJSON.Formatter.{MapBuilder, DateTime, Message, Metadata, Plug, RedactorEncoder}
import LoggerJSON.Formatter.{MapBuilder, DateTime, Message, Metadata, RedactorEncoder}

@behaviour LoggerJSON.Formatter

Expand Down Expand Up @@ -83,8 +83,8 @@ defmodule LoggerJSON.Formatters.Basic do
),
client:
json_map(
user_agent: get_header(conn, "user-agent"),
ip: remote_ip(conn)
user_agent: LoggerJSON.Formatter.Plug.get_header(conn, "user-agent"),
ip: LoggerJSON.Formatter.Plug.remote_ip(conn)
)
)
end
Expand Down
8 changes: 4 additions & 4 deletions lib/logger_json/formatters/datadog.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ defmodule LoggerJSON.Formatters.Datadog do
}
"""
import Jason.Helpers, only: [json_map: 1]
import LoggerJSON.Formatter.{MapBuilder, DateTime, Message, Metadata, Code, Plug, RedactorEncoder}
import LoggerJSON.Formatter.{MapBuilder, DateTime, Message, Metadata, Code, RedactorEncoder}

@behaviour LoggerJSON.Formatter

Expand Down Expand Up @@ -197,9 +197,9 @@ defmodule LoggerJSON.Formatters.Datadog do
if Code.ensure_loaded?(Plug.Conn) do
defp format_http_request(%{conn: %Plug.Conn{} = conn} = meta) do
request_url = Plug.Conn.request_url(conn)
user_agent = get_header(conn, "user-agent")
remote_ip = remote_ip(conn)
referer = get_header(conn, "referer")
user_agent = LoggerJSON.Formatter.Plug.get_header(conn, "user-agent")
remote_ip = LoggerJSON.Formatter.Plug.remote_ip(conn)
referer = LoggerJSON.Formatter.Plug.get_header(conn, "referer")

%{
http:
Expand Down
8 changes: 4 additions & 4 deletions lib/logger_json/formatters/elastic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ defmodule LoggerJSON.Formatters.Elastic do
```
"""
import Jason.Helpers, only: [json_map: 1]
import LoggerJSON.Formatter.{MapBuilder, DateTime, Message, Metadata, Plug, RedactorEncoder}
import LoggerJSON.Formatter.{MapBuilder, DateTime, Message, Metadata, RedactorEncoder}

@behaviour LoggerJSON.Formatter

Expand Down Expand Up @@ -230,13 +230,13 @@ defmodule LoggerJSON.Formatters.Elastic do
# - user_agent.original: https://www.elastic.co/guide/en/ecs/8.11/ecs-user_agent.html
defp format_http_request(%{conn: %Plug.Conn{} = conn}) do
json_map(
"client.ip": remote_ip(conn),
"client.ip": LoggerJSON.Formatter.Plug.remote_ip(conn),
"http.version": Plug.Conn.get_http_protocol(conn),
"http.request.method": conn.method,
"http.request.referrer": get_header(conn, "referer"),
"http.request.referrer": LoggerJSON.Formatter.Plug.get_header(conn, "referer"),
"http.response.status_code": conn.status,
"url.path": conn.request_path,
"user_agent.original": get_header(conn, "user-agent")
"user_agent.original": LoggerJSON.Formatter.Plug.get_header(conn, "user-agent")
)
end
end
Expand Down
8 changes: 4 additions & 4 deletions lib/logger_json/formatters/google_cloud.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ defmodule LoggerJSON.Formatters.GoogleCloud do
}
"""
import Jason.Helpers, only: [json_map: 1]
import LoggerJSON.Formatter.{MapBuilder, DateTime, Message, Metadata, Code, Plug, RedactorEncoder}
import LoggerJSON.Formatter.{MapBuilder, DateTime, Message, Metadata, Code, RedactorEncoder}

@behaviour LoggerJSON.Formatter

Expand Down Expand Up @@ -224,9 +224,9 @@ defmodule LoggerJSON.Formatters.GoogleCloud do
request_method = conn.method |> to_string() |> String.upcase()
request_url = Plug.Conn.request_url(conn)
status = conn.status
user_agent = get_header(conn, "user-agent")
remote_ip = remote_ip(conn)
referer = get_header(conn, "referer")
user_agent = LoggerJSON.Formatter.Plug.get_header(conn, "user-agent")
remote_ip = LoggerJSON.Formatter.Plug.remote_ip(conn)
referer = LoggerJSON.Formatter.Plug.get_header(conn, "referer")

json_map(
protocol: Plug.Conn.get_http_protocol(conn),
Expand Down
172 changes: 87 additions & 85 deletions lib/logger_json/plug.ex
Original file line number Diff line number Diff line change
@@ -1,107 +1,109 @@
defmodule LoggerJSON.Plug do
@moduledoc """
A telemetry handler that logs request information in JSON format.
if Code.ensure_loaded?(Plug) do
defmodule LoggerJSON.Plug do
@moduledoc """
A telemetry handler that logs request information in JSON format.
This module is not recommended to be used in production, as it can be
costly to log every single database query.
"""
require Logger
This module is not recommended to be used in production, as it can be
costly to log every single database query.
"""
require Logger

@doc """
Attaches the telemetry handler to the given event.
@doc """
Attaches the telemetry handler to the given event.
### Available options
### Available options
* `:level` - log level which is used to log requests. Defaults to `:info`.
* `:level` - log level which is used to log requests. Defaults to `:info`.
### Dynamic log level
### Dynamic log level
In some cases you may wish to set the log level dynamically
on a per-query basis. To do so, set the `:level` option to
a tuple, `{Mod, Fun, Args}`. The query and map of time measures
will be prepended to the provided list of arguments.
In some cases you may wish to set the log level dynamically
on a per-query basis. To do so, set the `:level` option to
a tuple, `{Mod, Fun, Args}`. The query and map of time measures
will be prepended to the provided list of arguments.
When invoked, your function must return a
[`Logger.level()`](`t:Logger.level()/0`) or `false` to
disable logging for the request.
When invoked, your function must return a
[`Logger.level()`](`t:Logger.level()/0`) or `false` to
disable logging for the request.
### Examples
### Examples
Attaching the telemetry handler to the `MyApp.Repo` events with the `:info` log level:
Attaching the telemetry handler to the `MyApp.Repo` events with the `:info` log level:
# in the endpoint
plug Plug.Telemetry, event_prefix: [:myapp, :plug]
# in the endpoint
plug Plug.Telemetry, event_prefix: [:myapp, :plug]
# in your application.ex
LoggerJSON.Plug.attach("logger-json-requests", [:myapp, :plug, :stop], :info)
# in your application.ex
LoggerJSON.Plug.attach("logger-json-requests", [:myapp, :plug, :stop], :info)
To make plug broadcast those events see [`Plug.Telemetry`](https://hexdocs.pm/plug/Plug.Telemetry.html) documentation.
To make plug broadcast those events see [`Plug.Telemetry`](https://hexdocs.pm/plug/Plug.Telemetry.html) documentation.
You can also attach to the `[:phoenix, :endpoint, :stop]` event to log request latency from Phoenix endpoints:
You can also attach to the `[:phoenix, :endpoint, :stop]` event to log request latency from Phoenix endpoints:
LoggerJSON.Plug.attach("logger-json-phoenix-requests", [:phoenix, :endpoint, :stop], :info)
"""
def attach(name, event, level) do
:telemetry.attach(name, event, &__MODULE__.telemetry_logging_handler/4, level)
end
LoggerJSON.Plug.attach("logger-json-phoenix-requests", [:phoenix, :endpoint, :stop], :info)
"""
def attach(name, event, level) do
:telemetry.attach(name, event, &__MODULE__.telemetry_logging_handler/4, level)
end

@doc """
A telemetry handler that logs requests in a structured format.
"""
@spec telemetry_logging_handler(
event_name :: [atom()],
query_time :: %{duration: non_neg_integer()},
metadata :: %{conn: Plug.Conn.t()},
level :: Logger.level() | {module :: module(), function :: atom(), arguments :: [term()]} | false
) :: :ok
def telemetry_logging_handler(_event_name, %{duration: duration}, %{conn: conn}, level) do
duration = System.convert_time_unit(duration, :native, :microsecond)

if level = level(level, conn) do
Logger.log(
level,
fn ->
%{
method: method,
request_path: request_path,
state: state,
status: status
} = conn

[
method,
?\s,
request_path,
?\s,
"[",
connection_type(state),
?\s,
status(status),
"in ",
duration(duration),
"]"
]
end,
conn: conn,
duration_μs: duration
)
@doc """
A telemetry handler that logs requests in a structured format.
"""
@spec telemetry_logging_handler(
event_name :: [atom()],
query_time :: %{duration: non_neg_integer()},
metadata :: %{conn: Plug.Conn.t()},
level :: Logger.level() | {module :: module(), function :: atom(), arguments :: [term()]} | false
) :: :ok
def telemetry_logging_handler(_event_name, %{duration: duration}, %{conn: conn}, level) do
duration = System.convert_time_unit(duration, :native, :microsecond)

if level = level(level, conn) do
Logger.log(
level,
fn ->
%{
method: method,
request_path: request_path,
state: state,
status: status
} = conn

[
method,
?\s,
request_path,
?\s,
"[",
connection_type(state),
?\s,
status(status),
"in ",
duration(duration),
"]"
]
end,
conn: conn,
duration_μs: duration
)
end
end
end

defp connection_type(:set_chunked), do: "Chunked"
defp connection_type(_), do: "Sent"
defp connection_type(:set_chunked), do: "Chunked"
defp connection_type(_), do: "Sent"

defp status(nil), do: ""
defp status(status), do: [status |> Plug.Conn.Status.code() |> Integer.to_string(), ?\s]
defp status(nil), do: ""
defp status(status), do: [status |> Plug.Conn.Status.code() |> Integer.to_string(), ?\s]

def duration(duration) do
if duration > 1000 do
[duration |> div(1000) |> Integer.to_string(), "ms"]
else
[Integer.to_string(duration), "µs"]
def duration(duration) do
if duration > 1000 do
[duration |> div(1000) |> Integer.to_string(), "ms"]
else
[Integer.to_string(duration), "µs"]
end
end
end

defp level({m, f, a}, conn), do: apply(m, f, [conn | a])
defp level(level, _conn) when is_atom(level), do: level
defp level({m, f, a}, conn), do: apply(m, f, [conn | a])
defp level(level, _conn) when is_atom(level), do: level
end
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule LoggerJSON.Mixfile do
use Mix.Project

@source_url "https://github.com/Nebo15/logger_json"
@version "6.0.0"
@version "6.0.1"

def project do
[
Expand Down

0 comments on commit 09f8164

Please sign in to comment.