Skip to content

Commit 484b7fc

Browse files
committed
Separate lib and exe and add nix dep
Signed-off-by: Sasha Bogicevic <[email protected]>
1 parent ecd4b2b commit 484b7fc

File tree

6 files changed

+304
-271
lines changed

6 files changed

+304
-271
lines changed

nix/coding-standards.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ _: {
2626
hydra-test-utils
2727
hydra-tui
2828
hydraw
29+
visualize-logs
2930
];
3031
inherit (pkgs) weeder;
3132
haskellType = "haskell.nix";

nix/hydra/packages.nix

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@
102102
"hydra-chain-observer"
103103
paddedRevision;
104104

105+
visualize-logs =
106+
embedRevision
107+
nativePkgs.visualize-logs.components.exes.visualize-logs
108+
"visualize-logs"
109+
paddedRevision;
110+
111+
visualize-logs-static =
112+
embedRevision
113+
musl64Pkgs.visualize-logs.components.exes.visualize-logs
114+
"visualize-logs"
115+
paddedRevision;
116+
105117
hydra-cluster = pkgs.writers.writeBashBin "hydra-cluster" ''
106118
export PATH=$PATH:${hydra-node}/bin
107119
${nativePkgs.hydra-cluster.components.exes.hydra-cluster}/bin/hydra-cluster "$@"

nix/hydra/project.nix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
hydra-chain-observer.writeHieFiles = true;
3939
hydra-cluster.writeHieFiles = true;
4040
hydra-node.writeHieFiles = true;
41+
visualize-logs.writeHieFiles = true;
4142
hydra-plutus.writeHieFiles = true;
4243
hydra-plutus-extras.writeHieFiles = true;
4344
hydra-prelude.writeHieFiles = true;
@@ -46,6 +47,7 @@
4647
hydra-tui.writeHieFiles = true;
4748
hydraw.writeHieFiles = true;
4849
hydra-node.dontStrip = false;
50+
visualize-logs.dontStrip = false;
4951
hydra-tui.dontStrip = false;
5052
hydraw.dontStrip = false;
5153
};
@@ -72,6 +74,7 @@
7274
hydra-node.ghcOptions = [ "-L${pkgs.lib.getLib pkgs.static-snappy}/lib" ];
7375
hydra-tui.ghcOptions = [ "-L${pkgs.lib.getLib pkgs.static-snappy}/lib" ];
7476
hydra-chain-observer.ghcOptions = [ "-L${pkgs.lib.getLib pkgs.static-snappy}/lib" ];
77+
visualize-logs.ghcOptions = [ "-L${pkgs.lib.getLib pkgs.static-snappy}/lib" ];
7578
hydraw.ghcOptions = [ "-L${pkgs.lib.getLib pkgs.static-snappy}/lib" ];
7679
};
7780
}

visualize-logs/Main.hs

