module Colog.Json.Action
( logToHandle
, encodeMessage
) where
import Colog.Core
import Colog.Json.Internal.Structured
import Control.Exception
import Data.Aeson.Encoding as Aeson
import qualified Data.ByteString.Builder as Builder
import Data.Coerce
import Data.Foldable
import qualified Data.List.NonEmpty as NE
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TLB
import System.IO
logToHandle :: Handle -> LogAction IO Message
logToHandle :: Handle -> LogAction IO Message
logToHandle Handle
h = (Message -> IO ()) -> LogAction IO Message
forall (m :: * -> *) msg. (msg -> m ()) -> LogAction m msg
LogAction \Message
m ->
let msg :: Encoding
msg = Message -> Encoding
encodeMessage Message
m
in IO () -> IO (Either SomeException ())
forall e a. Exception e => IO a -> IO (Either e a)
try (Handle -> Builder -> IO ()
Builder.hPutBuilder Handle
h (Builder -> IO ()) -> Builder -> IO ()
forall a b. (a -> b) -> a -> b
$ Encoding -> Builder
forall tag. Encoding' tag -> Builder
Aeson.fromEncoding Encoding
msg Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.char7 Char
'\n') IO (Either SomeException ())
-> (Either SomeException () -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Left SomeException
e -> do
Handle -> Builder -> IO ()
Builder.hPutBuilder Handle
h (Builder -> IO ()) -> Builder -> IO ()
forall a b. (a -> b) -> a -> b
$ Encoding -> Builder
forall tag. Encoding' tag -> Builder
Aeson.fromEncoding (Series -> Encoding
Aeson.pairs
(Series -> Encoding) -> Series -> Encoding
forall a b. (a -> b) -> a -> b
$ [Series] -> Series
forall a. Monoid a => [a] -> a
mconcat
[ Text -> Encoding -> Series
Aeson.pair Text
"namespace" (Text -> Encoding
forall a. Text -> Encoding' a
Aeson.text Text
"logger")
, Text -> Encoding -> Series
Aeson.pair Text
"exception" (String -> Encoding
forall a. String -> Encoding' a
Aeson.string (SomeException -> String
forall a. Show a => a -> String
show (SomeException
e::SomeException)))
])
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.char7 Char
'\n'
Right ()
_ -> () -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
encodeMessage :: Message -> Encoding
{-# INLINE encodeMessage #-}
encodeMessage :: Message -> Encoding
encodeMessage Message{Int
Seq Structured
Severity
LogStr
message :: Message -> LogStr
attributes :: Message -> Seq Structured
thread_id :: Message -> Int
message_severity :: Message -> Severity
message :: LogStr
attributes :: Seq Structured
thread_id :: Int
message_severity :: Severity
..} = Series -> Encoding
Aeson.pairs (Series -> Encoding) -> Series -> Encoding
forall a b. (a -> b) -> a -> b
$ [Series] -> Series
forall a. Monoid a => [a] -> a
mconcat [Series]
fields where
fields :: [Series]
fields =
[ case Maybe Text
namespace of
Maybe Text
Nothing -> Series
forall a. Monoid a => a
mempty
Just Text
xs -> Text -> Encoding -> Series
Aeson.pair Text
"namespace" (Encoding -> Series) -> Encoding -> Series
forall a b. (a -> b) -> a -> b
$ Text -> Encoding
forall a. Text -> Encoding' a
Aeson.lazyText Text
xs
, Text -> Encoding -> Series
Aeson.pair Text
"severity" (Encoding -> Series) -> Encoding -> Series
forall a b. (a -> b) -> a -> b
$ Severity -> Encoding
encodeSeverity Severity
message_severity
, Text -> Encoding -> Series
Aeson.pair Text
"thread" (Encoding -> Series) -> Encoding -> Series
forall a b. (a -> b) -> a -> b
$ Int -> Encoding
Aeson.int Int
thread_id
, case Maybe Series
user_data of
Maybe Series
Nothing -> Series
forall a. Monoid a => a
mempty
Just Series
xs -> Text -> Encoding -> Series
Aeson.pair Text
"data" (Encoding -> Series) -> Encoding -> Series
forall a b. (a -> b) -> a -> b
$ Series -> Encoding
Aeson.pairs Series
xs
, Text -> Encoding -> Series
Aeson.pair Text
"message" (Encoding -> Series) -> Encoding -> Series
forall a b. (a -> b) -> a -> b
$ Text -> Encoding
forall a. Text -> Encoding' a
lazyText (Text -> Encoding) -> Text -> Encoding
forall a b. (a -> b) -> a -> b
$ (Builder -> Text) -> LogStr -> Text
coerce Builder -> Text
TLB.toLazyText LogStr
message
]
namespace :: Maybe Text
namespace = Text -> [Text] -> Text
TL.intercalate Text
"."([Text] -> Text)
-> (NonEmpty Text -> [Text]) -> NonEmpty Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty Text -> [Text]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (NonEmpty Text -> Text) -> Maybe (NonEmpty Text) -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Text] -> Maybe (NonEmpty Text)
forall a. [a] -> Maybe (NonEmpty a)
NE.nonEmpty
[ Text -> Text
TL.fromStrict Text
tm
| Segment Text
tm <- Seq Structured -> [Structured]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList Seq Structured
attributes
]
user_data :: Maybe Series
user_data = NonEmpty Series -> Series
forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
fold (NonEmpty Series -> Series)
-> Maybe (NonEmpty Series) -> Maybe Series
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Series] -> Maybe (NonEmpty Series)
forall a. [a] -> Maybe (NonEmpty a)
NE.nonEmpty
[ Text -> Encoding -> Series
pair Text
key Encoding
attributeValue
| Attr Text
key Encoding
attributeValue <- Seq Structured -> [Structured]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList Seq Structured
attributes
]