Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 62: Wrote the first version of the snap-server-0.10.0.0 release notes. #40

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions blogdata/content/2015/06/12/snap-server-0.10.0.0-released.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
| title: Announcing: Snap Server v0.10.0.0
| author: Robert Massaioli <[email protected]>
| published: 2015-06-12T06:10:00+1000
| updated: 2015-06-12T06:10:00+1000
| summary: Release notes for Snap Server v0.10.0.0

The snap team is happy to announce the release of version 0.10.0.0 of snap-server.

## Changes

This release of snap-server only contains one major change from 0.9.5.0 and that is the addition of
custom logging.

### Custom Access and Error logging for Snap Server

In this release of snap-server we include two new configuration options aimed at letting you control
the logging format of snap-server.

Currently snap-server access logs appear in a format that resembles a common Apache httpd log format. For example,
it should be familiar to see a log line like the following from your snap application:

127.0.0.1 - - [10/Jun/2015:14:12:55 +1000] "POST /rest/round-rooms HTTP/1.1" 200 - "http://localhost:8080/panel/hackathon-round-transition?project_id=10000&project_key=SP" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36"

And the error log is logged in a similar, but not quite the same, format and looks like:

[04/Dec/2014:11:11:57 +1100] Server.httpServe: START, binding to [http://0.0.0.0:8080/]

With the latest release of snap-server you are now free to define your own custom logging format! If
you choose to not define you own custom format then the access and error logs will continue to
appear in the same format.

### Custom Access Logging (a working example)

To create you own custom access and error logging formats snap-server provides two new configuration
options setAccessLogHandler and setErrorLogHandler respectively. These methods will let you control
how the access and error log lines are rendered. Lets run through a quick example with the
setAccessLogHandler method. But first lets look at the types! The first type we will look at is:

setAccessLogHandler :: AccessLogHandler -> Config m a -> Config m a

This method accepts an AccessLogHandler and updates your snap-server configuration to use it. But
what is an AccessLogHandler? An AccessLogHandler lets you get contextual information from the
current request / response and output access log lines as a result and thus is defined as the
following simple type alias:

type AccessLogHandler = Request -> Response -> IO ByteString

As you can see the AccessLogHandler is passed the Snap Request and Response so that you can log
whatever you like in a context sensitive manner. Lets now show a working example of actually using
these new types to log in JSON format using the [Aeson][1] library. First lets define a datatype that
will represent the JSON data that should appear on each line of our log file:

-- import qualified Data.Text as T
data AccessLogLine = AccessLogLine
{ allEvent :: String
, allTimestamp :: UTCTime
, allHost :: T.Text
, allMethod :: T.Text
, allUrl :: T.Text
, allHttpVersion :: T.Text
, allStatus :: Int
, allResponseContentLength :: Maybe Int64
, allReferer :: Maybe T.Text
, allUserAgent :: Maybe T.Text
} deriving (Show, Generic)

As you can see this definition contains a lot of information about the request and response. We can
now define a ToJSON instance for this data type so that we can encode it easily. We will use the
derived Generic instance and a few helper methods to define the ToJSON instance:

instance ToJSON AccessLogLine where
toJSON = genericToJSON defaultOptions
{ fieldLabelModifier = stripFieldNamePrefix "all"
}

stripFieldNamePrefix :: String -> String -> String
stripFieldNamePrefix pre = lowerFirst . try (L.stripPrefix pre)

lowerFirst :: String -> String
lowerFirst (x : xs) = C.toLower x : xs
lowerFirst [] = []

Now it is a simple matter of defining the AccessLogHandler itself which will take the request and
response, put the relevant data into an AccessLogLine and then return the encoded version of the
AccessLogLine in JSON format:

-- import qualified Data.Text as T
-- import qualified Data.Text.Encoding as T
-- import qualified Snap.Core as SC
-- import qualified Snap.Types.Headers as H
accessLogHandler :: AccessLogHandler
accessLogHandler req rsp = do
let hdrs = SC.headers req
let host = SC.rqRemoteAddr req
let (v, v') = SC.rqVersion req
let referer = head <$> H.lookup "referer" hdrs
let userAgent = head <$> H.lookup "user-agent" hdrs

currentTime <- getCurrentTime
return . L.toStrict . encode $ AccessLogLine
{ allEvent = "log.access"
, allTimestamp = currentTime
, allHost = t host
, allMethod = tshow . SC.rqMethod $ req
, allUrl = t . SC.rqURI $ req
, allHttpVersion = T.concat [ tshow v, ".", tshow v' ]
, allStatus = SC.rspStatus rsp
, allResponseContentLength = rspContentLength rsp
, allReferer = t <$> referer
, allUserAgent = t <$> userAgent
}
where
tshow :: Show a => a -> T.Text
tshow = T.pack . show
t = T.decodeUtf8

As you can see there is nothing overly complicated going on inside our accessLogHandler but now we
can use the following code to update our snap-server configuration:

setAccessLogHandler accessLogHandler

And now the access logs for our snap application running on snap-server are being output in JSON
format! You are now free to reuse this code in your own applications or to define your own custom
formats. Please let us know what formats you were able to define that were the most useful to you.

We hope that this change allows for better integration with third party tools or simply allows you to
extract the information that you need from snap-server. Good luck and keep writing awesome web
apps in snap!

[1]: http://hackage.haskell.org/package/aeson