diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Zalora South East Asia Pte. Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/logsink.cabal b/logsink.cabal
new file mode 100644
--- /dev/null
+++ b/logsink.cabal
@@ -0,0 +1,47 @@
+name:             logsink
+version:          0.1.0
+synopsis:         A logging framework for Haskell
+description:      A logging framework for Haskell
+license:          MIT
+license-file:     LICENSE
+copyright:        (c) Zalora South East Asia Pte. Ltd
+maintainer:       Simon Hengel <sol@typeful.net>
+build-type:       Simple
+cabal-version:    >= 1.10
+category:         System
+
+source-repository head
+  type: git
+  location: https://github.com/zalora/logsink
+
+library
+  ghc-options:
+      -Wall
+  hs-source-dirs:
+      src
+  exposed-modules:
+      System.Logging.LogSink.Config
+      System.Logging.LogSink.Core
+      System.Logging.LogSink.Format
+  build-depends:
+      base == 4.*
+    , logging-facade
+    , hsyslog
+    , time
+  default-language: Haskell2010
+
+test-suite spec
+  type:
+      exitcode-stdio-1.0
+  ghc-options:
+      -Wall
+  hs-source-dirs:
+      test
+  main-is:
+      Spec.hs
+  build-depends:
+      base == 4.*
+    , logsink
+    , logging-facade
+    , hspec == 2.*
+  default-language: Haskell2010
diff --git a/src/System/Logging/LogSink/Config.hs b/src/System/Logging/LogSink/Config.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Logging/LogSink/Config.hs
@@ -0,0 +1,52 @@
+module System.Logging.LogSink.Config (
+  SinkConfig(..)
+, defaultSinkConfig
+, LogLevel(..)
+, LogTarget(..)
+, toLogSink
+, setupLogging
+) where
+
+import           Control.Applicative
+import           System.Exit (exitFailure)
+import           System.IO
+import           System.Logging.Facade.Sink
+import           System.Logging.Facade.Types
+
+import           System.Logging.LogSink.Core
+import           System.Logging.LogSink.Format
+
+data LogTarget = StdErr | SysLog
+  deriving (Eq, Show)
+
+data SinkConfig = SinkConfig {
+  sinkConfigLevel :: LogLevel
+, sinkConfigFormat :: String
+, sinkConfigTarget :: LogTarget
+} deriving (Eq, Show)
+
+defaultSinkConfig :: SinkConfig
+defaultSinkConfig = SinkConfig {
+  sinkConfigLevel = minBound
+, sinkConfigFormat = defaultFormatString
+, sinkConfigTarget = StdErr
+}
+
+setupLogging :: [SinkConfig] -> IO ()
+setupLogging sinks = either die (setLogSink . combine) (mapM toLogSink sinks)
+
+die :: String -> IO a
+die err = hPutStrLn stderr err >> exitFailure
+
+toLogSink :: SinkConfig -> Either String LogSink
+toLogSink sink = filterByLogLevel (sinkConfigLevel sink) . targetToSink sink <$> parseFormat_ (sinkConfigFormat sink)
+  where
+    parseFormat_ :: String -> Either String Format
+    parseFormat_ fmt = case parseFormat fmt of
+      Left err -> Left ("Invalid format " ++ show fmt ++ " (" ++ err ++ ")")
+      Right f -> return f
+
+targetToSink :: SinkConfig -> Format -> LogSink
+targetToSink sink = case sinkConfigTarget sink of
+  StdErr -> stdErrSink
+  SysLog -> sysLogSink
diff --git a/src/System/Logging/LogSink/Core.hs b/src/System/Logging/LogSink/Core.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Logging/LogSink/Core.hs
@@ -0,0 +1,56 @@
+module System.Logging.LogSink.Core (
+  Format
+, defaultFormat
+, stdErrSink
+, sysLogSink
+, combine
+, filterByLogLevel
+) where
+
+import           Control.Concurrent.MVar
+import           Control.Monad
+import           System.IO
+import           System.IO.Unsafe
+import           System.Logging.Facade.Sink
+import           System.Logging.Facade.Types
+import           System.Posix.Syslog
+
+import           System.Logging.LogSink.Format
+
+defaultFormat :: Format
+defaultFormat =
+  let Right format = parseFormat defaultFormatString
+  in format
+
+{-# NOINLINE stderrLock #-}
+stderrLock :: MVar ()
+stderrLock = unsafePerformIO $ newMVar ()
+
+stdErrSink :: Format -> LogSink
+stdErrSink format record = do
+  s <- format record
+  modifyMVar_ stderrLock $ \ () -> hPutStrLn stderr s
+
+sysLogSink :: Format -> LogSink
+sysLogSink format record = format record >>= syslog (toPriority $ logRecordLevel record)
+  where
+    toPriority :: LogLevel -> Priority
+    toPriority l = case l of
+      TRACE -> Debug
+      DEBUG -> Debug
+      INFO -> Info
+      WARN -> Warning
+      ERROR -> Error
+
+combine :: [LogSink] -> LogSink
+combine sinks record = do
+  forM_ sinks $ \sink -> sink record
+
+filterByLogLevel :: LogLevel -> LogSink -> LogSink
+filterByLogLevel level sink
+  | level == minBound = sink
+  | otherwise = filteringSink
+  where
+    filteringSink record
+      | logRecordLevel record < level = return ()
+      | otherwise = sink record
diff --git a/src/System/Logging/LogSink/Format.hs b/src/System/Logging/LogSink/Format.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Logging/LogSink/Format.hs
@@ -0,0 +1,57 @@
+{-# LANGUAGE RecordWildCards #-}
+module System.Logging.LogSink.Format where
+
+import           Control.Applicative
+import           Control.Concurrent
+import           Data.Char
+import           Data.Time.Clock
+import           Data.Time.LocalTime ()
+import           System.Logging.Facade.Types
+
+type Format = LogRecord -> IO String
+
+defaultFormatString :: String
+defaultFormatString = "{level}: {message}"
+
+parseFormat :: String -> Either String Format
+parseFormat format = formatNodes <$> parseNodes format
+
+data Node = Level | Message | Timestamp | ThreadId | Literal String
+  deriving (Eq, Show)
+
+formatNodes :: [Node] -> LogRecord -> IO String
+formatNodes nodes LogRecord{..} = concat <$> mapM evalNode nodes
+  where
+    evalNode :: Node -> IO String
+    evalNode node = case node of
+      Level -> return (show logRecordLevel)
+      Message -> return logRecordMessage
+      Timestamp -> show <$> getCurrentTime
+      ThreadId -> show <$> myThreadId
+      Literal s -> return s
+
+parseNodes :: String -> Either String [Node]
+parseNodes = fmap (filter $ not . isEmpty) . go ""
+  where
+    isIdChar :: Char -> Bool
+    isIdChar c = isAlphaNum c || (c `elem` "-_")
+
+    lookupNode :: String -> Maybe Node
+    lookupNode key = lookup key [("level", Level), ("message", Message), ("timestamp", Timestamp), ("thread-id", ThreadId)]
+
+    go :: String -> String -> Either String [Node]
+    go acc input = case input of
+      ""  -> return [lit acc]
+      '{':xs | (key,'}':ys) <- span isIdChar xs -> case lookupNode key of
+        Nothing -> Left ("invalid format directive " ++ show key)
+        Just node -> do
+          nodes <- go "" ys
+          return (lit acc : node : nodes)
+      x:xs -> go (x:acc) xs
+
+    lit :: String -> Node
+    lit acc = (Literal . reverse) acc
+
+    isEmpty :: Node -> Bool
+    isEmpty (Literal "") = True
+    isEmpty _ = False
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
