@@ -26,7 +26,8 @@ import Control.Monad.Trans.Reader
26import Control.Monad.Trans.Resource
26import Control.Monad.Trans.Resource
27import Control.Monad.Trans.State
27import Control.Monad.Trans.State
28import Control.Monad.Trans.Writer
28import Control.Monad.Trans.Writer
29−import Data.Aeson (ToJSON (..), object)
29+import Data.Aeson (FromJSON (..), ToJSON (..),
30import qualified Data.Aeson as A
31import qualified Data.Aeson as A
31import Data.Foldable (foldMap)
32import Data.Foldable (foldMap)
32import qualified Data.HashMap.Strict as HM
33import qualified Data.HashMap.Strict as HM
@@ -49,7 +50,7 @@ import System.Posix
50-------------------------------------------------------------------------------
51-------------------------------------------------------------------------------
51newtype Namespace = Namespace { unNamespace :: [Text] }
52newtype Namespace = Namespace { unNamespace :: [Text] }
52− deriving (Eq,Show,Read,Ord,Generic,ToJSON,Monoid)
53+ deriving (Eq,Show,Read,Ord,Generic,ToJSON,FromJSON,Monoid)
54instance IsString Namespace where
55instance IsString Namespace where
55 fromString s = Namespace [fromString s]
56 fromString s = Namespace [fromString s]
@@ -64,7 +65,7 @@ intercalateNs (Namespace xs) = intersperse "." xs
64-------------------------------------------------------------------------------
65-------------------------------------------------------------------------------
65-- | Application environment, like @prod@, @devel@, @testing@.
66-- | Application environment, like @prod@, @devel@, @testing@.
66newtype Environment = Environment { getEnvironment :: Text }
67newtype Environment = Environment { getEnvironment :: Text }
67− deriving (Eq,Show,Read,Ord,Generic,ToJSON,IsString)
68+ deriving (Eq,Show,Read,Ord,Generic,ToJSON,FromJSON,IsString)
70-------------------------------------------------------------------------------
71-------------------------------------------------------------------------------
@@ -106,8 +107,37 @@ renderSeverity s = case s of
108-------------------------------------------------------------------------------
109-------------------------------------------------------------------------------
110+severityText :: Prism' Text Severity
111+severityText = prism fromSev toSev
113+ fromSev DebugS = "Debug"
114+ fromSev InfoS = "Info"
115+ fromSev NoticeS = "Notice"
116+ fromSev WarningS = "Warning"
117+ fromSev ErrorS = "Error"
118+ fromSev CriticalS = "Critical"
119+ fromSev AlertS = "Alert"
120+ fromSev EmergencyS = "Emergency"
121+ toSev "Debug" = Right DebugS
122+ toSev "Info" = Right InfoS
123+ toSev "Notice" = Right NoticeS
124+ toSev "Warning" = Right WarningS
125+ toSev "Error" = Right ErrorS
126+ toSev "Critical" = Right CriticalS
127+ toSev "Alert" = Right AlertS
128+ toSev "Emergency" = Right EmergencyS
109instance ToJSON Severity where
131instance ToJSON Severity where
110− toJSON s = A.String (renderSeverity s)
132+ toJSON s = A.String (s ^. re severityText)
135+instance FromJSON Severity where
136+ parseJSON = A.withText "Severity" parseSeverity
138+ parseSeverity t = case t ^? severityText of
140+ Nothing -> fail $ "Invalid Severity " ++ toS t
113-------------------------------------------------------------------------------
143-------------------------------------------------------------------------------
@@ -122,6 +152,11 @@ instance Monoid LogStr where
122 mappend (LogStr a) (LogStr b) = LogStr (a `mappend` b)
152 mappend (LogStr a) (LogStr b) = LogStr (a `mappend` b)
123 mempty = LogStr mempty
153 mempty = LogStr mempty
155+instance FromJSON LogStr where
156+ parseJSON = A.withText "LogStr" parseLogStr
158+ parseLogStr = return . LogStr . B.fromText
125-------------------------------------------------------------------------------
160-------------------------------------------------------------------------------
126-- | Pack any string-like thing into a 'LogStr'. This will
161-- | Pack any string-like thing into a 'LogStr'. This will
127-- automatically work on 'String', 'ByteString, 'Text' and any of the
162-- automatically work on 'String', 'ByteString, 'Text' and any of the
@@ -141,13 +176,22 @@ showLS :: Show a => a -> LogStr
141showLS = ls . show
176showLS = ls . show
179+-------------------------------------------------------------------------------
180+newtype ThreadIdText = ThreadIdText {
181+ getThreadIdText :: Text
182+ } deriving (ToJSON, FromJSON, Show, Eq, Ord)
185+mkThreadIdText :: ThreadId -> ThreadIdText
186+mkThreadIdText = ThreadIdText . T.pack . show
144-------------------------------------------------------------------------------
188-------------------------------------------------------------------------------
145-- | This has everything each log message will contain.
189-- | This has everything each log message will contain.
146data Item a = Item {
190data Item a = Item {
147 _itemApp :: Namespace
191 _itemApp :: Namespace
148 , _itemEnv :: Environment
192 , _itemEnv :: Environment
149 , _itemSeverity :: Severity
193 , _itemSeverity :: Severity
150− , _itemThread :: ThreadId
194+ , _itemThread :: ThreadIdText
151 , _itemHost :: HostName
195 , _itemHost :: HostName
152 , _itemProcess :: ProcessID
196 , _itemProcess :: ProcessID
153 , _itemPayload :: a
197 , _itemPayload :: a
@@ -164,9 +208,9 @@ instance ToJSON a => ToJSON (Item a) where
164 [ "app" A..= _itemApp
208 [ "app" A..= _itemApp
165 , "env" A..= _itemEnv
209 , "env" A..= _itemEnv
166 , "sev" A..= _itemSeverity
210 , "sev" A..= _itemSeverity
167− , "thread" A..= show _itemThread
211+ , "thread" A..= getThreadIdText _itemThread
168 , "host" A..= _itemHost
212 , "host" A..= _itemHost
169− , "pid" A..= A.String (toS (show _itemProcess))
213+ , "pid" A..= ProcessIDJs _itemProcess
170 , "data" A..= _itemPayload
214 , "data" A..= _itemPayload
171 , "msg" A..= (B.toLazyText $ unLogStr _itemMessage)
215 , "msg" A..= (B.toLazyText $ unLogStr _itemMessage)
172 , "at" A..= _itemTime
216 , "at" A..= _itemTime
@@ -187,6 +231,61 @@ instance ToJSON LocJs where
234+instance FromJSON LocJs where
235+ parseJSON = A.withObject "LocJs" parseLocJs
238+ fn <- o A..: "loc_fn"
239+ p <- o A..: "loc_pkg"
240+ m <- o A..: "loc_mod"
241+ l <- o A..: "loc_ln"
242+ c <- o A..: "loc_col"
243+ return $ LocJs $ Loc fn p m (l, c) (l, c)
246+instance FromJSON a => FromJSON (Item a) where
247+ parseJSON = A.withObject "Item" parseItem
248+ where --TODO: match these up with the serialize
255+ <*> (getProcessIDJs <$> o A..: "pid")
260+ <*> (fmap getLocJs <$> o A..: "loc")
263+processIDText :: Prism' Text ProcessID
264+processIDText = prism fromProcessID toProcessID
266+ fromProcessID = toS . show
267+ toProcessID t = case toS t ^? _Show of
272+newtype ProcessIDJs = ProcessIDJs {
273+ getProcessIDJs :: ProcessID
277+instance ToJSON ProcessIDJs where
278+ toJSON (ProcessIDJs p) = A.String (p ^. re processIDText)
281+instance FromJSON ProcessIDJs where
282+ parseJSON = A.withText "ProcessID" parseProcessID
284+ parseProcessID t = case t ^? processIDText of
285+ Just p -> return $ ProcessIDJs p
286+ Nothing -> fail $ "Invalid ProcessIDJs " ++ toS t
190-------------------------------------------------------------------------------
289-------------------------------------------------------------------------------
191-- | Field selector by verbosity within JSON payload.
290-- | Field selector by verbosity within JSON payload.
192data PayloadSelection
291data PayloadSelection
@@ -411,7 +510,7 @@ logItem a ns loc sev msg = do
411 <$> pure _logEnvNs
510 <$> pure _logEnvNs
412 <*> pure _logEnvEnv
511 <*> pure _logEnvEnv
413 <*> pure sev
512 <*> pure sev
414− <*> liftIO myThreadId
513+ <*> liftIO (mkThreadIdText <$> myThreadId)
415 <*> pure _logEnvHost
514 <*> pure _logEnvHost
416 <*> pure _logEnvPid
515 <*> pure _logEnvPid
417 <*> pure a
516 <*> pure a