diff --git a/log.cabal b/log.cabal
--- a/log.cabal
+++ b/log.cabal
@@ -1,5 +1,5 @@
 name:                log
-version:             0.2.0
+version:             0.2.1
 synopsis:            Structured logging solution with multiple backends
 
 description:         A library that provides a way to record structured log messages with multiple backends.
diff --git a/src/Log/Backend/PostgreSQL.hs b/src/Log/Backend/PostgreSQL.hs
--- a/src/Log/Backend/PostgreSQL.hs
+++ b/src/Log/Backend/PostgreSQL.hs
@@ -4,7 +4,6 @@
 import Control.Concurrent
 import Control.Exception
 import Data.Aeson
-import Data.ByteString (ByteString)
 import Data.ByteString.Lazy (toStrict)
 import Data.Monoid
 import Data.Monoid.Utils
@@ -33,18 +32,13 @@
       , "now()"
       , "," <?> n
       , "," <?> lmTime
-      , "," <?> sqlifyLevel lmLevel
+      , "," <?> showLogLevel lmLevel
       , "," <?> lmComponent
       , "," <?> Array1 lmDomain
       , "," <?> lmMessage
       , "," <?> toStrict (encode lmData) <> "::jsonb"
       , ")"
       ]
-
-    sqlifyLevel :: LogLevel -> ByteString
-    sqlifyLevel LogAttention = "attention"
-    sqlifyLevel LogInfo = "info"
-    sqlifyLevel LogTrace = "trace"
 
     ts :: TransactionSettings
     ts = def {
diff --git a/src/Log/Backend/StandardOutput.hs b/src/Log/Backend/StandardOutput.hs
--- a/src/Log/Backend/StandardOutput.hs
+++ b/src/Log/Backend/StandardOutput.hs
@@ -1,15 +1,6 @@
-{-# LANGUAGE CPP, OverloadedStrings, RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
 module Log.Backend.StandardOutput (stdoutLogger) where
 
-import Data.Aeson.Encode.Pretty
-import Data.Aeson.Types
-import Data.ByteString.Lazy (toStrict)
-import Data.Time
-#if !MIN_VERSION_time(1,5,0)
-import System.Locale
-#endif
-import qualified Data.Text as T
-import qualified Data.Text.Encoding as T
 import qualified Data.Text.IO as T
 
 import Log.Data
@@ -17,27 +8,4 @@
 
 -- | Create logger that prints messages to standard output.
 stdoutLogger :: IO Logger
-stdoutLogger = mkLogger "stdout" printLogMessage
-
-printLogMessage :: LogMessage -> IO ()
-printLogMessage LogMessage{..} = T.putStrLn . T.concat $ [
-    T.pack $ formatTime defaultTimeLocale "%Y-%m-%d %H:%M:%S" lmTime
-  , " "
-  , textifyLevel lmLevel
-  , " "
-  , T.intercalate "/" $ lmComponent : lmDomain
-  , ": "
-  , lmMessage
-  ] ++ if lmData == emptyObject
-    then []
-    else [" ", textifyData lmData]
-  where
-    textifyData :: Value -> T.Text
-    textifyData = T.decodeUtf8 . toStrict . encodePretty' defConfig {
-      confIndent = 2
-    }
-
-    textifyLevel :: LogLevel -> T.Text
-    textifyLevel LogAttention = "ATTENTION"
-    textifyLevel LogInfo = "INFO"
-    textifyLevel LogTrace = "TRACE"
+stdoutLogger = mkLogger "stdout" $ T.putStrLn . showLogMessage
diff --git a/src/Log/Data.hs b/src/Log/Data.hs
--- a/src/Log/Data.hs
+++ b/src/Log/Data.hs
@@ -1,36 +1,85 @@
-{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE CPP, OverloadedStrings, RecordWildCards #-}
 module Log.Data (
     LogLevel(..)
+  , showLogLevel
+  , readLogLevel
   , LogMessage(..)
+  , showLogMessage
   ) where
 
 import Control.DeepSeq
 import Data.Aeson
-import Data.Text (Text)
+import Data.Aeson.Encode.Pretty
+import Data.Aeson.Types
+import Data.ByteString.Lazy (toStrict)
 import Data.Time
+#if !MIN_VERSION_time(1,5,0)
+import System.Locale
+#endif
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
 
 -- | Available log levels.
 data LogLevel = LogAttention | LogInfo | LogTrace
   deriving (Bounded, Eq, Ord, Show)
 
+readLogLevel :: T.Text -> LogLevel
+readLogLevel "attention" = LogAttention
+readLogLevel "info"      = LogInfo
+readLogLevel "trace"     = LogTrace
+readLogLevel level       = error $ "readLogLevel: unknown level: " ++ T.unpack level
+
+showLogLevel :: LogLevel -> T.Text
+showLogLevel LogAttention = "attention"
+showLogLevel LogInfo      = "info"
+showLogLevel LogTrace     = "trace"
+
 instance NFData LogLevel where
   rnf = (`seq` ())
 
 -- | Represents message to be logged.
 data LogMessage = LogMessage {
   -- | Component of an application.
-  lmComponent :: !Text
+  lmComponent :: !T.Text
   -- | Aplication log domain.
-, lmDomain    :: ![Text]
+, lmDomain    :: ![T.Text]
   -- | Time of log.
 , lmTime      :: !UTCTime
   -- | Log level.
 , lmLevel     :: !LogLevel
   -- | Message to be logged.
-, lmMessage   :: !Text
+, lmMessage   :: !T.Text
   -- | Additional data associated with the message.
 , lmData      :: !Value
 } deriving (Eq, Show)
+
+showLogMessage :: LogMessage -> T.Text
+showLogMessage LogMessage{..} = T.concat $ [
+    T.pack $ formatTime defaultTimeLocale "%Y-%m-%d %H:%M:%S" lmTime
+  , " "
+  , T.toUpper $ showLogLevel lmLevel
+  , " "
+  , T.intercalate "/" $ lmComponent : lmDomain
+  , ": "
+  , lmMessage
+  ] ++ if lmData == emptyObject
+    then []
+    else [" ", textifyData lmData]
+  where
+    textifyData :: Value -> T.Text
+    textifyData = T.decodeUtf8 . toStrict . encodePretty' defConfig {
+      confIndent = 2
+    }
+
+instance ToJSON LogMessage where
+  toJSON LogMessage{..} = object [
+      "component" .= lmComponent
+    , "domain"    .= lmDomain
+    , "time"      .= lmComponent
+    , "level"     .= showLogLevel lmLevel
+    , "message"   .= lmMessage
+    , "data"      .= lmData
+    ]
 
 instance NFData LogMessage where
   rnf LogMessage{..} = rnf lmComponent
