Plug.AccessLog

Plug for writing access logs.

Setup

To use the plug in your projects, edit your mix.exs file and add the project as a dependency:

defp deps do
  [ { :plug_accesslog, "~> 0.8" } ]
end

You should also update your applications to start the plug:

def application do
  [ applications: [ :plug_accesslog ] ]
end

Usage

The easiest way to use the plug is to add it to your existing router:

defmodule AppRouter do
  use Plug.Router

  plug Plug.AccessLog,
    format: :clf,
    file: "/path/to/your/logs/access.log"

  plug :match
  plug :dispatch

  get "/hello" do
    send_resp(conn, 200, "world")
  end

  match _ do
    send_resp(conn, 404, "oops")
  end
end

Note: The usage examples apply to a usecase where your are using plug directly without any framework. Using the plug Plug.AccessLog line in a framework based on plug should be no problem. Please refer to your frameworks individual documentation or source to find a suitable place.

Custom Formatters

If you want to extend the formatting capabilities or replace existing ones you can define a custom formatter pipeline to use:

defmodule CustomFormatter do
  @behaviour Plug.AccessLog.Formatter

  def format(format, conn) do
    # manipulate to your liking
    format
  end
end

defmodule Router do
  use Plug.Router

  plug Plug.AccessLog,
    format: :clf,
    formatters: [ CustomFormatter, Plug.AccessLog.DefaultFormatter ],
    file: "/path/to/your/logs/access.log"
end

If you do not configure a list of formatters only the DefaultFormatter will be used. If you define an empty list then no formatting will take place.

All formatters are called in the order they are defined in.

Do Not Log Filter

To filter the requests before logging you can configure a “do not log” filter function:

defmodule LogFilter do
  def dontlog?(conn), do: "/favicon.ico" == full_path(conn)
end

defmodule Router do
  use Plug.Router

  plug Plug.AccessLog,
    dontlog: &LogFilter.dontlog?/1,
    format: :clf,
    file: "/path/to/your/logs/access.log"
end

If the function you pass to the plug returns true the request will not be logged.

Logging Functions

To have the parsed log message sent to a logging function instead of writing it to a file you can configure a logging function:

defmodule InfoLogger do
  def log(msg), do: Logger.log(:info, msg)
end

defmodule Router do
  use Plug.Router

  plug Plug.AccessLog,
    format: :clf,
    fun: &InfoLogger.log/1
end

If a logging function is configured the configured file (if any) will be ignored.

Log Format

The default format is CLF.

Available formats

Besides a self defined format you can use one of the predefined aliases:

:agent
> %{User-Agent}i
> curl/7.35.0

:clf
> %h %l %u %t "%r" %>s %b
> 127.0.0.1 - - [10/Jan/2015:14:46:18 +0100] "GET / HTTP/1.1" 200 31337

:clf_vhost
> %v %h %l %u %t "%r" %>s %b
> www.example.com 127.0.0.1 - - [10/Jan/2015:14:46:18 +0100] "GET / HTTP/1.1" 200 31337

:combined
> %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"
> 127.0.0.1 - - [22/Jan/2015:19:33:58 +0100] "GET / HTTP/1.1" 200 2 "http://www.example.com/previous_page" "curl/7.35.0"

:combined_vhost
> %v %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"
> www.example.com 127.0.0.1 - - [22/Jan/2015:19:33:58 +0100] "GET / HTTP/1.1" 200 2 "http://www.example.com/previous_page" "curl/7.35.0"

:referer
> %{Referer}i -> %U
> http://www.example.com/previous_page -> /

Formatting directives

The following formatting directives are available:

Note for %b and %B: To determine the size of the response the “Content-Length” will be inspected and, if available, returned unverified. If the header is not present the response body will be inspected using byte_size/1.

Note for %h: The hostname will always be the ip of the client (same as %a).

Note for %l: Always a dash (“-“).

Note for %r: For now the http version is always logged as “HTTP/1.1”, regardless of the true http version.

Note for %T: Rounding happens, so “0.6 seconds” will be reported as “1 second”.

Note for %V: Alias for %v.

License

Apache License, Version 2.0