packages feed

ribosome-host-0.9.9.9: lib/Ribosome/Host/Interpreter/Log.hs

module Ribosome.Host.Interpreter.Log where

import qualified Data.Text as Text
import Exon (exon)
import qualified Log
import Log (
  Log (Log),
  LogMessage (LogMessage),
  Severity (Warn),
  dataLog,
  formatLogEntry,
  interceptDataLogConc,
  interpretLogDataLogConc,
  interpretLogNull,
  setLogLevel,
  )
import Path (Abs, File, Path, toFilePath)
import Polysemy.Chronos (ChronosTime)
import Polysemy.Log (interpretLogStderrLevelConc)
import Polysemy.Log.Handle (interpretDataLogHandleWith)
import Polysemy.Log.Log (interpretDataLog)
import System.IO (Handle, IOMode (AppendMode), hClose, openFile)

import Ribosome.Host.Api.Effect (nvimEcho)
import Ribosome.Host.Class.Msgpack.Encode (toMsgpack)
import qualified Ribosome.Host.Data.HostConfig as HostConfig
import Ribosome.Host.Data.HostConfig (LogConfig (LogConfig))
import Ribosome.Host.Data.Report (LogReport (LogReport), Report (Report), prefixReportContext')
import Ribosome.Host.Effect.Log (FileLog, StderrLog, fileLog, stderrLog)
import qualified Ribosome.Host.Effect.Reports as Reports
import Ribosome.Host.Effect.Reports (Reports)
import Ribosome.Host.Effect.Rpc (Rpc)
import Ribosome.Host.Effect.UserError (UserError, userError)

echoError ::
  Show e =>
  Members [Rpc !! e, UserError, Log] r =>
  Severity ->
  Text ->
  Severity ->
  Sem r ()
echoError minSeverity err severity | severity >= minSeverity =
  userError err severity >>= traverse_ \ msg ->
    nvimEcho [toMsgpack @[_] msg] True mempty !! \ e' ->
      Log.error [exon|Couldn't echo handler error: #{show e'}|]
echoError _ _ _ =
  unit

logLogReport ::
  Show e =>
  Members [Rpc !! e, Reports, UserError, Log] r =>
  Severity ->
  LogReport ->
  Sem r ()
logLogReport minSeverity (LogReport msg@(Report user log severity) echo store context) =
  withFrozenCallStack do
    Log.log severity (Text.intercalate "\n" (maybeToList (prefixReportContext' context) <> log))
    when store (Reports.storeReport context msg)
    when echo (echoError minSeverity user severity)

interpretDataLogRpc ::
  Show e =>
  Members [Reader LogConfig, Rpc !! e, Reports, UserError, Log, Resource, Race, Async, Embed IO] r =>
  InterpreterFor (DataLog LogReport) r
interpretDataLogRpc sem = do
  LogConfig {..} <- ask
  interpretDataLog (logLogReport logLevelEcho) ((if dataLogConc then interceptDataLogConc 64 else id) sem)

interpretLogRpc ::
  Members [Log, DataLog LogReport] r =>
  InterpreterFor Log r
interpretLogRpc =
  interpret \case
    Log (LogMessage severity msg) -> do
      dataLog (LogReport (Report msg [msg] severity) True (severity >= Warn) mempty)

interpretLogStderrFile ::
  Members [StderrLog, FileLog] r =>
  InterpreterFor Log r
interpretLogStderrFile =
  interpret \case
    Log m ->
      fileLog (send (Log m)) *> stderrLog (send (Log m))

interpretLogHandleLevel ::
  Members [Resource, ChronosTime, Race, Async, Embed IO] r =>
  Handle ->
  Maybe Severity ->
  InterpreterFor Log r
interpretLogHandleLevel handle level =
  interpretDataLogHandleWith handle formatLogEntry .
  setLogLevel level .
  interpretLogDataLogConc 64 .
  raiseUnder
{-# inline interpretLogHandleLevel #-}

interpretLogFileLevel ::
  Members [Resource, ChronosTime, Race, Async, Embed IO] r =>
  Maybe Severity ->
  Path Abs File ->
  InterpreterFor Log r
interpretLogFileLevel level path sem =
  bracket acquire (embed . hClose) \ handle ->
    interpretLogHandleLevel handle level sem
  where
    acquire =
      embed (openFile (toFilePath path) AppendMode)
{-# inline interpretLogFileLevel #-}

interpretLogs ::
  Members [Reader LogConfig, Resource, ChronosTime, Race, Async, Embed IO] r =>
  InterpretersFor [StderrLog, FileLog] r
interpretLogs sem =
  ask >>= \ LogConfig {..} ->
    maybe interpretLogNull (\ f -> interpretLogFileLevel (Just logLevelFile) f) logFile $
    untag @"file" $
    interpretLogStderrLevelConc (Just logLevelStderr) $
    untag @"stderr" sem