Skip to content

Commit b2d2d68

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

File tree

12 files changed

+250
-100
lines changed

12 files changed

+250
-100
lines changed

cardano-tracer/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# ChangeLog
22

3+
## NEXT
4+
5+
* Cardano-tracer library functionality, allows shutting down and sending signals to running
6+
instances through channels.
7+
38
## 0.3.4 (July, 2025)
49
* Forwarding protocol supports connections over TCP socket, in addition to Unix domain sockets.
510

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/bench/cardano-tracer-bench.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import Control.Concurrent.Extra (newLock)
1919
#if RTVIEW
2020
import Control.Concurrent.STM.TVar (newTVarIO)
2121
#endif
22+
import Control.Concurrent.Chan.Unagi (newChan)
2223
import Control.DeepSeq
2324
import qualified Data.List.NonEmpty as NE
2425
import Data.Time.Clock (UTCTime, getCurrentTime)
@@ -63,6 +64,8 @@ main = do
6364

6465
tracer <- mkTracerTracer $ SeverityF $ Just Warning
6566

67+
(inChan, _outChan) <- newChan
68+
6669
let tracerEnv :: TracerConfig -> HandleRegistry -> TracerEnv
6770
tracerEnv config handleRegistry = TracerEnv
6871
{ teConfig = config
@@ -74,6 +77,7 @@ main = do
7477
, teDPRequestors = dpRequestors
7578
, teProtocolsBrake = protocolsBrake
7679
, teTracer = tracer
80+
, teInChan = inChan
7781
, teReforwardTraceObjects = \_-> pure ()
7882
, teRegistry = handleRegistry
7983
, teStateDir = Nothing

cardano-tracer/cardano-tracer.cabal

Lines changed: 5 additions & 2 deletions
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
@@ -452,12 +454,13 @@ benchmark cardano-tracer-bench
452454
build-depends: stm <2.5.2 || >=2.5.3
453455
build-depends: cardano-tracer
454456
, criterion
455-
, directory
456457
, deepseq
458+
, directory
457459
, extra
458460
, filepath
459461
, time
460462
, trace-dispatcher
463+
, unagi-chan
461464

462465
ghc-options: -threaded
463466
-rtsopts

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

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Cardano.Tracer.Utils
1414
import Cardano.Logging.Types (TraceObject)
1515
import qualified Cardano.Logging.Types as Net
1616

17+
import Control.Concurrent.Chan.Unagi (dupChan)
1718
import Control.Concurrent.Async (forConcurrently_)
1819
import "contra-tracer" Control.Tracer (Tracer, contramap, nullTracer, stdoutTracer)
1920
import qualified Data.List.NonEmpty as NE
@@ -33,20 +34,28 @@ import qualified Trace.Forward.Protocol.TraceObject.Type as TOF
3334
-- 1. Server mode, when the tracer accepts connections from any number of nodes.
3435
-- 2. Client mode, when the tracer initiates connections to specified number of nodes.
3536
runAcceptors :: TracerEnv -> TracerEnvRTView -> IO ()
36-
runAcceptors tracerEnv@TracerEnv{teTracer} tracerEnvRTView = do
37+
runAcceptors tracerEnv@TracerEnv{teTracer, teInChan = inChan} tracerEnvRTView = do
3738
traceWith teTracer $ TracerStartedAcceptors network
3839
case network of
39-
AcceptAt howToConnect ->
40+
AcceptAt howToConnect -> let
4041
-- 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 ->
42+
43+
action :: IO ()
44+
action = do
45+
dieOnShutdown =<< dupChan inChan
46+
runAcceptorsServer tracerEnv tracerEnvRTView howToConnect $ acceptorsConfigs (Net.howToConnectString howToConnect)
47+
48+
in runInLoop action verbosity howToConnect initialPauseInSec
49+
ConnectTo localSocks -> do
4550
-- 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
51+
forConcurrently_ (NE.nub localSocks) \howToConnect -> let
52+
53+
action :: IO ()
54+
action = runAcceptorsClient tracerEnv tracerEnvRTView howToConnect $ acceptorsConfigs (Net.howToConnectString howToConnect)
55+
56+
in do
57+
dieOnShutdown =<< dupChan inChan
58+
runInLoop action verbosity howToConnect initialPauseInSec
5059
where
5160
TracerConfig{network, ekgRequestFreq, verbosity, ekgRequestFull} = teConfig tracerEnv
5261
ekgUseFullRequests = fromMaybe False ekgRequestFull

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

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

35
module Cardano.Tracer.Environment
46
( TracerEnv (..)
57
, TracerEnvRTView (..)
8+
, RawMessage (..)
9+
, InternalMessage (..)
10+
, Tag (..)
11+
, CardanoTracerMessage
12+
, onRawMessage
13+
, onInternal
14+
, onUser
15+
, blockUntilShutdown
16+
, dieOnShutdown
17+
, forever'tilShutdown
618
) where
719

820
import Cardano.Logging.Types
21+
import Cardano.Logging.Resources.Types (ResourceStats)
922
import Cardano.Tracer.Configuration
1023
#if RTVIEW
1124
import Cardano.Tracer.Handlers.Notifications.Types
@@ -16,10 +29,13 @@ import Cardano.Tracer.Handlers.State.TraceObjects
1629
import Cardano.Tracer.MetaTrace
1730
import Cardano.Tracer.Types
1831

32+
import Control.Concurrent (myThreadId)
33+
import Control.Exception (AsyncException(ThreadKilled), throwTo)
34+
import Control.Concurrent.Chan.Unagi (InChan, OutChan, readChan)
1935
import Control.Concurrent.Extra (Lock)
2036
import Data.Text (Text)
2137
import Data.Text.Lazy.Builder (Builder)
22-
38+
import Data.Kind (Type)
2339

2440
-- | Environment for all functions.
2541
data TracerEnv = TracerEnv
@@ -36,6 +52,7 @@ data TracerEnv = TracerEnv
3652
, teRegistry :: !HandleRegistry
3753
, teStateDir :: !(Maybe FilePath)
3854
, teMetricsHelp :: ![(Text, Builder)]
55+
, teInChan :: !(InChan (CardanoTracerMessage ()))
3956
}
4057

