Skip to content

Commit a17155f

Browse files
committed
cardano-tracer: Add functionality to run cardano-tracer as a library, with shut-down functionality and internal/user messaging.
1 parent 0f6b522 commit a17155f

File tree

15 files changed

+352
-111
lines changed

15 files changed

+352
-111
lines changed
Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
import Cardano.Tracer.CLI (TracerParams, parseTracerParams)
1+
{-# LANGUAGE OverloadedRecordDot #-}
2+
3+
import Cardano.Tracer.CLI (TracerParams(..), parseTracerParams)
4+
import Cardano.Tracer.MetaTrace
25
import Cardano.Tracer.Run (runCardanoTracer)
36

7+
import Data.Functor (void)
48
import Data.Version (showVersion)
59
import Options.Applicative
610

711
import Paths_cardano_tracer (version)
812

913
main :: IO ()
10-
main =
11-
runCardanoTracer =<< customExecParser (prefs showHelpOnEmpty) tracerInfo
14+
main = void do
15+
tracerParams :: TracerParams
16+
<- customExecParser (prefs showHelpOnEmpty) tracerInfo
17+
trace :: Trace IO TracerTrace <-
18+
-- Default `Nothing' severity filter to Info.
19+
mkTracerTracer $ SeverityF (tracerParams.logSeverity <|> Just Info)
20+
runCardanoTracer trace tracerParams
1221

1322
tracerInfo :: ParserInfo TracerParams
1423
tracerInfo = info
@@ -21,7 +30,9 @@ tracerInfo = info
2130

2231
versionOption :: Parser (a -> a)
2332
versionOption = infoOption
24-
(showVersion version)
25-
(long "version" <>
26-
short 'v' <>
27-
help "Show version")
33+
do showVersion version
34+
do mconcat
35+
[ long "version"
36+
, short 'v'
37+
, help "Show version"
38+
]

cardano-tracer/cardano-tracer.cabal

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ library
200200
, trace-dispatcher ^>= 2.10.0
201201
, trace-forward ^>= 2.3.0
202202
, trace-resources ^>= 0.2.3
203+
, unagi-chan
203204
, wai ^>= 3.2
204205
, warp ^>= 3.4
205206
, yaml
@@ -294,6 +295,7 @@ library demo-acceptor-lib
294295
exposed-modules: Cardano.Tracer.Test.Acceptor
295296

296297
build-depends: bytestring
298+
, QuickCheck
297299
, cardano-tracer
298300
, containers
299301
, extra
@@ -306,9 +308,9 @@ library demo-acceptor-lib
306308
, text
307309
, trace-dispatcher
308310
, trace-forward
311+
, unagi-chan
309312
, vector
310313
, vector-algorithms
311-
, QuickCheck
312314

313315
executable demo-acceptor
314316
import: project-config

cardano-tracer/src/Cardano/Tracer/Acceptors/Run.hs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE LambdaCase #-}
12
{-# LANGUAGE NamedFieldPuns #-}
23
{-# LANGUAGE PackageImports #-}
34

@@ -14,6 +15,7 @@ import Cardano.Tracer.Utils
1415
import Cardano.Logging.Types (TraceObject)
1516
import qualified Cardano.Logging.Types as Net
1617

18+
import Control.Concurrent.Chan.Unagi (dupChan)
1719
import Control.Concurrent.Async (forConcurrently_)
1820
import "contra-tracer" Control.Tracer (Tracer, contramap, nullTracer, stdoutTracer)
1921
import qualified Data.List.NonEmpty as NE
@@ -33,20 +35,28 @@ import qualified Trace.Forward.Protocol.TraceObject.Type as TOF
3335
-- 1. Server mode, when the tracer accepts connections from any number of nodes.
3436
-- 2. Client mode, when the tracer initiates connections to specified number of nodes.
3537
runAcceptors :: TracerEnv -> TracerEnvRTView -> IO ()
36-
runAcceptors tracerEnv@TracerEnv{teTracer} tracerEnvRTView = do
38+
runAcceptors tracerEnv@TracerEnv{teTracer, teInChan = inChan} tracerEnvRTView = do
3739
traceWith teTracer $ TracerStartedAcceptors network
3840
case network of
39-
AcceptAt howToConnect ->
41+
AcceptAt howToConnect -> let
4042
-- Run one server that accepts connections from the nodes.
41-
runInLoop
42-
(runAcceptorsServer tracerEnv tracerEnvRTView howToConnect $ acceptorsConfigs (Net.howToConnectString howToConnect))
43-
verbosity howToConnect initialPauseInSec
44-
ConnectTo localSocks ->
43+
44+
action :: IO ()
45+
action = do
46+
dieOnShutdown =<< dupChan inChan
47+
runAcceptorsServer tracerEnv tracerEnvRTView howToConnect $ acceptorsConfigs (Net.howToConnectString howToConnect)
48+
49+
in runInLoop action verbosity howToConnect initialPauseInSec
50+
ConnectTo localSocks -> do
4551
-- Run N clients that initiate connections to the nodes.
46-
forConcurrently_ (NE.nub localSocks) \howToConnect ->
47-
runInLoop
48-
(runAcceptorsClient tracerEnv tracerEnvRTView howToConnect $ acceptorsConfigs (Net.howToConnectString howToConnect))
49-
verbosity howToConnect initialPauseInSec
52+
forConcurrently_ (NE.nub localSocks) \howToConnect -> let
53+
54+
action :: IO ()
55+
action = runAcceptorsClient tracerEnv tracerEnvRTView howToConnect $ acceptorsConfigs (Net.howToConnectString howToConnect)
56+
57+
in do
58+
dieOnShutdown =<< dupChan inChan
59+
runInLoop action verbosity howToConnect initialPauseInSec
5060
where
5161
TracerConfig{network, ekgRequestFreq, verbosity, ekgRequestFull} = teConfig tracerEnv
5262
ekgUseFullRequests = fromMaybe False ekgRequestFull

cardano-tracer/src/Cardano/Tracer/Environment.hs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE GADTs #-}
3+
{-# LANGUAGE LambdaCase #-}
24

35
module Cardano.Tracer.Environment
46
( TracerEnv (..)
57
, TracerEnvRTView (..)
8+
, ChanMessage (..)
9+
, InternalMessage (..)
10+
, Tag (..)
11+
, CardanoTracerMessage
12+
, onChanMessage
13+
, onInternal
14+
, onUser
15+
, dieOnShutdown
16+
, forever'tilShutdown
617
) where
718

819
import Cardano.Logging.Types
20+
import Cardano.Logging.Resources.Types (ResourceStats)
921
import Cardano.Tracer.Configuration
1022
#if RTVIEW
1123
import Cardano.Tracer.Handlers.Notifications.Types
@@ -16,10 +28,13 @@ import Cardano.Tracer.Handlers.State.TraceObjects
1628
import Cardano.Tracer.MetaTrace
1729
import Cardano.Tracer.Types
1830

31+
import Control.Concurrent (myThreadId)
32+
import Control.Exception (AsyncException(ThreadKilled), throwTo)
33+
import Control.Concurrent.Chan.Unagi (InChan, OutChan, readChan)
1934
import Control.Concurrent.Extra (Lock)
2035
import Data.Text (Text)
2136
import Data.Text.Lazy.Builder (Builder)
22-
37+
import Data.Kind (Type)
2338

2439
-- | Environment for all functions.
2540
data TracerEnv = TracerEnv
@@ -36,6 +51,7 @@ data TracerEnv = TracerEnv
3651
, teRegistry :: !HandleRegistry
3752
, teStateDir :: !(Maybe FilePath)
3853
, teMetricsHelp :: ![(Text, Builder)]
54+
, teInChan :: !(InChan (CardanoTracerMessage ()))
3955
}
4056

4157
#if RTVIEW
@@ -51,3 +67,39 @@ data TracerEnvRTView = TracerEnvRTView
5167
#else
5268
data TracerEnvRTView = TracerEnvRTView
5369
#endif
70+
71+
type CardanoTracerMessage userMsg = ChanMessage InternalMessage userMsg
72+
73+
type ChanMessage :: Type -> Type -> Type
74+
data ChanMessage internal user
75+
= Shutdown
76+
| InternalMessage internal
77+
| UserMessage user
78+
79+
onChanMessage :: (internal -> IO ()) -> (user -> IO ()) -> OutChan (ChanMessage internal user) -> IO ()
80+
onChanMessage internalAction userAction outChan =
81+
readChan outChan >>= \case
82+
Shutdown -> myThreadId >>= (`throwTo` ThreadKilled)
83+
InternalMessage internal -> internalAction internal
84+
UserMessage user -> userAction user
85+
86+
onInternal :: (internal -> IO ()) -> OutChan (ChanMessage internal user) -> IO ()
87+
onInternal = (`onChanMessage` mempty)
88+
89+
onUser :: (user -> IO ()) -> OutChan (ChanMessage internal user) -> IO ()
90+
onUser = (mempty `onChanMessage`)
91+
92+
data InternalMessage where
93+
ResourceMessage :: Tag ex -> (ex -> IO ()) -> InternalMessage
94+
95+
data Tag a where
96+
TagResource :: Tag (ResourceStats, Trace IO TracerTrace)
97+
98+
dieOnShutdown :: OutChan (ChanMessage internal user) -> IO ()
99+
dieOnShutdown = onChanMessage mempty mempty
100+
101+
forever'tilShutdown :: OutChan (ChanMessage internal user) -> IO () -> IO ()
102+
forever'tilShutdown outChan action = do
103+
readChan outChan >>= \case
104+
Shutdown -> pure ()
105+
_ -> action *> forever'tilShutdown outChan action

cardano-tracer/src/Cardano/Tracer/Handlers/Logs/Rotator.hs

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ module Cardano.Tracer.Handlers.Logs.Rotator
66
) where
77

88
import Cardano.Tracer.Configuration
9-
import Cardano.Tracer.Environment
9+
import Cardano.Tracer.Environment (TracerEnv (..), forever'tilShutdown)
1010
import Cardano.Tracer.Handlers.Logs.Utils (createOrUpdateEmptyLog, getTimeStampFromLog,
1111
isItLog)
1212
import Cardano.Tracer.MetaTrace
1313
import Cardano.Tracer.Types (HandleRegistry, HandleRegistryKey, NodeName)
1414
import Cardano.Tracer.Utils (showProblemIfAny, readRegistry)
1515

1616
import Control.Concurrent.Async (forConcurrently_)
17+
import Control.Concurrent.Chan.Unagi (dupChan)
1718
import Control.Concurrent.Extra (Lock)
18-
import Control.Monad (forM_, forever, unless, when)
19+
import Control.Monad (forM_, unless, when)
1920
import Control.Monad.Extra (whenJust, whenM)
2021
import Data.Foldable (for_)
2122
import Data.List (nub, sort)
@@ -33,38 +34,40 @@ import System.Time.Extra (sleep)
3334

3435
-- | Runs rotation mechanism for the log files.
3536
runLogsRotator :: TracerEnv -> IO ()
36-
runLogsRotator TracerEnv
37-
{ teConfig = TracerConfig{rotation, verbosity, logging}
38-
, teCurrentLogLock
39-
, teTracer
40-
, teRegistry
41-
} = do
42-
whenJust rotation \rotParams -> do
37+
runLogsRotator tracerEnv@TracerEnv { teConfig = TracerConfig{rotation}, teTracer } = do
38+
whenJust rotation \rot -> do
4339
traceWith teTracer TracerStartedLogRotator
44-
launchRotator loggingParamsForFiles rotParams verbosity teRegistry teCurrentLogLock
45-
where
40+
launchRotator tracerEnv rot
41+
42+
launchRotator
43+
:: TracerEnv
44+
-> RotationParams
45+
-> IO ()
46+
launchRotator tracerEnv rot@RotationParams{rpFrequencySecs} = do
47+
whenNonEmpty loggingParamsForFiles do
48+
outChan <- dupChan teInChan
49+
forever'tilShutdown outChan do
50+
showProblemIfAny verbosity do
51+
forM_ loggingParamsForFiles \loggingParam -> do
52+
checkRootDir teCurrentLogLock teRegistry rot loggingParam
53+
sleep (fromIntegral rpFrequencySecs)
54+
where
55+
whenNonEmpty :: Applicative f => [a] -> f () -> f ()
56+
whenNonEmpty = unless . null
57+
58+
TracerEnv
59+
{ teConfig = TracerConfig{verbosity, logging}
60+
, teCurrentLogLock
61+
, teRegistry
62+
, teInChan
63+
} = tracerEnv
64+
4665
loggingParamsForFiles :: [LoggingParams]
4766
loggingParamsForFiles = nub (NE.filter filesOnly logging)
4867

4968
filesOnly :: LoggingParams -> Bool
5069
filesOnly LoggingParams{logMode} = logMode == FileMode
5170

52-
launchRotator
53-
:: [LoggingParams]
54-
-> RotationParams
55-
-> Maybe Verbosity
56-
-> HandleRegistry
57-
-> Lock
58-
-> IO ()
59-
launchRotator [] _ _ _ _ = return ()
60-
launchRotator loggingParamsForFiles
61-
rotParams@RotationParams{rpFrequencySecs} verb registry currentLogLock =
62-
forever do
63-
showProblemIfAny verb do
64-
forM_ loggingParamsForFiles \loggingParam -> do
65-
checkRootDir currentLogLock registry rotParams loggingParam
66-
sleep $ fromIntegral rpFrequencySecs
67-
6871
-- | All the logs with 'TraceObject's received from particular node
6972
-- will be stored in a separate subdirectory in the root directory.
7073
--

cardano-tracer/src/Cardano/Tracer/Handlers/Metrics/Monitoring.hs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE LambdaCase #-}
12
{-# LANGUAGE NamedFieldPuns #-}
23
{-# LANGUAGE OverloadedStrings #-}
34
{-# LANGUAGE ScopedTypeVariables #-}
@@ -14,6 +15,9 @@ import Cardano.Tracer.Types
1415

1516
import Prelude hiding (head)
1617

18+
import Control.Concurrent (myThreadId)
19+
import Control.Concurrent.Chan.Unagi (OutChan, readChan, dupChan)
20+
import Control.Exception (AsyncException(ThreadKilled), throwTo)
1721
import Data.ByteString as ByteString (ByteString, isInfixOf)
1822
import Data.ByteString.Builder (stringUtf8)
1923
import qualified Data.Text as T
@@ -39,19 +43,24 @@ runMonitoringServer
3943
-> Endpoint -- ^ (web page with list of connected nodes, EKG web page).
4044
-> IO RouteDictionary
4145
-> IO ()
42-
runMonitoringServer TracerEnv{teTracer} endpoint computeRoutes_autoUpdate = do
46+
runMonitoringServer TracerEnv{teTracer, teInChan = inChan} endpoint computeRoutes_autoUpdate = do
4347
-- Pause to prevent collision between "Listening"-notifications from servers.
4448
sleep 0.2
4549
traceWith teTracer TracerStartedMonitoring
4650
{ ttMonitoringEndpoint = endpoint
4751
, ttMonitoringType = "list"
4852
}
4953
dummyStore <- EKG.newStore
50-
runSettings (setEndpoint endpoint defaultSettings) do
51-
renderEkg dummyStore computeRoutes_autoUpdate
54+
runSettings (setEndpoint endpoint defaultSettings) \req respond -> do
55+
outChan <- dupChan inChan
56+
renderEkg dummyStore outChan computeRoutes_autoUpdate req respond
57+
58+
renderEkg :: EKG.Store -> OutChan (CardanoTracerMessage ()) -> IO RouteDictionary -> Application
59+
renderEkg dummyStore outChan computeRoutes_autoUpdate request send = do
60+
readChan outChan >>= \case
61+
Shutdown -> myThreadId >>= (`throwTo` ThreadKilled)
62+
_ -> pure ()
5263

53-
renderEkg :: EKG.Store -> IO RouteDictionary -> Application
54-
renderEkg dummyStore computeRoutes_autoUpdate request send = do
5564
routeDictionary :: RouteDictionary <-
5665
computeRoutes_autoUpdate
5766

cardano-tracer/src/Cardano/Tracer/Handlers/Metrics/Prometheus.hs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE LambdaCase #-}
12
{-# LANGUAGE NamedFieldPuns #-}
23
{-# LANGUAGE OverloadedStrings #-}
34
{-# LANGUAGE ScopedTypeVariables #-}
@@ -14,6 +15,9 @@ import Cardano.Tracer.MetaTrace
1415

1516
import Prelude hiding (head)
1617

18+
import Control.Concurrent (myThreadId)
19+
import Control.Concurrent.Chan.Unagi (OutChan, readChan, dupChan)
20+
import Control.Exception (AsyncException(ThreadKilled), throwTo)
1721
import qualified Data.ByteString as ByteString
1822
import Data.ByteString.Builder (stringUtf8)
1923
import Data.Functor ((<&>))
@@ -67,26 +71,33 @@ runPrometheusServer tracerEnv endpoint computeRoutes_autoUpdate = do
6771
-- If everything is okay, the function 'simpleHttpServe' never returns.
6872
-- But if there is some problem, it never throws an exception, but just stops.
6973
-- So if it stopped - it will be re-started.
70-
traceWith teTracer TracerStartedPrometheus
74+
traceWith tracer TracerStartedPrometheus
7175
{ ttPrometheusEndpoint = endpoint
7276
}
73-
runSettings (setEndpoint endpoint defaultSettings) do
74-
renderPrometheus computeRoutes_autoUpdate noSuffix teMetricsHelp
77+
runSettings (setEndpoint endpoint defaultSettings) \req respond -> do
78+
outChan <- dupChan inChan
79+
renderPrometheus computeRoutes_autoUpdate outChan noSuffix metricsHelp req respond
7580
where
7681
TracerEnv
77-
{ teTracer
78-
, teConfig = TracerConfig { metricsNoSuffix }
79-
, teMetricsHelp
82+
{ teTracer = tracer
83+
, teConfig = TracerConfig { metricsNoSuffix }
84+
, teMetricsHelp = metricsHelp
85+
, teInChan = inChan
8086
} = tracerEnv
8187

8288
noSuffix = or @Maybe metricsNoSuffix
8389

8490
renderPrometheus
8591
:: IO RouteDictionary
92+
-> OutChan (CardanoTracerMessage ())
8693
-> Bool
8794
-> [(Text, Builder)]
8895
-> Application
89-
renderPrometheus computeRoutes_autoUpdate noSuffix helpTextDict request send = do
96+
renderPrometheus computeRoutes_autoUpdate outChan noSuffix helpTextDict request send = do
97+
readChan outChan >>= \case
98+
Shutdown -> myThreadId >>= (`throwTo` ThreadKilled)
99+
_ -> pure ()
100+
90101
routeDictionary :: RouteDictionary <-
91102
computeRoutes_autoUpdate
92103

cardano-tracer/src/Cardano/Tracer/Handlers/Notifications/Timer.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,5 @@ mkTimerOnFailure onFailure tracer io state callPeriod_sec = do
110110
, startTimer = modifyIORef' isRunning (const True)
111111
, stopTimer = modifyIORef' isRunning (const False)
112112
}
113+
114+
-- TODO: Store last timestamp

0 commit comments

Comments
 (0)