Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PLT-5286 Adjust default logging configuration for each component and document #542

Merged
merged 11 commits into from Mar 21, 2023
55 changes: 55 additions & 0 deletions eventuo11y-extras/README.md
@@ -0,0 +1,55 @@
# eventuo11y-extras

Observability utilities built on top of eventu011y.

## Logging component

`Observe.Event.Component` exports a `logger` component (from `async-components`)
that exposes an `EventBackend` which will write events to a queue. The `logger`
writes the serialized events as log messages. Individual events can be enabled
and disabled by way of a configuration file.

The format of the configuration file is a JSON `object` with the following
schema:

```typescript
interface LogConfigMap {
[eventKey: string]: bool | null | { [fieldKey: string]: bool };
}
```

For example:

```jsonc
{
"event1": true, // Enables the event with key `event1`. Uses default config for fields
"event2": false, // Disables the event with key `event2`.
"event3": null, // Uses the default config for `event3`. This is not necessary, as missing fields will use the default config.
"event4": { // Enables `event4`. Uses a custom config for fields.
"field1": true, // Enables `field1` for `event4`.
"field2": false, // Disables `field2` for `event4`.
"field3": true // Uses the default config for `field3`. This is not necessary, missing fields will use the default config.
}
}
```

Note that comments are not allowed in the actual log config file.

This file can be changed at runtime and the `logger` component will reload the
config automatically to apply the changes.

Though this functionality is not built-in to the `logger`, all applications in
`marlowe-chain-sync` and `marlowe-runtime` which use a `logger` have a
command-line option `--print-log-config` which will print the available events
with their default configuration to stdout. Note that field keys are not yet
supported in this feature. The intended use for this is to generate a log
config file:

```bash
# Generate a log config file with the default values
$ marlowe-sync --print-log-config > marlowe-sync.log.config
# Run marlowe-sync with the generated log config. You can enable and disable
# events by editing the file and the output of `marlowe-sync` will be changed to
# reflect the new log config.
$ marlowe-sync --log-config-file marlowe-sync.log.config
```
10 changes: 9 additions & 1 deletion eventuo11y-extras/src/Observe/Event/Backend/Extra.hs
Expand Up @@ -51,7 +51,15 @@ filterEventBackendM selectorPredicate EventBackend{..} = EventBackend
Nothing -> pure Event
{ reference = Nothing
, addField = const $ pure ()
, finalize = const $ pure ()
, finalize = \case
Nothing -> pure ()
Just ex -> do
Event{..} <- newEvent args
{ newEventInitialFields = mempty
, newEventParent = join newEventParent
, newEventCauses = catMaybes newEventCauses
}
finalize $ Just ex
}
Just fieldPredicate -> do
filteredInitFields <- filterM fieldPredicate newEventInitialFields
Expand Down
10 changes: 10 additions & 0 deletions eventuo11y-extras/src/Observe/Event/Component.hs
Expand Up @@ -10,8 +10,10 @@ module Observe.Event.Component
, GetSelectorConfig
, LoggerDependencies(..)
, SelectorConfig(..)
, SelectorLogConfig(..)
, SomeJSON(..)
, absurdFieldConfig
, getDefaultLogConfig
, logger
, prependKey
, singletonFieldConfig
Expand Down Expand Up @@ -118,6 +120,14 @@ instance ToJSON FieldLogConfig where
FieldDisabled -> Bool False
FieldEnabled -> Bool True

getDefaultLogConfig
:: GetSelectorConfig s
-> s f
-> Map Text SelectorLogConfig
getDefaultLogConfig getConfig sel = case getConfig sel of
SelectorConfig key True _ -> Map.singleton key SelectorEnabled
SelectorConfig key False _ -> Map.singleton key SelectorDisabled

