Current section
Files
Jump to
Current section
Files
lib/plug/accesslog/formatter.ex
defmodule Plug.AccessLog.Formatter do
@moduledoc """
Log message formatter.
"""
import Plug.Conn
@doc """
Formats a log message.
If no `format` is given a default will be used.
The following format values are supported:
- `%b` - Size of response in bytes
- `%h` - Remote IP of the client
- `%r` - First line of HTTP request
- `%>s` - Response status code
For now the http version written to the log message
will always be "HTTP/1.1", regardless of the true http version.
"""
@spec format(format :: atom | String.t, conn :: Plug.Conn.t) :: String.t
def format(nil, conn), do: format(:default, conn)
def format(:default, conn) do
"%h \"%r\" %>s %b" |> format(conn)
end
def format(format, conn) when is_binary(format) do
format(format, conn, "")
end
# Internal construction methods
defp format(<< "%b", rest :: binary >>, conn, message) do
content_length = case get_resp_header(conn, "Content-Length") do
[ length ] -> length
_ -> (conn.resp_body || "") |> byte_size()
end
if 0 == content_length do
content_length = "-"
end
format(rest, conn, message <> to_string(content_length))
end
defp format(<< "%h", rest :: binary >>, conn, message) do
remote_ip = conn.remote_ip |> :inet_parse.ntoa() |> to_string()
format(rest, conn, message <> remote_ip)
end
defp format(<< "%r", rest :: binary >>, conn, message) do
request = conn.method <> " " <> full_path(conn) <> " HTTP/1.1"
format(rest, conn, message <> request)
end
defp format(<< "%>s", rest :: binary >>, conn, message) do
status = conn.status |> to_string()
format(rest, conn, message <> status)
end
defp format(<< char, rest :: binary >>, conn, message) do
format(rest, conn, message <> << char >>)
end
defp format("", _conn, message), do: message
end