Hello World Suave

This post describes how to write a small HTTP service with logging and metrics in F# to run on linux, using Xamarin Studio and officially available nugets.

Getting Started

First pull down Xamarin Studio and click ‘New Solution…’:

New Solution

Select new ‘F# Console Application’.

F# New Console App

type out the name and press OK.

Prepare the git repo and build environment with albacore, by opening up your terminal. First ensure you have albacore properly installed. Now you can init the new solution, before we dive into code, with albacore init:

What it looks like in iTerm2 when initialising the folder

It’s now time to init git: git init:

State after git init

Before doing anything else with the sorrounding repository structure, let’s start installing our dependencies and write some sweet F#.

Add NuGet

Search for Suave and tick the box for pre-release software (at the time of writing my logging framework v2 is in beta-state).

Prerelease tickbox

Search Suave

NuGet selections

Do the selection shown above (Suave and Logary.Adapters.Suave) and press ‘Add Packages’. Choosing Nugets

Now you can add the Hello World-code:

open Suave
open Suave.Http.Successful
open Suave.Web

let main argv =
  web_server default_config (OK "Hello World!")

And press CMD+Enter to run the sample.


Adding Logging

The final piece of the code puzzle is to enable Logary logging. First add the required opens:

open Logary
open Logary.Configuration
open Logary.Targets

open Logary is used to open the assembly’s root namespace, which gives access to modules such as Logger - i.e. to use the consumer APIs of Logary.

The Configuration namespace lets you configure/bootstrap the targets, metrics and rules. It’s required for starting Logary.

The Targets namespace lets you construct targets - we’re constructing the most basic of targets, the console appender.

Logary also has an adapter for Suave to extract and ship its internal logs:

open Logary.Suave

Now, let’s construct the logging infrastructure:

use logary =
  withLogary' "HelloWorldSuave" (
    // a new allow-all rule for 'console' with a 'console' target
    withRule (Rule.createForTarget "console")
    >> withTarget (Console.create Console.empty "console")

The use takes care of properly shutting down the library when the current function main goes out of scope.

withLogary' is a helper function that lets you pass the ‘service name’ and configuration function (LogaryConf -> LogaryConf, second arg).

Since we don’t need a reference to the value LogaryConf we make do with function composition >> between two functions whose signatures end with LogaryConf -> LogaryConf, which is also the signature of the function we need to give withLogary'’s second argument.

The brackets is to show the compiler that the function composition should be done before giving the value to withLogary'.

We can log that the application has started:

let logger = logary.GetLogger("HelloWorldSuave.main")
Logger.debug logger "Starting Web Server"

You can also inject the Logary adapter into Suave:

  { default_config with logger = SuaveAdapter(logger) }
  (OK "Hello World!")

Now you can press CMD+Enter again, and see the output:

D 2014-11-19T10:36:10.9695370+00:00: logary/target/cons pre-start Status: Shutdown "" [FSharp.Actor]
D 2014-11-19T10:36:10.9974710+00:00: logary/target/cons started Status: Running [FSharp.Actor]
D 2014-11-19T10:36:11.0336540+00:00: logary/target/console pre-start Status: Shutdown "" [FSharp.Actor]
D 2014-11-19T10:36:11.0339720+00:00: logary/target/console started Status: Running [FSharp.Actor]
D 2014-11-19T10:36:11.0364090+00:00: logary/supervisor pre-start Status: Shutdown "" [FSharp.Actor]
D 2014-11-19T10:36:11.0368700+00:00: logary/supervisor started Status: Running [FSharp.Actor]
D 2014-11-19T10:36:11.0718940+00:00: logary/scheduler pre-start Status: Shutdown "" [FSharp.Actor]
D 2014-11-19T10:36:11.0724480+00:00: logary/scheduler started Status: Running [FSharp.Actor]
D 2014-11-19T10:36:11.0743710+00:00: logary/registry pre-start Status: Shutdown "" [FSharp.Actor]
D 2014-11-19T10:36:11.0746580+00:00: logary/registry started Status: Running [FSharp.Actor]
D 2014-11-19T10:36:13.5074250+00:00: Starting Web Server [HelloWorldSuave.main]
V 2014-11-19T10:36:13.6001440+00:00: initialising BufferManager with 827392 bytes [Suave.Socket.BufferManager]
I 2014-11-19T10:36:13.6313830+00:00: started listener in: started in 29.644000 ms: [Suave.Tcp.tcp_ip_server]

Adding a build environment

Previously we initialized Albacore. Let’s finish up what we started:

mkdir -p tools
curl -L -o tools/NuGet.exe https://nuget.org/NuGet.exe
echo packages/ >>.gitignore
sed -i '' 's/src[/]MyProj.sln/HelloWorldSuave.sln/g' Rakefile
sed -i '' 's/src[/]packages/packages/g' Rakefile
semver inc minor

Remove the nugets_pack task and change the :default task to point to :build instead.

Now we can try to build it:

bundle exec rake

With output:

##teamcity[buildNumber '0']
##teamcity[setParameter name='build.version' value='']
##teamcity[setParameter name='build.version.formal' value='0.1.0']
mono tools/NuGet.exe install packages.config -OutputDirectory packages
xbuild /verbosity:minimal HelloWorldSuave.sln
XBuild Engine Version 12.0
Mono, Version
Copyright (C) 2005-2013 Various Mono authors
    Configuration: Debug Platform: x86

A final touch

Since this is a web service, we should package it. Currently albacore supports packaging for deployment on Linux and on Windows, with a simple task-type appspec. Add this to the Rakefile:

desc 'package it as a service'
appspecs :package => [:build, 'build/pkg'] do |as|
  as.files = %w|.appspec|
  as.out   = 'build/pkg'

And for the .appspec file:

author: Henrik

And to the gemfile:

gem 'fpm'

Now, run rake again (on Windows or Linux):

bundle exec rake

You should now have a .rpm if you built on CentOS or a .nupkg if you built on Windows. If you built on TeamCity, your TeamCity is capable of hosting this package on its own and you can then use chocolatey to manage it after adding your teamcity server as a source.

This concludes the Hello World tutorial for suave. You can fetch the code at github.

Twitter @henrikfeldt

Twitter @logarylib

Henrik Feldt +46 737 53 27 18 haf