data LoggerDependencies m r s = LoggerDependencies
{ configFilePath :: Maybe FilePath
, getSelectorConfig :: GetSelectorConfig s
Expand Down
37 changes: 2 additions & 35 deletions marlowe-chain-sync/README.md
Expand Up @@ -57,39 +57,6 @@ performed:
find the result from the client's position, and retrieve the new position for
the client.

### Configuring the log output
## Logging

The log output of `marlowe-chain-sync` can be configured on-the-fly (i.e. updated while
it is running) using a config file. This file is passed via an optional command line
argument (see `--help`). An example config file is located in this directory.
It shows the default configuration.

Each key in the configuration object corresponds to an event type. Setting the
value to true means the event will be logged, false means it won't be logged,
and not present means the default behaviour will be used. For example, to turn
on the `chain-sync.send` event (disabled by default), you can use a config file
with contents.

```json
{
"chain-sync.send": true
}
```

You can also assign an object value to each key. The keys of this object
correspond to fields associated with that event. `true` means include the
field, `false` means don't include the field, and not defined means the default
behaviour is used. For example, to log the `stateBefore` field of the
`query.recv` event (disabled by default), you can use a config file with the contents:

```json
{
"query.recv": {
"stateBefore": true
}
}
```

Each time you save this file, it will be read and the configuration will be
updated by `marlowe-chain-sync`. If it cannot find the config file, the default
configuration will be used.
See [eventuo11y-extras](../eventuo11y-extras).
@@ -1,12 +1,11 @@

{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}


module Language.Marlowe.Runtime.ChainSync.NodeClient
( NodeClient(..)
, NodeClientDependencies(..)
, NodeClientSelector
, NodeClientSelector(..)
, QueryNode
, SubmitToNode
, getNodeClientSelectorConfig
Expand Down
28 changes: 27 additions & 1 deletion marlowe-chain-sync/marlowe-chain-indexer/Logging.hs
Expand Up @@ -5,11 +5,24 @@

module Logging
( RootSelector(..)
, defaultRootSelectorLogConfig
, getRootSelectorConfig
) where

import Data.Foldable (fold)
import Data.Map (Map)
import Data.Text (Text)
import Language.Marlowe.Runtime.ChainIndexer (ChainIndexerSelector(..), getChainIndexerSelectorConfig)
import Observe.Event.Component (ConfigWatcherSelector(..), GetSelectorConfig, SelectorConfig(..), singletonFieldConfig)
import Language.Marlowe.Runtime.ChainIndexer.NodeClient (NodeClientSelector(..))
import Language.Marlowe.Runtime.ChainIndexer.Store (ChainStoreSelector(..))
import Observe.Event.Component
( ConfigWatcherSelector(..)
, GetSelectorConfig
, SelectorConfig(..)
, SelectorLogConfig
, getDefaultLogConfig
, singletonFieldConfig
)

data RootSelector f where
App :: ChainIndexerSelector f -> RootSelector f
Expand All @@ -21,3 +34,16 @@ getRootSelectorConfig = \case
App sel -> getChainIndexerSelectorConfig sel
ConfigWatcher ReloadConfig -> SelectorConfig "reload-log-config" True
$ singletonFieldConfig "config" True

defaultRootSelectorLogConfig :: Map Text SelectorLogConfig
defaultRootSelectorLogConfig = fold
[ getDefaultLogConfig (App $ NodeClientEvent Connect) getRootSelectorConfig
, getDefaultLogConfig (App $ NodeClientEvent Intersect) getRootSelectorConfig
, getDefaultLogConfig (App $ NodeClientEvent IntersectFound) getRootSelectorConfig
, getDefaultLogConfig (App $ NodeClientEvent IntersectNotFound) getRootSelectorConfig
, getDefaultLogConfig (App $ NodeClientEvent RollForward) getRootSelectorConfig
, getDefaultLogConfig (App $ NodeClientEvent RollBackward) getRootSelectorConfig
, getDefaultLogConfig (App $ ChainStoreEvent CheckGenesisBlock) getRootSelectorConfig
, getDefaultLogConfig (App $ ChainStoreEvent Save) getRootSelectorConfig
, getDefaultLogConfig (ConfigWatcher ReloadConfig) getRootSelectorConfig
]
10 changes: 10 additions & 0 deletions marlowe-chain-sync/marlowe-chain-indexer/Options.hs
Expand Up @@ -4,9 +4,13 @@ module Options
) where

import Cardano.Api (NetworkId(..), NetworkMagic(..))
import Data.Aeson.Encode.Pretty (encodePretty)
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text.Lazy as T
import Data.Text.Lazy.Encoding (decodeUtf8)
import Language.Marlowe.Runtime.ChainIndexer.NodeClient (CostModel(..))
import Logging (defaultRootSelectorLogConfig)
import qualified Options.Applicative as O
import System.Environment (lookupEnv)
import Text.Read (readMaybe)
Expand Down Expand Up @@ -61,6 +65,7 @@ parseOptions defaultNetworkId defaultSocketPath defaultDatabaseUri version = O.i
parser :: O.Parser Options
parser = O.helper
<*> versionOption
<*> printLogConfigOption
<*> ( Options
<$> socketPathOption
<*> networkIdOption
Expand All @@ -79,6 +84,11 @@ parseOptions defaultNetworkId defaultSocketPath defaultDatabaseUri version = O.i
("marlowe-chain-indexer " <> version)
(O.long "version" <> O.help "Show version.")

printLogConfigOption :: O.Parser (a -> a)
printLogConfigOption = O.infoOption
(T.unpack $ decodeUtf8 $ encodePretty defaultRootSelectorLogConfig)
(O.long "print-log-config" <> O.help "Print the default log configuration.")

socketPathOption :: O.Parser FilePath
socketPathOption = O.strOption options
where
Expand Down
7 changes: 4 additions & 3 deletions marlowe-chain-sync/marlowe-chain-sync.cabal
Expand Up @@ -114,7 +114,6 @@ library libchainsync
, text
, transformers
, vector
visibility: public

library chain-indexer
import: lang
Expand Down Expand Up @@ -159,7 +158,6 @@ library chain-indexer
, typed-protocols
, vector
, witherable
visibility: public

library plutus-compat
import: lang
Expand Down Expand Up @@ -232,10 +230,12 @@ executable marlowe-chain-indexer
build-depends:
base >= 4.9 && < 5
, aeson
, aeson-pretty
, async-components
, cardano-api
, cardano-crypto-wrapper
, cardano-ledger-byron
, containers
, eventuo11y ^>= { 0.9, 0.10 }
, eventuo11y-extras
, hasql
Expand All @@ -247,7 +247,6 @@ executable marlowe-chain-indexer
, time
, transformers
, uuid
visibility: public

executable marlowe-chain-sync
import: lang
Expand All @@ -262,9 +261,11 @@ executable marlowe-chain-sync
Paths_marlowe_chain_sync
build-depends:
base >= 4.9 && < 5
, aeson-pretty
, async-components
, cardano-api
, cardano-ledger-byron
, containers
, eventuo11y ^>= { 0.9, 0.10 }
, eventuo11y-extras
, hasql
Expand Down
34 changes: 28 additions & 6 deletions marlowe-chain-sync/marlowe-chain-sync/Logging.hs
Expand Up @@ -5,17 +5,28 @@

module Logging
( RootSelector(..)
, defaultRootSelectorLogConfig
, getRootSelectorConfig
) where

import Data.Foldable (fold)
import Data.Map (Map)
import Data.Text (Text)
import Language.Marlowe.Runtime.ChainSync.Api (ChainSyncCommand, ChainSyncQuery, RuntimeChainSeek)
import Language.Marlowe.Runtime.ChainSync.NodeClient (NodeClientSelector, getNodeClientSelectorConfig)
import Network.Protocol.Connection (ConnectorSelector, getConnectorSelectorConfig)
import Language.Marlowe.Runtime.ChainSync.NodeClient (NodeClientSelector(..), getNodeClientSelectorConfig)
import Network.Protocol.Connection (ConnectorSelector, getConnectorSelectorConfig, getDefaultConnectorLogConfig)
import Network.Protocol.Handshake.Types (Handshake)
import Network.Protocol.Job.Types (Job)
import Network.Protocol.Query.Types (Query)
import Observe.Event.Component
(ConfigWatcherSelector(..), GetSelectorConfig, SelectorConfig(..), prependKey, singletonFieldConfig)
( ConfigWatcherSelector(..)
, GetSelectorConfig
, SelectorConfig(..)
, SelectorLogConfig
, getDefaultLogConfig
, prependKey
, singletonFieldConfig
)

data RootSelector f where
ChainSeekServer :: ConnectorSelector (Handshake RuntimeChainSeek) f -> RootSelector f
Expand All @@ -27,9 +38,20 @@ data RootSelector f where
-- TODO automate this boilerplate with Template Haskell
getRootSelectorConfig :: GetSelectorConfig RootSelector
getRootSelectorConfig = \case
ChainSeekServer sel -> prependKey "chain-sync" $ getConnectorSelectorConfig True False sel
QueryServer sel -> prependKey "query" $ getConnectorSelectorConfig True True sel
JobServer sel -> prependKey "job" $ getConnectorSelectorConfig True True sel
ChainSeekServer sel -> prependKey "chain-sync" $ getConnectorSelectorConfig False False sel
QueryServer sel -> prependKey "query" $ getConnectorSelectorConfig False False sel
JobServer sel -> prependKey "job" $ getConnectorSelectorConfig False False sel
NodeService sel -> prependKey "node" $ getNodeClientSelectorConfig sel
ConfigWatcher ReloadConfig -> SelectorConfig "reload-log-config" True
$ singletonFieldConfig "config" True

defaultRootSelectorLogConfig :: Map Text SelectorLogConfig
defaultRootSelectorLogConfig = fold
[ getDefaultConnectorLogConfig getRootSelectorConfig ChainSeekServer
, getDefaultConnectorLogConfig getRootSelectorConfig QueryServer
, getDefaultConnectorLogConfig getRootSelectorConfig JobServer
, getDefaultLogConfig getRootSelectorConfig $ NodeService Connect
, getDefaultLogConfig getRootSelectorConfig $ NodeService Submit
, getDefaultLogConfig getRootSelectorConfig $ NodeService Query
, getDefaultLogConfig getRootSelectorConfig $ ConfigWatcher ReloadConfig
]
10 changes: 10 additions & 0 deletions marlowe-chain-sync/marlowe-chain-sync/Options.hs
Expand Up @@ -4,7 +4,11 @@ module Options
) where

import Cardano.Api (NetworkId(..), NetworkMagic(..))
import Data.Aeson.Encode.Pretty (encodePretty)
import Data.Maybe (fromMaybe)
import qualified Data.Text.Lazy as T
import Data.Text.Lazy.Encoding (decodeUtf8)
import Logging (defaultRootSelectorLogConfig)
import Network.Socket (HostName, PortNumber)
import qualified Options.Applicative as O
import System.Environment (lookupEnv)
Expand Down Expand Up @@ -89,6 +93,7 @@ parseOptions defaultNetworkId defaultSocketPath defaultDatabaseUri defaultHost d
parser :: O.Parser Options
parser = O.helper
<*> versionOption
<*> printLogConfigOption
<*> ( Options
<$> socketPathOption
<*> networkIdOption
Expand All @@ -106,6 +111,11 @@ parseOptions defaultNetworkId defaultSocketPath defaultDatabaseUri defaultHost d
("marlowe-chain-sync " <> version)
(O.long "version" <> O.help "Show version.")

printLogConfigOption :: O.Parser (a -> a)
printLogConfigOption = O.infoOption
(T.unpack $ decodeUtf8 $ encodePretty defaultRootSelectorLogConfig)
(O.long "print-log-config" <> O.help "Print the default log configuration.")

socketPathOption :: O.Parser FilePath
socketPathOption = O.strOption options
where
Expand Down