Lines changed: 3 additions & 268 deletions
Original file line numberDiff line numberDiff line change
@@ -1,276 +1,11 @@
1-
{-# LANGUAGE OverloadedRecordDot #-}
2-
31
-- | Parse hydra-node logs format more easy on the eyes. Parser works with regular json logs as well as journalctl format.
42
module Main where
53

6-
import Hydra.Cardano.Api (Tx)
7-
import Hydra.Prelude hiding (encodeUtf8, takeWhile)
8-
9-
import Conduit
10-
import Control.Lens ((^?))
11-
import Control.Monad (foldM)
12-
import Data.Aeson (eitherDecode')
13-
import Data.Aeson.Lens (key, _String)
14-
import Data.Attoparsec.ByteString
15-
import Data.Attoparsec.ByteString qualified as A
16-
import Data.Attoparsec.ByteString.Char8 (char8, endOfLine, isEndOfLine)
17-
import Data.Text.Encoding (encodeUtf8)
18-
import Hydra.Chain (ChainEvent (..))
19-
import Hydra.HeadLogic (Effect (..), Input (..), Outcome (..), StateChanged (..))
20-
import Hydra.Logging (Envelope (..))
21-
import Hydra.Logging.Messages (HydraLog (..))
22-
import Hydra.Node (HydraNodeLog (..))
23-
import Options.Applicative hiding (Parser)
24-
import Options.Applicative qualified as Options
25-
26-
data InfoLine = InfoLine {toplabel :: LogType, details :: Text} deriving (Eq, Show)
27-
28-
data Decoded tx
29-
= DecodedHydraLog {t :: UTCTime, n :: Text, infoLine :: InfoLine}
30-
| DropLog
31-
deriving (Eq, Show)
32-
33-
-- | This instance is needed to sort results by timestamp
34-
instance Ord (Decoded tx) where
35-
compare DropLog DropLog = EQ
36-
compare DropLog DecodedHydraLog{} = LT
37-
compare DecodedHydraLog{} DropLog = GT
38-
compare (DecodedHydraLog t1 _ _) (DecodedHydraLog t2 _ _) = compare t1 t2
39-
40-
-- | Log type labels for visualization
41-
data LogType
42-
= NodeOptionsLabel
43-
| ClientSentLabel
44-
| ObservationLabel
45-
| NetworkLabel
46-
| ChainEffectLabel
47-
| ErrorLabel
48-
| LogicLabel Text
49-
| LogicError Text
50-
| Other Text
51-
deriving (Eq, Show)
52-
53-
labelLog :: LogType -> Text
54-
labelLog NodeOptionsLabel = "NODE OPTIONS"
55-
labelLog ClientSentLabel = "CLIENT SENT"
56-
labelLog ObservationLabel = "OBSERVATION"
57-
labelLog NetworkLabel = "NETWORK EFFECT"
58-
labelLog ChainEffectLabel = "POSTING TX"
59-
labelLog ErrorLabel = "ERROR"
60-
labelLog (LogicLabel t) = unlines ["LOGIC", t]
61-
labelLog (LogicError t) = unlines ["LOGIC ERROR", t]
62-
labelLog (Other t) = unlines ["OTHER", t]
63-
64-
colorLog :: LogType -> Text
65-
colorLog = \case
66-
NodeOptionsLabel -> green
67-
ClientSentLabel -> green
68-
ObservationLabel -> blue
69-
NetworkLabel -> magenta
70-
ChainEffectLabel -> blue
71-
ErrorLabel -> red
72-
LogicLabel t -> case t of
73-
"DepositExpired" -> red
74-
"DecommitInvalid" -> red
75-
"IgnoredHeadInitializing" -> red
76-
"TxInvalid" -> red
77-
_ -> cyan
78-
LogicError _ -> red
79-
Other _ -> green
80-
81-
newtype Options = Options
82-
{ paths :: [FilePath]
83-
}
84-
deriving (Show)
85-
86-
options :: Options.Parser Options
87-
options =
88-
Options
89-
<$> many
90-
( strArgument
91-
( metavar "FILES"
92-
<> help "One or more log file paths."
93-
)
94-
)
95-
96-
opts :: ParserInfo Options
97-
opts =
98-
info
99-
(options <**> helper)
100-
( fullDesc
101-
<> progDesc "Group logs by the timestamp and display using colors and separators for easy inspection."
102-
<> header "Visualize hydra-node logs"
103-
)
4+
import Hydra.Prelude
5+
import Options.Applicative (execParser)
6+
import VisualizeLogs
1047

1058
main :: IO ()
1069
main = do
10710
args <- execParser opts
10811
visualize $ paths args
109-
110-
visualize :: [FilePath] -> IO ()
111-
visualize paths = do
112-
decodedLines <-
113-
runConduitRes $
114-
mapM_ sourceFileBS paths
115-
.| linesUnboundedAsciiC
116-
.| mapMC decodeAndProcess
117-
.| filterC (/= DropLog)
118-
.| sinkList
119-
120-
forM_ (sort decodedLines) render
121-
122-
decodeAndProcess :: ByteString -> ResourceT IO (Decoded Tx)
123-
decodeAndProcess l =
124-
case inCurlyBraces l of
125-
Left _ -> lift $ pure DropLog
126-
Right incomingLine ->
127-
case incomingLine of
128-
Nothing -> lift $ pure DropLog
129-
Just jsonLine ->
130-
case jsonLine ^? key "message" . _String of
131-
Nothing -> process jsonLine
132-
Just line -> process $ encodeUtf8 line
133-
where
134-
process :: ByteString -> ResourceT IO (Decoded Tx)
135-
process line =
136-
case eitherDecode' (fromStrict line) of
137-
Left e -> do
138-
putTextLn $ red <> "Decoding failed for line: " <> decodeUtf8 l <> "\nError: " <> show e
139-
pure DropLog
140-
Right decoded -> lift $ processLogs decoded
141-
142-
charToWord8 :: Char -> Word8
143-
charToWord8 = fromIntegral . ord
144-
145-
textBetweenBraces :: A.Parser ByteString
146-
textBetweenBraces = do
147-
skipWhile (/= charToWord8 '{')
148-
_ <- char8 '{'
149-
jsonStr <- takeWhile (not . isEndOfLine)
150-
pure $ "{" <> jsonStr
151-
152-
lineParser :: A.Parser (Maybe ByteString)
153-
lineParser = do
154-
result <- optional textBetweenBraces
155-
skipWhile (not . isEndOfLine)
156-
endOfLine <|> endOfInput
157-
return result
158-
159-
inCurlyBraces :: ByteString -> Either String (Maybe ByteString)
160-
inCurlyBraces = parseOnly lineParser
161-
162-
-- | Ideally we would have Data instances for all types so we could get data type string representation
163-
-- instead of providing strings directly but that would add some compilation time overhead so not worth it.
164-
processLogs :: Envelope (HydraLog Tx) -> IO (Decoded Tx)
165-
processLogs decoded =
166-
case decoded.message of
167-
NodeOptions opt -> logIt NodeOptionsLabel opt
168-
Node msg ->
169-
case msg of
170-
BeginInput{input} ->
171-
case input of
172-
ClientInput{clientInput} -> logIt ClientSentLabel clientInput
173-
NetworkInput{} -> pure DropLog
174-
ChainInput{chainEvent} ->
175-
case chainEvent of
176-
Observation{observedTx} -> logIt ObservationLabel observedTx
177-
Rollback{} -> pure DropLog
178-
Tick{} -> pure DropLog
179-
PostTxError{postTxError} -> logIt ErrorLabel postTxError
180-
EndInput{} -> pure DropLog
181-
BeginEffect{effect} ->
182-
case effect of
183-
ClientEffect{} -> pure DropLog
184-
NetworkEffect{message} -> logIt NetworkLabel message
185-
OnChainEffect{postChainTx} -> logIt ChainEffectLabel postChainTx
186-
EndEffect{} -> pure DropLog
187-
LogicOutcome{outcome} ->
188-
case outcome of
189-
Continue{stateChanges} ->
190-
foldM
191-
( \_ a -> case a of
192-
details@HeadInitialized{} -> logIt (LogicLabel "HeadInitialized") details
193-
details@HeadOpened{} -> logIt (LogicLabel "HeadOpened") details
194-
details@CommittedUTxO{} -> logIt (LogicLabel "CommittedUTxO") details
195-
details@HeadAborted{} -> logIt (LogicLabel "HeadAborted") details
196-
details@SnapshotRequestDecided{} -> logIt (LogicLabel "SnapshotRequestDecided") details
197-
details@SnapshotRequested{} -> logIt (LogicLabel "SnapshotRequested") details
198-
details@PartySignedSnapshot{} -> logIt (LogicLabel "PartySignedSnapshot") details
199-
details@SnapshotConfirmed{} -> logIt (LogicLabel "SnapshotConfirmed") details
200-
details@DepositRecorded{} -> logIt (LogicLabel "DepositRecorded") details
201-
details@DepositActivated{} -> logIt (LogicLabel "DepositActivated") details
202-
details@DepositExpired{} -> logIt (LogicLabel "DepositExpired") details
203-
details@DepositRecovered{} -> logIt (LogicLabel "DepositRecovered") details
204-
details@CommitApproved{} -> logIt (LogicLabel "CommitApproved") details
205-
details@CommitFinalized{} -> logIt (LogicLabel "CommitFinalized") details
206-
details@DecommitRecorded{} -> logIt (LogicLabel "DecommitRecorded") details
207-
details@DecommitApproved{} -> logIt (LogicLabel "DecommitApproved") details
208-
details@DecommitInvalid{} -> logIt (LogicLabel "DecommitInvalid") details
209-
details@DecommitFinalized{} -> logIt (LogicLabel "DecommitFinalized") details
210-
details@HeadClosed{} -> logIt (LogicLabel "HeadClosed") details
211-
details@HeadContested{} -> logIt (LogicLabel "HeadContested") details
212-
details@HeadIsReadyToFanout{} -> logIt (LogicLabel "HeadIsReadyToFanout") details
213-
details@HeadFannedOut{} -> logIt (LogicLabel "HeadFannedOut") details
214-
details@IgnoredHeadInitializing{} -> logIt (LogicLabel "IgnoredHeadInitializing") details
215-
details@TxInvalid{} -> logIt (LogicLabel "TxInvalid") details
216-
NetworkConnected{} -> pure DropLog
217-
NetworkDisconnected{} -> pure DropLog
218-
PeerConnected{} -> pure DropLog
219-
PeerDisconnected{} -> pure DropLog
220-
NetworkVersionMismatch{} -> pure DropLog
221-
NetworkClusterIDMismatch{} -> pure DropLog
222-
TransactionReceived{} -> pure DropLog
223-
TransactionAppliedToLocalUTxO{} -> pure DropLog
224-
ChainRolledBack{} -> pure DropLog
225-
TickObserved{} -> pure DropLog
226-
LocalStateCleared{} -> pure DropLog
227-
Checkpoint{} -> pure DropLog
228-
)
229-
DropLog
230-
stateChanges
231-
Wait{} -> pure DropLog
232-
Error{error = err} -> logIt (LogicError "LOGIC ERROR") err
233-
DroppedFromQueue{} -> pure DropLog
234-
LoadingState -> logIt (Other "Loading state...") ()
235-
LoadedState{} -> logIt (Other "Loaded.") ()
236-
ReplayingState -> logIt (Other "Replaying state...") ()
237-
details@Misconfiguration{} -> logIt (Other "MISCONFIG!") details
238-
_ -> pure DropLog
239-
where
240-
logIt :: Show x => LogType -> x -> IO (Decoded Tx)
241-
logIt l s =
242-
pure $ DecodedHydraLog decoded.timestamp decoded.namespace (InfoLine l (show s))
243-
244-
render :: Decoded tx -> IO ()
245-
render = \case
246-
DecodedHydraLog{t, n, infoLine = InfoLine{toplabel, details}} -> do
247-
putTextLn $
248-
unlines
249-
[ "-----------------------------------"
250-
, "[" <> show t <> "]"
251-
, "NAMESPACE:" <> show n
252-
, colorLog toplabel
253-
, labelLog toplabel
254-
, details
255-
, reset
256-
]
257-
DropLog -> putTextLn ""
258-
259-
-- ANSI escape codes for colors
260-
red :: Text
261-
red = "\ESC[31m"
262-
263-
green :: Text
264-
green = "\ESC[32m"
265-
266-
blue :: Text
267-
blue = "\ESC[34m"
268-
269-
cyan :: Text
270-
cyan = "\ESC[36m"
271-
272-
magenta :: Text
273-
magenta = "\ESC[45m"
274-
275-
reset :: Text
276-
reset = "\ESC[0m"

0 commit comments

Comments
 (0)