@@ -14,6 +14,7 @@ import Control.Applicative
14import Control.AutoUpdate
14import Control.AutoUpdate
15import Control.Concurrent
15import Control.Concurrent
16import Control.Lens
16import Control.Lens
17+import Control.Monad.Catch
17import Control.Monad.Reader
18import Control.Monad.Reader
18import Data.Aeson (ToJSON (..))
19import Data.Aeson (ToJSON (..))
19import qualified Data.Aeson as A
20import qualified Data.Aeson as A
@@ -26,6 +27,7 @@ import Data.String
26import Data.String.Conv
27import Data.String.Conv
27import Data.Text (Text)
28import Data.Text (Text)
28import qualified Data.Text as T
29import qualified Data.Text as T
30+import qualified Data.Text.Lazy.Builder as B
29import Data.Time
31import Data.Time
30import GHC.Generics hiding (to)
32import GHC.Generics hiding (to)
31import Language.Haskell.TH
33import Language.Haskell.TH
@@ -98,6 +100,29 @@ instance ToJSON Severity where
98 toJSON s = A.String (renderSeverity s)
100 toJSON s = A.String (renderSeverity s)
103+-------------------------------------------------------------------------------
104+-- | Log message with Builder unerneath; use '<>' to concat in O(1).
105+newtype LogStr = LogStr { unLogStr :: B.Builder }
108+instance IsString LogStr where
109+ fromString = LogStr . B.fromString
112+-------------------------------------------------------------------------------
113+-- | Pack any string-like thing into a 'LogMsg'. This will
114+-- automatically work on 'String', 'ByteString, 'Text' and any of the
116+logStr :: StringConv a Text => a -> LogStr
117+logStr t = LogStr (B.fromText $ toS t)
120+-------------------------------------------------------------------------------
121+-- | Shorthand for 'logMsg'
122+ls :: StringConv a Text => a -> LogStr
101-------------------------------------------------------------------------------
126-------------------------------------------------------------------------------
102-- | This has everything each log message will contain.
127-- | This has everything each log message will contain.
103data Item a = Item {
128data Item a = Item {
@@ -108,7 +133,7 @@ data Item a = Item {
108 , itemHost :: HostName
133 , itemHost :: HostName
109 , itemProcess :: ProcessID
134 , itemProcess :: ProcessID
110 , itemPayload :: a
135 , itemPayload :: a
111− , itemMessage :: Text
136+ , itemMessage :: LogStr
112 , itemTime :: UTCTime
137 , itemTime :: UTCTime
113 , itemNamespace :: Namespace
138 , itemNamespace :: Namespace
114 , itemLoc :: Maybe Loc
139 , itemLoc :: Maybe Loc
@@ -125,7 +150,7 @@ instance ToJSON a => ToJSON (Item a) where
125 , "host" A..= itemHost
150 , "host" A..= itemHost
126 , "pid" A..= A.String (toS (show itemProcess))
151 , "pid" A..= A.String (toS (show itemProcess))
127 , "data" A..= itemPayload
152 , "data" A..= itemPayload
128− , "msg" A..= itemMessage
153+ , "msg" A..= (B.toLazyText $ unLogStr itemMessage)
129 , "at" A..= itemTime
154 , "at" A..= itemTime
130 , "ns" A..= itemNamespace
155 , "ns" A..= itemNamespace
131 , "loc" A..= fmap LocJs itemLoc
156 , "loc" A..= fmap LocJs itemLoc
@@ -236,6 +261,19 @@ class Katip m where
236 getLogEnv :: m LogEnv
261 getLogEnv :: m LogEnv
264+-------------------------------------------------------------------------------
265+-- | A concrete monad you can use to run logging actions.
266+newtype KatipT m a = KatipT { unKatipT :: ReaderT LogEnv m a }
267+ deriving ( Monad, MonadIO, MonadMask, MonadCatch, MonadThrow
268+ , MonadReader LogEnv)
271+-------------------------------------------------------------------------------
272+-- | Execute 'KatipT' on a log env.
273+runKatipT :: LogEnv -> KatipT m a -> m a
274+runKatipT le (KatipT f) = runReaderT f le
239-------------------------------------------------------------------------------
277-------------------------------------------------------------------------------
240-- | Log with everything, including a source code location. This is
278-- | Log with everything, including a source code location. This is
241-- very low level and you typically can use 'logT' in its place.
279-- very low level and you typically can use 'logT' in its place.
@@ -245,7 +283,7 @@ logI
245 -> Namespace
283 -> Namespace
246 -> Maybe Loc
284 -> Maybe Loc
247 -> Severity
285 -> Severity
248− -> Text
286+ -> LogStr
250logI a ns loc sev msg = do
288logI a ns loc sev msg = do
251 LogEnv{..} <- getLogEnv
289 LogEnv{..} <- getLogEnv
@@ -274,7 +312,7 @@ logF
274 -- ^ Specific namespace of the message.
312 -- ^ Specific namespace of the message.
275 -> Severity
313 -> Severity
276 -- ^ Severity of the message
314 -- ^ Severity of the message
277− -> Text
315+ -> LogStr
278 -- ^ The log message
316 -- ^ The log message
280logF a ns sev msg = logI a ns Nothing sev msg
318logF a ns sev msg = logI a ns Nothing sev msg
@@ -286,7 +324,7 @@ logM
286 :: (Applicative m, MonadIO m, Katip m)
324 :: (Applicative m, MonadIO m, Katip m)
287 => Namespace
325 => Namespace
288 -> Severity
326 -> Severity
289− -> Text
327+ -> LogStr
291logM ns sev msg = logF () ns sev msg
329logM ns sev msg = logF () ns sev msg