* teminal: option to log errors and service messages to file, closes #1516 * rename function
This commit is contained in:
parent
38b7e4d4a4
commit
2b77920dcd
@ -138,7 +138,7 @@ createChatDatabase filePrefix key yesToMigrations = do
|
|||||||
pure ChatDatabase {chatStore, agentStore}
|
pure ChatDatabase {chatStore, agentStore}
|
||||||
|
|
||||||
newChatController :: ChatDatabase -> Maybe User -> ChatConfig -> ChatOpts -> Maybe (Notification -> IO ()) -> IO ChatController
|
newChatController :: ChatDatabase -> Maybe User -> ChatConfig -> ChatOpts -> Maybe (Notification -> IO ()) -> IO ChatController
|
||||||
newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agentConfig = aCfg, defaultServers, inlineFiles} ChatOpts {coreOptions = CoreChatOpts {smpServers, networkConfig, logLevel, logConnections, logServerHosts, tbqSize}, optFilesFolder, allowInstantFiles} sendToast = do
|
newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agentConfig = aCfg, defaultServers, inlineFiles} ChatOpts {coreOptions = CoreChatOpts {smpServers, networkConfig, logLevel, logConnections, logServerHosts, logFile, tbqSize}, optFilesFolder, allowInstantFiles} sendToast = do
|
||||||
let inlineFiles' = if allowInstantFiles then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False}
|
let inlineFiles' = if allowInstantFiles then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False}
|
||||||
config = cfg {logLevel, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, defaultServers = configServers, inlineFiles = inlineFiles'}
|
config = cfg {logLevel, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, defaultServers = configServers, inlineFiles = inlineFiles'}
|
||||||
sendNotification = fromMaybe (const $ pure ()) sendToast
|
sendNotification = fromMaybe (const $ pure ()) sendToast
|
||||||
@ -163,7 +163,7 @@ newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agen
|
|||||||
cleanupManagerAsync <- newTVarIO Nothing
|
cleanupManagerAsync <- newTVarIO Nothing
|
||||||
timedItemThreads <- atomically TM.empty
|
timedItemThreads <- atomically TM.empty
|
||||||
showLiveItems <- newTVarIO False
|
showLiveItems <- newTVarIO False
|
||||||
pure ChatController {activeTo, firstTime, currentUser, smpAgent, agentAsync, chatStore, chatStoreChanged, idsDrg, inputQ, outputQ, notifyQ, chatLock, sndFiles, rcvFiles, currentCalls, config, sendNotification, incognitoMode, filesFolder, expireCIThreads, expireCIFlags, cleanupManagerAsync, timedItemThreads, showLiveItems}
|
pure ChatController {activeTo, firstTime, currentUser, smpAgent, agentAsync, chatStore, chatStoreChanged, idsDrg, inputQ, outputQ, notifyQ, chatLock, sndFiles, rcvFiles, currentCalls, config, sendNotification, incognitoMode, filesFolder, expireCIThreads, expireCIFlags, cleanupManagerAsync, timedItemThreads, showLiveItems, logFilePath = logFile}
|
||||||
where
|
where
|
||||||
configServers :: DefaultAgentServers
|
configServers :: DefaultAgentServers
|
||||||
configServers =
|
configServers =
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE StrictData #-}
|
{-# LANGUAGE StrictData #-}
|
||||||
@ -165,7 +166,8 @@ data ChatController = ChatController
|
|||||||
expireCIFlags :: TMap UserId Bool,
|
expireCIFlags :: TMap UserId Bool,
|
||||||
cleanupManagerAsync :: TVar (Maybe (Async ())),
|
cleanupManagerAsync :: TVar (Maybe (Async ())),
|
||||||
timedItemThreads :: TMap (ChatRef, ChatItemId) (TVar (Maybe (Weak ThreadId))),
|
timedItemThreads :: TMap (ChatRef, ChatItemId) (TVar (Maybe (Weak ThreadId))),
|
||||||
showLiveItems :: TVar Bool
|
showLiveItems :: TVar Bool,
|
||||||
|
logFilePath :: Maybe FilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
data HelpSection = HSMain | HSFiles | HSGroups | HSMyAddress | HSMarkdown | HSMessages | HSSettings
|
data HelpSection = HSMain | HSFiles | HSGroups | HSMyAddress | HSMarkdown | HSMessages | HSSettings
|
||||||
@ -487,6 +489,25 @@ data ChatResponse
|
|||||||
| CRChatError {user_ :: Maybe User, chatError :: ChatError}
|
| CRChatError {user_ :: Maybe User, chatError :: ChatError}
|
||||||
deriving (Show, Generic)
|
deriving (Show, Generic)
|
||||||
|
|
||||||
|
logResponseToFile :: ChatResponse -> Bool
|
||||||
|
logResponseToFile = \case
|
||||||
|
CRContactsDisconnected {} -> True
|
||||||
|
CRContactsSubscribed {} -> True
|
||||||
|
CRContactSubError {} -> True
|
||||||
|
CRMemberSubError {} -> True
|
||||||
|
CRSndFileSubError {} -> True
|
||||||
|
CRRcvFileSubError {} -> True
|
||||||
|
CRHostConnected {} -> True
|
||||||
|
CRHostDisconnected {} -> True
|
||||||
|
CRConnectionDisabled {} -> True
|
||||||
|
CRAgentRcvQueueDeleted {} -> True
|
||||||
|
CRAgentConnDeleted {} -> True
|
||||||
|
CRAgentUserDeleted {} -> True
|
||||||
|
CRChatCmdError {} -> True
|
||||||
|
CRChatError {} -> True
|
||||||
|
CRMessageError {} -> True
|
||||||
|
_ -> False
|
||||||
|
|
||||||
instance ToJSON ChatResponse where
|
instance ToJSON ChatResponse where
|
||||||
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "CR"
|
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "CR"
|
||||||
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "CR"
|
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "CR"
|
||||||
|
@ -14,11 +14,12 @@ import Simplex.Chat.Types
|
|||||||
import UnliftIO.Async
|
import UnliftIO.Async
|
||||||
|
|
||||||
simplexChatCore :: ChatConfig -> ChatOpts -> Maybe (Notification -> IO ()) -> (User -> ChatController -> IO ()) -> IO ()
|
simplexChatCore :: ChatConfig -> ChatOpts -> Maybe (Notification -> IO ()) -> (User -> ChatController -> IO ()) -> IO ()
|
||||||
simplexChatCore cfg@ChatConfig {yesToMigrations} opts@ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, dbKey, logAgent}} sendToast chat
|
simplexChatCore cfg@ChatConfig {yesToMigrations} opts@ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, dbKey, logAgent}} sendToast chat =
|
||||||
| logAgent = do
|
case logAgent of
|
||||||
setLogLevel LogInfo -- LogError
|
Just level -> do
|
||||||
withGlobalLogging logCfg initRun
|
setLogLevel level
|
||||||
| otherwise = initRun
|
withGlobalLogging logCfg initRun
|
||||||
|
_ -> initRun
|
||||||
where
|
where
|
||||||
initRun = do
|
initRun = do
|
||||||
db@ChatDatabase {chatStore} <- createChatDatabase dbFilePrefix dbKey yesToMigrations
|
db@ChatDatabase {chatStore} <- createChatDatabase dbFilePrefix dbKey yesToMigrations
|
||||||
|
@ -134,7 +134,8 @@ mobileChatOpts dbFilePrefix dbKey =
|
|||||||
logLevel = CLLImportant,
|
logLevel = CLLImportant,
|
||||||
logConnections = False,
|
logConnections = False,
|
||||||
logServerHosts = True,
|
logServerHosts = True,
|
||||||
logAgent = False,
|
logAgent = Nothing,
|
||||||
|
logFile = Nothing,
|
||||||
tbqSize = 1024
|
tbqSize = 1024
|
||||||
},
|
},
|
||||||
chatCmd = "",
|
chatCmd = "",
|
||||||
|
@ -16,6 +16,7 @@ module Simplex.Chat.Options
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Control.Logger.Simple (LogLevel (..))
|
||||||
import qualified Data.Attoparsec.ByteString.Char8 as A
|
import qualified Data.Attoparsec.ByteString.Char8 as A
|
||||||
import qualified Data.ByteString.Char8 as B
|
import qualified Data.ByteString.Char8 as B
|
||||||
import Numeric.Natural (Natural)
|
import Numeric.Natural (Natural)
|
||||||
@ -46,10 +47,19 @@ data CoreChatOpts = CoreChatOpts
|
|||||||
logLevel :: ChatLogLevel,
|
logLevel :: ChatLogLevel,
|
||||||
logConnections :: Bool,
|
logConnections :: Bool,
|
||||||
logServerHosts :: Bool,
|
logServerHosts :: Bool,
|
||||||
logAgent :: Bool,
|
logAgent :: Maybe LogLevel,
|
||||||
|
logFile :: Maybe FilePath,
|
||||||
tbqSize :: Natural
|
tbqSize :: Natural
|
||||||
}
|
}
|
||||||
|
|
||||||
|
agentLogLevel :: ChatLogLevel -> LogLevel
|
||||||
|
agentLogLevel = \case
|
||||||
|
CLLDebug -> LogDebug
|
||||||
|
CLLInfo -> LogInfo
|
||||||
|
CLLWarning -> LogWarn
|
||||||
|
CLLError -> LogError
|
||||||
|
CLLImportant -> LogInfo
|
||||||
|
|
||||||
coreChatOptsP :: FilePath -> FilePath -> Parser CoreChatOpts
|
coreChatOptsP :: FilePath -> FilePath -> Parser CoreChatOpts
|
||||||
coreChatOptsP appDir defaultDbFileName = do
|
coreChatOptsP appDir defaultDbFileName = do
|
||||||
dbFilePrefix <-
|
dbFilePrefix <-
|
||||||
@ -125,6 +135,12 @@ coreChatOptsP appDir defaultDbFileName = do
|
|||||||
( long "log-agent"
|
( long "log-agent"
|
||||||
<> help "Enable logs from SMP agent (also with `-l debug`)"
|
<> help "Enable logs from SMP agent (also with `-l debug`)"
|
||||||
)
|
)
|
||||||
|
logFile <-
|
||||||
|
optional $
|
||||||
|
strOption
|
||||||
|
( long "log-file"
|
||||||
|
<> help "Log to specified file / device"
|
||||||
|
)
|
||||||
tbqSize <-
|
tbqSize <-
|
||||||
option
|
option
|
||||||
auto
|
auto
|
||||||
@ -144,7 +160,8 @@ coreChatOptsP appDir defaultDbFileName = do
|
|||||||
logLevel,
|
logLevel,
|
||||||
logConnections = logConnections || logLevel <= CLLInfo,
|
logConnections = logConnections || logLevel <= CLLInfo,
|
||||||
logServerHosts = logServerHosts || logLevel <= CLLInfo,
|
logServerHosts = logServerHosts || logLevel <= CLLInfo,
|
||||||
logAgent = logAgent || logLevel == CLLDebug,
|
logAgent = if logAgent || logLevel == CLLDebug then Just $ agentLogLevel logLevel else Nothing,
|
||||||
|
logFile,
|
||||||
tbqSize
|
tbqSize
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
|
@ -19,6 +19,7 @@ import Simplex.Chat.Messages hiding (NewChatItem (..))
|
|||||||
import Simplex.Chat.Styled
|
import Simplex.Chat.Styled
|
||||||
import Simplex.Chat.View
|
import Simplex.Chat.View
|
||||||
import System.Console.ANSI.Types
|
import System.Console.ANSI.Types
|
||||||
|
import System.IO (IOMode (..), hPutStrLn, withFile)
|
||||||
import System.Mem.Weak (Weak)
|
import System.Mem.Weak (Weak)
|
||||||
import System.Terminal
|
import System.Terminal
|
||||||
import System.Terminal.Internal (LocalTerminal, Terminal, VirtualTerminal)
|
import System.Terminal.Internal (LocalTerminal, Terminal, VirtualTerminal)
|
||||||
@ -91,17 +92,19 @@ withTermLock ChatTerminal {termLock} action = do
|
|||||||
atomically $ putTMVar termLock ()
|
atomically $ putTMVar termLock ()
|
||||||
|
|
||||||
runTerminalOutput :: ChatTerminal -> ChatController -> IO ()
|
runTerminalOutput :: ChatTerminal -> ChatController -> IO ()
|
||||||
runTerminalOutput ct cc@ChatController {outputQ, showLiveItems} = do
|
runTerminalOutput ct cc@ChatController {outputQ, showLiveItems, logFilePath} = do
|
||||||
forever $ do
|
forever $ do
|
||||||
(_, r) <- atomically $ readTBQueue outputQ
|
(_, r) <- atomically $ readTBQueue outputQ
|
||||||
case r of
|
case r of
|
||||||
CRNewChatItem _ ci -> markChatItemRead ci
|
CRNewChatItem _ ci -> markChatItemRead ci
|
||||||
CRChatItemUpdated _ ci -> markChatItemRead ci
|
CRChatItemUpdated _ ci -> markChatItemRead ci
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
let printResp = case logFilePath of
|
||||||
|
Just path -> if logResponseToFile r then logResponse path else printToTerminal ct
|
||||||
|
_ -> printToTerminal ct
|
||||||
liveItems <- readTVarIO showLiveItems
|
liveItems <- readTVarIO showLiveItems
|
||||||
printRespToTerminal ct cc liveItems r
|
responseString cc liveItems r >>= printResp
|
||||||
where
|
where
|
||||||
markChatItemRead :: AChatItem -> IO ()
|
|
||||||
markChatItemRead (AChatItem _ _ chat item@ChatItem {meta = CIMeta {itemStatus}}) =
|
markChatItemRead (AChatItem _ _ chat item@ChatItem {meta = CIMeta {itemStatus}}) =
|
||||||
case (muted chat item, itemStatus) of
|
case (muted chat item, itemStatus) of
|
||||||
(False, CISRcvNew) -> do
|
(False, CISRcvNew) -> do
|
||||||
@ -109,12 +112,16 @@ runTerminalOutput ct cc@ChatController {outputQ, showLiveItems} = do
|
|||||||
chatRef = chatInfoToRef chat
|
chatRef = chatInfoToRef chat
|
||||||
void $ runReaderT (runExceptT $ processChatCommand (APIChatRead chatRef (Just (itemId, itemId)))) cc
|
void $ runReaderT (runExceptT $ processChatCommand (APIChatRead chatRef (Just (itemId, itemId)))) cc
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
logResponse path s = withFile path AppendMode $ \h -> mapM_ (hPutStrLn h . unStyle) s
|
||||||
|
|
||||||
printRespToTerminal :: ChatTerminal -> ChatController -> Bool -> ChatResponse -> IO ()
|
printRespToTerminal :: ChatTerminal -> ChatController -> Bool -> ChatResponse -> IO ()
|
||||||
printRespToTerminal ct cc liveItems r = do
|
printRespToTerminal ct cc liveItems r = responseString cc liveItems r >>= printToTerminal ct
|
||||||
|
|
||||||
|
responseString :: ChatController -> Bool -> ChatResponse -> IO [StyledString]
|
||||||
|
responseString cc liveItems r = do
|
||||||
user <- readTVarIO $ currentUser cc
|
user <- readTVarIO $ currentUser cc
|
||||||
ts <- getCurrentTime
|
ts <- getCurrentTime
|
||||||
printToTerminal ct $ responseToView user (config cc) liveItems ts r
|
pure $ responseToView user (config cc) liveItems ts r
|
||||||
|
|
||||||
printToTerminal :: ChatTerminal -> [StyledString] -> IO ()
|
printToTerminal :: ChatTerminal -> [StyledString] -> IO ()
|
||||||
printToTerminal ct s =
|
printToTerminal ct s =
|
||||||
|
@ -59,8 +59,9 @@ testOpts =
|
|||||||
logLevel = CLLImportant,
|
logLevel = CLLImportant,
|
||||||
logConnections = False,
|
logConnections = False,
|
||||||
logServerHosts = False,
|
logServerHosts = False,
|
||||||
logAgent = False,
|
logAgent = Nothing,
|
||||||
tbqSize = 64
|
logFile = Nothing,
|
||||||
|
tbqSize = 16
|
||||||
},
|
},
|
||||||
chatCmd = "",
|
chatCmd = "",
|
||||||
chatCmdDelay = 3,
|
chatCmdDelay = 3,
|
||||||
@ -91,7 +92,7 @@ data TestCC = TestCC
|
|||||||
}
|
}
|
||||||
|
|
||||||
aCfg :: AgentConfig
|
aCfg :: AgentConfig
|
||||||
aCfg = agentConfig defaultChatConfig
|
aCfg = (agentConfig defaultChatConfig) {tbqSize = 16}
|
||||||
|
|
||||||
testAgentCfg :: AgentConfig
|
testAgentCfg :: AgentConfig
|
||||||
testAgentCfg = aCfg {reconnectInterval = (reconnectInterval aCfg) {initialInterval = 50000}}
|
testAgentCfg = aCfg {reconnectInterval = (reconnectInterval aCfg) {initialInterval = 50000}}
|
||||||
@ -100,7 +101,8 @@ testCfg :: ChatConfig
|
|||||||
testCfg =
|
testCfg =
|
||||||
defaultChatConfig
|
defaultChatConfig
|
||||||
{ agentConfig = testAgentCfg,
|
{ agentConfig = testAgentCfg,
|
||||||
testView = True
|
testView = True,
|
||||||
|
tbqSize = 16
|
||||||
}
|
}
|
||||||
|
|
||||||
testAgentCfgV1 :: AgentConfig
|
testAgentCfgV1 :: AgentConfig
|
||||||
|
Loading…
Reference in New Issue
Block a user