4158
#if RTVIEW
@@ -51,3 +68,46 @@ data TracerEnvRTView = TracerEnvRTView
5168
#else
5269
data TracerEnvRTView = TracerEnvRTView
5370
#endif
71+
72+
type CardanoTracerMessage userMsg = RawMessage InternalMessage userMsg
73+
74+
type RawMessage :: Type -> Type -> Type
75+
data RawMessage internal user
76+
= Shutdown
77+
| InternalMessage internal
78+
| UserMessage user
79+
80+
blockUntilShutdown :: OutChan (RawMessage internal user) -> IO ()
81+
blockUntilShutdown outChan = go where
82+
go :: IO ()
83+
go = readChan outChan >>= \case
84+
Shutdown -> pure ()
85+
_ -> go
86+
87+
onRawMessage :: (internal -> IO ()) -> (user -> IO ()) -> OutChan (RawMessage internal user) -> IO ()
88+
onRawMessage internalAction userAction outChan =
89+
readChan outChan >>= \case
90+
Shutdown -> myThreadId >>= (`throwTo` ThreadKilled)
91+
InternalMessage internal -> internalAction internal
92+
UserMessage user -> userAction user
93+
94+
onInternal :: (internal -> IO ()) -> OutChan (RawMessage internal user) -> IO ()
95+
onInternal = (`onRawMessage` mempty)
96+
97+
onUser :: (user -> IO ()) -> OutChan (RawMessage internal user) -> IO ()
98+
onUser = (mempty `onRawMessage`)
99+
100+
data InternalMessage where
101+
ResourceMessage :: Tag ex -> (ex -> IO ()) -> InternalMessage
102+
103+
data Tag a where
104+
TagResource :: Tag (ResourceStats, Trace IO TracerTrace)
105+
106+
dieOnShutdown :: OutChan (RawMessage internal user) -> IO ()
107+
dieOnShutdown = onRawMessage mempty mempty
108+
109+
forever'tilShutdown :: OutChan (RawMessage internal user) -> IO () -> IO ()
110+
forever'tilShutdown outChan action = do
111+
readChan outChan >>= \case
112+
Shutdown -> pure ()
113+
_ -> 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
@@ -14,6 +14,8 @@ import Cardano.Tracer.Types
1414

1515
import Prelude hiding (head)
1616

17+
import Control.Concurrent.Async (race_)
18+
import Control.Concurrent.Chan.Unagi (OutChan, dupChan)
1719
import Data.ByteString as ByteString (ByteString, isInfixOf)
1820
import Data.ByteString.Builder (stringUtf8)
1921
import qualified Data.Text as T
@@ -39,19 +41,26 @@ runMonitoringServer
3941
-> Endpoint -- ^ (web page with list of connected nodes, EKG web page).
4042
-> IO RouteDictionary
4143
-> IO ()
42-
runMonitoringServer TracerEnv{teTracer} endpoint computeRoutes_autoUpdate = do
44+
runMonitoringServer TracerEnv{teTracer, teInChan = inChan} endpoint computeRoutes_autoUpdate = do
4345
-- Pause to prevent collision between "Listening"-notifications from servers.
4446
sleep 0.2
4547
traceWith teTracer TracerStartedMonitoring
4648
{ ttMonitoringEndpoint = endpoint
4749
, ttMonitoringType = "list"
4850
}
4951
dummyStore <- EKG.newStore
50-
runSettings (setEndpoint endpoint defaultSettings) do
51-
renderEkg dummyStore computeRoutes_autoUpdate
52+
outChan <- dupChan inChan
53+
54+
let run :: IO ()
55+
run = runSettings (setEndpoint endpoint defaultSettings) $
56+
renderEkg dummyStore outChan computeRoutes_autoUpdate
57+
58+
race_ run (blockUntilShutdown outChan)
59+
60+
renderEkg :: EKG.Store -> OutChan (CardanoTracerMessage ()) -> IO RouteDictionary -> Application
61+
renderEkg dummyStore outChan computeRoutes_autoUpdate request send = do
62+
dieOnShutdown outChan
5263

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

0 commit comments

Comments
 (0)