packages feed

katip-elasticsearch 0.3.1.0 → 0.4.0.0

raw patch · 7 files changed

+888/−442 lines, 7 filesdep +bytestringdep +taggeddep ~bloodhounddep ~katipPVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring, tagged

Dependency ranges changed: bloodhound, katip

API changes (from Hackage documentation)

- Katip.Scribes.ElasticSearch: instance GHC.Classes.Eq Katip.Scribes.ElasticSearch.EsPoolSize
- Katip.Scribes.ElasticSearch: instance GHC.Classes.Eq Katip.Scribes.ElasticSearch.EsQueueSize
- Katip.Scribes.ElasticSearch: instance GHC.Classes.Eq Katip.Scribes.ElasticSearch.IndexNameSegment
- Katip.Scribes.ElasticSearch: instance GHC.Classes.Ord Katip.Scribes.ElasticSearch.EsPoolSize
- Katip.Scribes.ElasticSearch: instance GHC.Classes.Ord Katip.Scribes.ElasticSearch.EsQueueSize
- Katip.Scribes.ElasticSearch: instance GHC.Classes.Ord Katip.Scribes.ElasticSearch.IndexNameSegment
- Katip.Scribes.ElasticSearch: instance GHC.Enum.Bounded Katip.Scribes.ElasticSearch.EsPoolSize
- Katip.Scribes.ElasticSearch: instance GHC.Enum.Bounded Katip.Scribes.ElasticSearch.EsQueueSize
- Katip.Scribes.ElasticSearch: instance GHC.Exception.Exception Katip.Scribes.ElasticSearch.EsScribeSetupError
- Katip.Scribes.ElasticSearch: instance GHC.Show.Show Katip.Scribes.ElasticSearch.EsPoolSize
- Katip.Scribes.ElasticSearch: instance GHC.Show.Show Katip.Scribes.ElasticSearch.EsQueueSize
- Katip.Scribes.ElasticSearch: instance GHC.Show.Show Katip.Scribes.ElasticSearch.EsScribeSetupError
- Katip.Scribes.ElasticSearch: instance GHC.Show.Show Katip.Scribes.ElasticSearch.IndexNameSegment
- Katip.Scribes.ElasticSearch: instance GHC.Show.Show Katip.Scribes.ElasticSearch.IndexShardingPolicy
- Katip.Scribes.ElasticSearch: roundToSunday :: Day -> Day
+ Katip.Scribes.ElasticSearch: data ESV1
+ Katip.Scribes.ElasticSearch: data ESV5
+ Katip.Scribes.ElasticSearch: defaultEsScribeCfg' :: ESVersion v => proxy v -> EsScribeCfg v
+ Katip.Scribes.ElasticSearch: defaultEsScribeCfgV1 :: EsScribeCfg ESV1
+ Katip.Scribes.ElasticSearch: defaultEsScribeCfgV5 :: EsScribeCfg ESV5
+ Katip.Scribes.ElasticSearch.Internal: CouldNotCreateIndex :: !(Response ByteString) -> EsScribeSetupError
+ Katip.Scribes.ElasticSearch.Internal: CouldNotCreateMapping :: !(Response ByteString) -> EsScribeSetupError
+ Katip.Scribes.ElasticSearch.Internal: CustomIndexSharding :: (forall a. Item a -> [IndexNameSegment]) -> IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: DailyIndexSharding :: IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: ESV1 :: ESV1
+ Katip.Scribes.ElasticSearch.Internal: ESV5 :: ESV5
+ Katip.Scribes.ElasticSearch.Internal: EsPoolSize :: Int -> EsPoolSize
+ Katip.Scribes.ElasticSearch.Internal: EsQueueSize :: Int -> EsQueueSize
+ Katip.Scribes.ElasticSearch.Internal: EsScribeCfg :: RetryPolicy -> EsQueueSize -> EsPoolSize -> Bool -> IndexSettings v -> IndexShardingPolicy -> EsScribeCfg v
+ Katip.Scribes.ElasticSearch.Internal: EveryMinuteIndexSharding :: IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: HourlyIndexSharding :: IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: IndexNameSegment :: Text -> IndexNameSegment
+ Katip.Scribes.ElasticSearch.Internal: MonthlyIndexSharding :: IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: NoIndexSharding :: IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: WeeklyIndexSharding :: IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: [essAnnotateTypes] :: EsScribeCfg v -> Bool
+ Katip.Scribes.ElasticSearch.Internal: [essIndexSettings] :: EsScribeCfg v -> IndexSettings v
+ Katip.Scribes.ElasticSearch.Internal: [essIndexSharding] :: EsScribeCfg v -> IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: [essPoolSize] :: EsScribeCfg v -> EsPoolSize
+ Katip.Scribes.ElasticSearch.Internal: [essQueueSize] :: EsScribeCfg v -> EsQueueSize
+ Katip.Scribes.ElasticSearch.Internal: [essRetryPolicy] :: EsScribeCfg v -> RetryPolicy
+ Katip.Scribes.ElasticSearch.Internal: [indexNameSegment] :: IndexNameSegment -> Text
+ Katip.Scribes.ElasticSearch.Internal: [unEsPoolSize] :: EsPoolSize -> Int
+ Katip.Scribes.ElasticSearch.Internal: [unEsQueueSize] :: EsQueueSize -> Int
+ Katip.Scribes.ElasticSearch.Internal: baseMapping :: ESVersion v => proxy v -> MappingName v -> Value
+ Katip.Scribes.ElasticSearch.Internal: chooseIxn :: ESVersion v => proxy v -> IndexName v -> IndexShardingPolicy -> Item a -> IndexName v
+ Katip.Scribes.ElasticSearch.Internal: class ESVersion v where type family BHEnv v type family IndexSettings v type family IndexName v type family MappingName v type family DocId v type family BH v :: (* -> *) -> * -> * type family TemplateName v type family TemplatePattern v type family IndexTemplate v type family IndexDocumentSettings v
+ Katip.Scribes.ElasticSearch.Internal: createIndex :: ESVersion v => proxy v -> IndexSettings v -> IndexName v -> BH v IO (Response ByteString)
+ Katip.Scribes.ElasticSearch.Internal: data ESV1
+ Katip.Scribes.ElasticSearch.Internal: data ESV5
+ Katip.Scribes.ElasticSearch.Internal: data EsScribeCfg v
+ Katip.Scribes.ElasticSearch.Internal: data EsScribeSetupError
+ Katip.Scribes.ElasticSearch.Internal: data IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: defaultEsScribeCfg :: EsScribeCfg ESV1
+ Katip.Scribes.ElasticSearch.Internal: defaultEsScribeCfg' :: ESVersion v => proxy v -> EsScribeCfg v
+ Katip.Scribes.ElasticSearch.Internal: defaultEsScribeCfgV1 :: EsScribeCfg ESV1
+ Katip.Scribes.ElasticSearch.Internal: defaultEsScribeCfgV5 :: EsScribeCfg ESV5
+ Katip.Scribes.ElasticSearch.Internal: defaultIndexDocumentSettings :: ESVersion v => proxy v -> IndexDocumentSettings v
+ Katip.Scribes.ElasticSearch.Internal: defaultIndexSettings :: ESVersion v => proxy v -> IndexSettings v
+ Katip.Scribes.ElasticSearch.Internal: esDateFormat :: Text
+ Katip.Scribes.ElasticSearch.Internal: fromIndexName :: ESVersion v => proxy v -> IndexName v -> Text
+ Katip.Scribes.ElasticSearch.Internal: fromMappingName :: ESVersion v => proxy v -> MappingName v -> Text
+ Katip.Scribes.ElasticSearch.Internal: indexDocument :: (ESVersion v, ToJSON doc) => proxy v -> IndexName v -> MappingName v -> IndexDocumentSettings v -> doc -> DocId v -> BH v IO (Response ByteString)
+ Katip.Scribes.ElasticSearch.Internal: indexExists :: ESVersion v => proxy v -> IndexName v -> BH v IO Bool
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Classes.Eq Katip.Scribes.ElasticSearch.Internal.EsPoolSize
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Classes.Eq Katip.Scribes.ElasticSearch.Internal.EsQueueSize
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Classes.Eq Katip.Scribes.ElasticSearch.Internal.IndexNameSegment
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Classes.Ord Katip.Scribes.ElasticSearch.Internal.EsPoolSize
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Classes.Ord Katip.Scribes.ElasticSearch.Internal.EsQueueSize
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Classes.Ord Katip.Scribes.ElasticSearch.Internal.IndexNameSegment
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Enum.Bounded Katip.Scribes.ElasticSearch.Internal.EsPoolSize
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Enum.Bounded Katip.Scribes.ElasticSearch.Internal.EsQueueSize
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Exception.Exception Katip.Scribes.ElasticSearch.Internal.EsScribeSetupError
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Show.Show Katip.Scribes.ElasticSearch.Internal.EsPoolSize
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Show.Show Katip.Scribes.ElasticSearch.Internal.EsQueueSize
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Show.Show Katip.Scribes.ElasticSearch.Internal.EsScribeSetupError
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Show.Show Katip.Scribes.ElasticSearch.Internal.IndexNameSegment
+ Katip.Scribes.ElasticSearch.Internal: instance GHC.Show.Show Katip.Scribes.ElasticSearch.Internal.IndexShardingPolicy
+ Katip.Scribes.ElasticSearch.Internal: instance Katip.Scribes.ElasticSearch.Internal.ESVersion Katip.Scribes.ElasticSearch.Internal.ESV1
+ Katip.Scribes.ElasticSearch.Internal: instance Katip.Scribes.ElasticSearch.Internal.ESVersion Katip.Scribes.ElasticSearch.Internal.ESV5
+ Katip.Scribes.ElasticSearch.Internal: mkDocId :: ESVersion v => proxy v -> IO (DocId v)
+ Katip.Scribes.ElasticSearch.Internal: mkEsPoolSize :: Int -> Maybe EsPoolSize
+ Katip.Scribes.ElasticSearch.Internal: mkEsQueueSize :: Int -> Maybe EsQueueSize
+ Katip.Scribes.ElasticSearch.Internal: mkEsScribe :: (ESVersion v, MonadIO (BH v IO), Functor (BH v IO)) => EsScribeCfg v -> BHEnv v -> IndexName v -> MappingName v -> Severity -> Verbosity -> IO Scribe
+ Katip.Scribes.ElasticSearch.Internal: mkNonZero :: (Int -> a) -> Int -> Maybe a
+ Katip.Scribes.ElasticSearch.Internal: newtype EsPoolSize
+ Katip.Scribes.ElasticSearch.Internal: newtype EsQueueSize
+ Katip.Scribes.ElasticSearch.Internal: newtype IndexNameSegment
+ Katip.Scribes.ElasticSearch.Internal: putMapping :: (ESVersion v, ToJSON a) => proxy v -> IndexName v -> MappingName v -> a -> BH v IO (Response ByteString)
+ Katip.Scribes.ElasticSearch.Internal: putTemplate :: ESVersion v => proxy v -> IndexTemplate v -> TemplateName v -> BH v IO (Response ByteString)
+ Katip.Scribes.ElasticSearch.Internal: roundToSunday :: Day -> Day
+ Katip.Scribes.ElasticSearch.Internal: runBH :: ESVersion v => proxy v -> BHEnv v -> BH v m a -> m a
+ Katip.Scribes.ElasticSearch.Internal: shardPolicySegs :: IndexShardingPolicy -> Item a -> [IndexNameSegment]
+ Katip.Scribes.ElasticSearch.Internal: sis :: Integral a => a -> IndexNameSegment
+ Katip.Scribes.ElasticSearch.Internal: splitTime :: DiffTime -> (Int, Int)
+ Katip.Scribes.ElasticSearch.Internal: startWorker :: (ESVersion v) => EsScribeCfg v -> BHEnv v -> MappingName v -> TBMQueue (IndexName v, Value) -> IO ()
+ Katip.Scribes.ElasticSearch.Internal: toDocId :: ESVersion v => proxy v -> Text -> DocId v
+ Katip.Scribes.ElasticSearch.Internal: toIndexName :: ESVersion v => proxy v -> Text -> IndexName v
+ Katip.Scribes.ElasticSearch.Internal: toIndexTemplate :: ESVersion v => proxy v -> TemplatePattern v -> Maybe (IndexSettings v) -> [Value] -> IndexTemplate v
+ Katip.Scribes.ElasticSearch.Internal: toTemplateName :: ESVersion v => proxy v -> Text -> TemplateName v
+ Katip.Scribes.ElasticSearch.Internal: toTemplatePattern :: ESVersion v => proxy v -> Text -> TemplatePattern v
- Katip.Scribes.ElasticSearch: CouldNotCreateIndex :: !Reply -> EsScribeSetupError
+ Katip.Scribes.ElasticSearch: CouldNotCreateIndex :: !(Response ByteString) -> EsScribeSetupError
- Katip.Scribes.ElasticSearch: CouldNotCreateMapping :: !Reply -> EsScribeSetupError
+ Katip.Scribes.ElasticSearch: CouldNotCreateMapping :: !(Response ByteString) -> EsScribeSetupError
- Katip.Scribes.ElasticSearch: data EsScribeCfg
+ Katip.Scribes.ElasticSearch: data EsScribeCfg v
- Katip.Scribes.ElasticSearch: defaultEsScribeCfg :: EsScribeCfg
+ Katip.Scribes.ElasticSearch: defaultEsScribeCfg :: EsScribeCfg ESV1
- Katip.Scribes.ElasticSearch: essAnnotateTypes :: EsScribeCfg -> Bool
+ Katip.Scribes.ElasticSearch: essAnnotateTypes :: EsScribeCfg v -> Bool
- Katip.Scribes.ElasticSearch: essIndexSettings :: EsScribeCfg -> IndexSettings
+ Katip.Scribes.ElasticSearch: essIndexSettings :: EsScribeCfg v -> IndexSettings v
- Katip.Scribes.ElasticSearch: essIndexSharding :: EsScribeCfg -> IndexShardingPolicy
+ Katip.Scribes.ElasticSearch: essIndexSharding :: EsScribeCfg v -> IndexShardingPolicy
- Katip.Scribes.ElasticSearch: essPoolSize :: EsScribeCfg -> EsPoolSize
+ Katip.Scribes.ElasticSearch: essPoolSize :: EsScribeCfg v -> EsPoolSize
- Katip.Scribes.ElasticSearch: essQueueSize :: EsScribeCfg -> EsQueueSize
+ Katip.Scribes.ElasticSearch: essQueueSize :: EsScribeCfg v -> EsQueueSize
- Katip.Scribes.ElasticSearch: essRetryPolicy :: EsScribeCfg -> RetryPolicy
+ Katip.Scribes.ElasticSearch: essRetryPolicy :: EsScribeCfg v -> RetryPolicy
- Katip.Scribes.ElasticSearch: mkDocId :: IO DocId
+ Katip.Scribes.ElasticSearch: mkDocId :: ESVersion v => proxy v -> IO (DocId v)
- Katip.Scribes.ElasticSearch: mkEsScribe :: EsScribeCfg -> BHEnv -> IndexName -> MappingName -> Severity -> Verbosity -> IO (Scribe, IO ())
+ Katip.Scribes.ElasticSearch: mkEsScribe :: (ESVersion v, MonadIO (BH v IO), Functor (BH v IO)) => EsScribeCfg v -> BHEnv v -> IndexName v -> MappingName v -> Severity -> Verbosity -> IO Scribe

Files

bench/Main.hs view
@@ -11,14 +11,15 @@ import           Control.Monad import           Criterion.Main import           Data.Aeson-import qualified Data.HashMap.Strict                     as HM+import qualified Data.HashMap.Strict                  as HM+import           Data.Proxy                           (Proxy (..)) import           Data.RNG-import qualified Data.Text                               as T-import           Database.Bloodhound.Types+import qualified Data.Text                            as T+import           Database.V1.Bloodhound.Types import           Numeric ------------------------------------------------------------------------------- import           Katip.Scribes.ElasticSearch-import           Katip.Scribes.ElasticSearch.Annotations+import           Katip.Scribes.ElasticSearch.Internal (ESV1) -------------------------------------------------------------------------------  main :: IO ()@@ -34,7 +35,7 @@ mkDocIdBenchmark :: RNG -> Benchmark mkDocIdBenchmark rng = bgroup "mkDocId"   [-    bench "mkDocId (randomIO)" $ nfIO mkDocId+    bench "mkDocId (randomIO)" $ nfIO (mkDocId (Proxy :: Proxy ESV1))   , bench "mkDocId' (shared )" $ nfIO $ mkDocId' rng   ] 
changelog.md view
@@ -1,3 +1,8 @@+0.4.0.0+=======+* Update to bloodhound >= 0.13.0.0. This version adds support for both ElasticSearch versions 1 and 5. Previously, we implicitly supported one and maybe would work on 5. The types in `EsScribeCfg` had to change to be able to specify which version was being targeted.+* Improved documentation.+ 0.3.1.0 ======= * Widen dependency on katip
+ examples/example.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE OverloadedStrings #-}+module Main+    ( main+    ) where+++-------------------------------------------------------------------------------+import           Control.Exception+import           Database.V5.Bloodhound+import           Network.HTTP.Client+-------------------------------------------------------------------------------+import           Katip+import           Katip.Scribes.ElasticSearch+-------------------------------------------------------------------------------+++main :: IO ()+main = do+  mgr <- newManager defaultManagerSettings+  let bhe = mkBHEnv (Server "localhost") mgr+  esScribe <- mkEsScribe+    -- Reasonable for production+    defaultEsScribeCfgV5+    -- Reasonable for single-node in development+    -- defaultEsScribeCfgV5 { essIndexSettings = IndexSettings (ShardCound 1) (ReplicaCount 0)}+    bhe+    (IndexName "all-indices-prefixed-with")+    (MappingName "application-logs")+    DebugS+    V3+  let mkLogEnv = registerScribe "es" esScribe defaultScribeSettings =<< initLogEnv "MyApp" "production"+  bracket mkLogEnv closeScribes $ \le -> runKatipT le $ do+    logMsg "ns" InfoS "This goes to elasticsearch"
katip-elasticsearch.cabal view
@@ -1,7 +1,7 @@ name:                katip-elasticsearch synopsis:            ElasticSearch scribe for the Katip logging framework. description:         See README.md for more details.-version:             0.3.1.0+version:             0.4.0.0 license:             BSD3 license-file:        LICENSE author:              Ozgun Ataman, Michael Xavier@@ -14,6 +14,7 @@   README.md   changelog.md   bench/Main.hs+  examples/example.hs   test/Main.hs tested-with: GHC == 7.8.4, GHC== 7.10.3 @@ -25,10 +26,11 @@   exposed-modules:     Katip.Scribes.ElasticSearch     Katip.Scribes.ElasticSearch.Annotations+    Katip.Scribes.ElasticSearch.Internal   build-depends:       base >=4.6 && <5-    , katip >= 0.2.0.0 && < 0.5-    , bloodhound >= 0.11.0.0 && < 0.13+    , katip >= 0.2.0.0 && < 0.6+    , bloodhound >= 0.13.0.0 && < 0.15     , uuid >= 1.3.12 && < 1.4     , aeson >=0.6 && <1.2     , stm >= 2.4.3 && < 2.5@@ -44,6 +46,7 @@     , transformers >= 0.2 && < 0.6     , http-types >= 0.8 && < 0.10     , time >= 1 && < 1.7+    , bytestring   hs-source-dirs:      src   default-language:    Haskell2010   hs-source-dirs:      src@@ -96,6 +99,8 @@     , scientific     , time     , stm+    , bytestring+    , tagged    if flag(lib-Werror)     ghc-options: -Wall -Werror
src/Katip/Scribes/ElasticSearch.hs view
@@ -1,15 +1,42 @@-{-# LANGUAGE DeriveDataTypeable  #-}-{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE RankNTypes          #-}-{-# LANGUAGE RecordWildCards     #-}-{-# LANGUAGE ScopedTypeVariables #-} -- | Includes a scribe that can be used to log structured, JSON log -- messages to ElasticSearch. These logs can be explored easily using -- <https://www.elastic.co/products/kibana kibana> or your tool of--- choice.+-- choice. Supports ElasticSearch servers with version 1.x or 5.x by+-- way of different configs. ----- == __Important Note on Index Settings__+-- Example of configuring for ES5: --+-- @+--+-- import           Control.Exception+-- import           Database.V5.Bloodhound+-- import           Network.HTTP.Client+-- import           Katip+-- import           Katip.Scribes.ElasticSearch+--+--+-- main :: IO ()+-- main = do+--   mgr <- newManager defaultManagerSettings+--   let bhe = mkBHEnv (Server "localhost") mgr+--   esScribe <- mkEsScribe+--     -- Reasonable for production+--     defaultEsScribeCfgV5+--     -- Reasonable for single-node in development+--     -- defaultEsScribeCfgV5 { essIndexSettings = IndexSettings (ShardCound 1) (ReplicaCount 0)}+--     bhe+--     (IndexName "all-indices-prefixed-with")+--     (MappingName "application-logs")+--     DebugS+--     V3+--   let mkLogEnv = registerScribe "es" esScribe defaultScribeSettings =<< initLogEnv "MyApp" "production"+--   bracket mkLogEnv closeScribes $ \le -> runKatipT le $ do+--     logMsg "ns" InfoS "This goes to elasticsearch"+--+-- @+--+-- __Important Note on Index Settings__+-- -- 'defaultEsScribeCfg' inherits a set of default index settings from -- the @bloodhound@ package. These settings at this time of writing -- set the indices up to have 3 shards and 2 replicas. This is an@@ -30,6 +57,9 @@     , mkEsQueueSize     , EsPoolSize     , mkEsPoolSize+    , IndexShardingPolicy(..)+    , IndexNameSegment(..)+    -- ** EsScribeCfg and fields     , EsScribeCfg     , essRetryPolicy     , essQueueSize@@ -37,375 +67,29 @@     , essAnnotateTypes     , essIndexSettings     , essIndexSharding-    , IndexShardingPolicy(..)-    , IndexNameSegment(..)     , defaultEsScribeCfg+    , defaultEsScribeCfgV1+    , defaultEsScribeCfgV5+    -- ** Version-Proxied APIS+    -- $versionproxies+    , defaultEsScribeCfg'+    , ESV1+    , ESV5     -- * Utilities     , mkDocId     , module Katip.Scribes.ElasticSearch.Annotations-    , roundToSunday     ) where   --------------------------------------------------------------------------------import           Control.Applicative                     as A-import           Control.Concurrent-import           Control.Concurrent.Async-import           Control.Concurrent.STM.TBMQueue-import           Control.Exception.Base-import           Control.Exception.Enclosed-import           Control.Monad-import           Control.Monad.Catch-import           Control.Monad.IO.Class-import           Control.Monad.STM-import           Control.Retry                           (RetryPolicy,-                                                          exponentialBackoff,-                                                          limitRetries,-                                                          recovering)-import           Data.Aeson-import           Data.Monoid                             ((<>))-import           Data.Text                               (Text)-import qualified Data.Text                               as T-import qualified Data.Text.Encoding                      as T-import           Data.Time-import           Data.Time.Calendar.WeekDate-import           Data.Typeable-import           Data.UUID-import qualified Data.UUID.V4                            as UUID4-import           Database.Bloodhound-import           Network.HTTP.Client-import           Network.HTTP.Types.Status-import           Text.Printf                             (printf)---------------------------------------------------------------------------------import           Katip.Core import           Katip.Scribes.ElasticSearch.Annotations+import           Katip.Scribes.ElasticSearch.Internal -------------------------------------------------------------------------------  -data EsScribeCfg = EsScribeCfg {-      essRetryPolicy   :: RetryPolicy-    -- ^ Retry policy when there are errors sending logs to the server-    , essQueueSize     :: EsQueueSize-    -- ^ Maximum size of the bounded log queue-    , essPoolSize      :: EsPoolSize-    -- ^ Worker pool size limit for sending data to the-    , essAnnotateTypes :: Bool-    -- ^ Different payload items coexist in the "data" attribute in-    -- ES. It is possible for different payloads to have different-    -- types for the same key, e.g. an "id" key that is sometimes a-    -- number and sometimes a string. If you're having ES do dynamic-    -- mapping, the first log item will set the type and any that-    -- don't conform will be *discarded*. If you set this to True,-    -- keys will recursively be appended with their ES core-    -- type. e.g. "id" would become "id::l" and "id::s"-    -- automatically, so they won't conflict. When this library-    -- exposes a querying API, we will try to make deserialization and-    -- querying transparently remove the type annotations if this is-    -- enabled.-    , essIndexSettings :: IndexSettings-    , essIndexSharding :: IndexShardingPolicy-    } deriving (Typeable)----- | Reasonable defaults for a config:------     * defaultManagerSettings------     * exponential backoff with 25ms base delay up to 5 retries------     * Queue size of 1000------     * Pool size of 2------     * Annotate types set to False------     * DailyIndexSharding-defaultEsScribeCfg :: EsScribeCfg-defaultEsScribeCfg = EsScribeCfg {-      essRetryPolicy     = exponentialBackoff 25 <> limitRetries 5-    , essQueueSize       = EsQueueSize 1000-    , essPoolSize        = EsPoolSize 2-    , essAnnotateTypes   = False-    , essIndexSettings   = defaultIndexSettings-    , essIndexSharding   = DailyIndexSharding-    }------------------------------------------------------------------------------------- | How should katip store your log data?------ * NoIndexSharding will store all logs in one index name. This is--- the simplest option but is not advised in production. In practice,--- the index will grow very large and will get slower to--- search. Deleting records based on some sort of retention period is--- also extremely slow.------ * MonthlyIndexSharding, DailyIndexSharding, HourlyIndexSharding,--- EveryMinuteIndexSharding will generate indexes based on the time of--- the log. Index name is treated as a prefix. So if your index name--- is @foo@ and DailySharding is used, logs will be stored in--- @foo-2016-2-25@, @foo-2016-2-26@ and so on. Index templating will--- be used to set up mappings automatically. Deletes based on date are--- very fast and queries can be restricted to date ranges for better--- performance. Queries against all dates should use @foo-*@ as an--- index name. Note that index aliasing's glob feature is not suitable--- for these date ranges as it matches index names as they are--- declared, so new dates will be excluded. DailyIndexSharding is a--- reasonable choice. Changing index sharding strategies is not--- advisable.------ * CustomSharding: supply your own function that decomposes an item--- into its index name hierarchy which will be appended to the index--- name. So for instance if your function return ["arbitrary",--- "prefix"], the index will be @foo-arbitrary-prefix@ and the index--- template will be set to match @foo-*@. In general, you want to use--- segments of increasing granularity (like year, month, day for--- dates). This makes it easier to address groups of indexes--- (e.g. @foo-2016-*@).-data IndexShardingPolicy = NoIndexSharding-                         | MonthlyIndexSharding-                         | WeeklyIndexSharding-                         -- ^ A special case of daily which shards to sunday-                         | DailyIndexSharding-                         | HourlyIndexSharding-                         | EveryMinuteIndexSharding-                         | CustomIndexSharding (forall a. Item a -> [IndexNameSegment])---instance Show IndexShardingPolicy where- show NoIndexSharding          = "NoIndexSharding"- show MonthlyIndexSharding     = "MonthlyIndexSharding"- show WeeklyIndexSharding      = "WeeklyIndexSharding"- show DailyIndexSharding       = "DailyIndexSharding"- show HourlyIndexSharding      = "HourlyIndexSharding"- show EveryMinuteIndexSharding = "EveryMinuteIndexSharding"- show (CustomIndexSharding _)  = "CustomIndexSharding λ"-----------------------------------------------------------------------------------newtype IndexNameSegment = IndexNameSegment {-      indexNameSegment :: Text-    } deriving (Show, Eq, Ord)-----------------------------------------------------------------------------------shardPolicySegs :: IndexShardingPolicy -> Item a -> [IndexNameSegment]-shardPolicySegs NoIndexSharding _ = []-shardPolicySegs MonthlyIndexSharding Item {..} = [sis y, sis m]-  where-    (y, m, _) = toGregorian (utctDay _itemTime)-shardPolicySegs WeeklyIndexSharding Item {..} = [sis y, sis m, sis d]-  where-    (y, m, d) = toGregorian (roundToSunday (utctDay _itemTime))-shardPolicySegs DailyIndexSharding Item {..} = [sis y, sis m, sis d]-  where-    (y, m, d) = toGregorian (utctDay _itemTime)-shardPolicySegs HourlyIndexSharding Item {..} = [sis y, sis m, sis d, sis h]-  where-    (y, m, d) = toGregorian (utctDay _itemTime)-    (h, _) = splitTime (utctDayTime _itemTime)-shardPolicySegs EveryMinuteIndexSharding Item {..} = [sis y, sis m, sis d, sis h, sis mn]-  where-    (y, m, d) = toGregorian (utctDay _itemTime)-    (h, mn) = splitTime (utctDayTime _itemTime)-shardPolicySegs (CustomIndexSharding f) i  = f i------------------------------------------------------------------------------------- | If the given day is sunday, returns the input, otherwise returns--- the previous sunday-roundToSunday :: Day -> Day-roundToSunday d-    | dow == 7  = d-    | w > 1     = fromWeekDate y (w - 1) 7-    | otherwise = fromWeekDate (y - 1) 53 7-  where-    (y, w, dow) = toWeekDate d-----------------------------------------------------------------------------------chooseIxn :: IndexName -> IndexShardingPolicy -> Item a -> IndexName-chooseIxn (IndexName ixn) p i =-  IndexName (T.intercalate "-" (ixn:segs))-  where-    segs = indexNameSegment A.<$> shardPolicySegs p i-----------------------------------------------------------------------------------sis :: Integral a => a -> IndexNameSegment-sis = IndexNameSegment . T.pack . fmt-  where-    fmt = printf "%02d" . toInteger-----------------------------------------------------------------------------------splitTime :: DiffTime -> (Int, Int)-splitTime t = asMins `divMod` 60-  where-    asMins = floor t `div` 60-----------------------------------------------------------------------------------data EsScribeSetupError = CouldNotCreateIndex !Reply-                        | CouldNotCreateMapping !Reply deriving (Typeable, Show)---instance Exception EsScribeSetupError----------------------------------------------------------------------------------mkEsScribe-    :: EsScribeCfg-    -> BHEnv-    -> IndexName-    -- ^ Treated as a prefix if index sharding is enabled-    -> MappingName-    -> Severity-    -> Verbosity-    -> IO (Scribe, IO ())-    -- ^ Returns a finalizer that will gracefully flush all remaining logs before shutting down workers-mkEsScribe cfg@EsScribeCfg {..} env ix mapping sev verb = do-  q <- newTBMQueueIO $ unEsQueueSize essQueueSize-  endSig <- newEmptyMVar--  runBH env $ do-    chk <- indexExists ix-    -- note that this doesn't update settings. That's not available-    -- through the Bloodhound API yet-    unless chk $ void $ do-      r1 <- createIndex essIndexSettings ix-      unless (statusIsSuccessful (responseStatus r1)) $-        liftIO $ throwIO (CouldNotCreateIndex r1)-      r2 <- if shardingEnabled-              then putTemplate tpl tplName-              else putMapping ix mapping (baseMapping mapping)-      unless (statusIsSuccessful (responseStatus r2)) $-        liftIO $ throwIO (CouldNotCreateMapping r2)--  workers <- replicateM (unEsPoolSize essPoolSize) $ async $-    startWorker cfg env mapping q--  _ <- async $ do-    takeMVar endSig-    atomically $ closeTBMQueue q-    mapM_ waitCatch workers-    putMVar endSig ()--  let scribe = Scribe $ \ i ->-        when (_itemSeverity i >= sev) $-          void $ atomically $ tryWriteTBMQueue q (chooseIxn ix essIndexSharding i, itemJson' i)-  let finalizer = putMVar endSig () >> takeMVar endSig-  return (scribe, finalizer)-  where-    tplName = TemplateName ixn-    shardingEnabled = case essIndexSharding of-      NoIndexSharding -> False-      _               -> True-    tpl = IndexTemplate (TemplatePattern (ixn <> "-*")) (Just essIndexSettings) [toJSON (baseMapping mapping)]-    IndexName ixn = ix-    itemJson' i-      | essAnnotateTypes = itemJson verb (TypeAnnotated <$> i)-      | otherwise        = itemJson verb i-----------------------------------------------------------------------------------baseMapping :: MappingName -> Value-baseMapping (MappingName mn) =-  object [ mn .= object ["properties" .= object prs] ]-  where prs = [ str "thread"-              , str "sev"-              , str "pid"-              , str "ns"-              , str "msg"-              , "loc" .= locType-              , str "host"-              , str "env"-              , "at" .= dateType-              , str "app"-              ]-        str k = k .= object ["type" .= String "string"]-        locType = object ["properties" .= object locPairs]-        locPairs = [ str "loc_pkg"-                   , str "loc_mod"-                   , str "loc_ln"-                   , str "loc_fn"-                   , str "loc_col"-                   ]-        dateType = object [ "format" .= esDateFormat-                          , "type" .= String "date"-                          ]------------------------------------------------------------------------------------- | Handle both old-style aeson and picosecond-level precision-esDateFormat :: Text-esDateFormat = "yyyy-MM-dd'T'HH:mm:ssZ||yyyy-MM-dd'T'HH:mm:ss.SSSZ||yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSSSSZ"-----------------------------------------------------------------------------------mkDocId :: IO DocId-mkDocId = (DocId . T.decodeUtf8 . toASCIIBytes) `fmap` UUID4.nextRandom-----------------------------------------------------------------------------------newtype EsQueueSize = EsQueueSize {-       unEsQueueSize :: Int-     } deriving (Show, Eq, Ord)---instance Bounded EsQueueSize where-  minBound = EsQueueSize 1-  maxBound = EsQueueSize maxBound---mkEsQueueSize :: Int -> Maybe EsQueueSize-mkEsQueueSize = mkNonZero EsQueueSize-----------------------------------------------------------------------------------newtype EsPoolSize = EsPoolSize {-      unEsPoolSize :: Int-    } deriving (Show, Eq, Ord)---instance Bounded EsPoolSize where-  minBound = EsPoolSize 1-  maxBound = EsPoolSize maxBound---mkEsPoolSize :: Int -> Maybe EsPoolSize-mkEsPoolSize = mkNonZero EsPoolSize-----------------------------------------------------------------------------------mkNonZero :: (Int -> a) -> Int -> Maybe a-mkNonZero ctor n-  | n > 0     = Just $ ctor n-  | otherwise = Nothing-+{- $versionproxies ---------------------------------------------------------------------------------startWorker-    :: EsScribeCfg-    -> BHEnv-    -> MappingName-    -> TBMQueue (IndexName, Value)-    -> IO ()-startWorker EsScribeCfg {..} env mapping q = go-  where-    go = do-      popped <- atomically $ readTBMQueue q-      case popped of-        Just (ixn, v) -> do-          sendLog ixn v `catchAny` eat-          go-        Nothing -> return ()-    sendLog :: IndexName -> Value -> IO ()-    sendLog ixn v = void $ recovering essRetryPolicy [handler] $ const $ do-      did <- mkDocId-      res <- runBH env $ indexDocument ixn mapping defaultIndexDocumentSettings v did-      return res-    eat _ = return ()-    handler _ = Handler $ \e ->-      case fromException e of-        Just (_ :: AsyncException) -> return False-        _ -> return True+  You may need these these functions and types if type inference+  fails. For instance, you may need to hint to the compiler that a+  config is @:: EsScribeCfg ESV5@, for instance.+-}
+ src/Katip/Scribes/ElasticSearch/Internal.hs view
@@ -0,0 +1,520 @@+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE DeriveDataTypeable  #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE RecordWildCards     #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies        #-}+-- | This is an internal module. No guarantees are made in this module+-- about API stability.+module Katip.Scribes.ElasticSearch.Internal where+++-------------------------------------------------------------------------------+import           Control.Applicative                     as A+import           Control.Concurrent+import           Control.Concurrent.Async+import           Control.Concurrent.STM.TBMQueue+import           Control.Exception.Base+import           Control.Exception.Enclosed+import           Control.Monad+import           Control.Monad.Catch+import           Control.Monad.IO.Class+import           Control.Monad.STM+import           Control.Retry                           (RetryPolicy,+                                                          exponentialBackoff,+                                                          limitRetries,+                                                          recovering)+import           Data.Aeson+import           Data.ByteString.Lazy                    (ByteString)+import           Data.Monoid                             ((<>))+import           Data.Text                               (Text)+import qualified Data.Text                               as T+import qualified Data.Text.Encoding                      as T+import           Data.Time+import           Data.Time.Calendar.WeekDate+import           Data.Typeable                           as Typeable+import           Data.UUID+import qualified Data.UUID.V4                            as UUID4+import qualified Database.V1.Bloodhound                  as V1+import qualified Database.V5.Bloodhound                  as V5+import           Network.HTTP.Client+import           Network.HTTP.Types.Status+import           Text.Printf                             (printf)+-------------------------------------------------------------------------------+import           Katip.Core+import           Katip.Scribes.ElasticSearch.Annotations+-------------------------------------------------------------------------------+++-- | EsScribeCfg now carries a type variable for the version of+-- ElasticSearch it targets, either 'ESV1' or 'ESV5'. You can use+-- 'defaultEsScribeCfgV1' and 'defaultESScribeCfgV5' for a good+-- starting point depending on the ES version you have.+data EsScribeCfg v = EsScribeCfg {+      essRetryPolicy   :: RetryPolicy+    -- ^ Retry policy when there are errors sending logs to the server+    , essQueueSize     :: EsQueueSize+    -- ^ Maximum size of the bounded log queue+    , essPoolSize      :: EsPoolSize+    -- ^ Worker pool size limit for sending data to the+    , essAnnotateTypes :: Bool+    -- ^ Different payload items coexist in the "data" attribute in+    -- ES. It is possible for different payloads to have different+    -- types for the same key, e.g. an "id" key that is sometimes a+    -- number and sometimes a string. If you're having ES do dynamic+    -- mapping, the first log item will set the type and any that+    -- don't conform will be *discarded*. If you set this to True,+    -- keys will recursively be appended with their ES core+    -- type. e.g. "id" would become "id::l" and "id::s"+    -- automatically, so they won't conflict. When this library+    -- exposes a querying API, we will try to make deserialization and+    -- querying transparently remove the type annotations if this is+    -- enabled.+    , essIndexSettings :: IndexSettings v+    -- ^ This will be the IndexSettings type from the appropriate+    -- bloodhound module, either @Database.V1.Bloodhound@ or+    -- @Database.V5.Bloodhound@+    , essIndexSharding :: IndexShardingPolicy+    } deriving (Typeable)+++-- | Reasonable defaults for a config:+--+--     * defaultManagerSettings+--+--     * exponential backoff with 25ms base delay up to 5 retries+--+--     * Queue size of 1000+--+--     * Pool size of 2+--+--     * Annotate types set to False+--+--     * DailyIndexSharding+defaultEsScribeCfg' :: ESVersion v => proxy v -> EsScribeCfg v+defaultEsScribeCfg' prx = EsScribeCfg {+      essRetryPolicy     = exponentialBackoff 25 <> limitRetries 5+    , essQueueSize       = EsQueueSize 1000+    , essPoolSize        = EsPoolSize 2+    , essAnnotateTypes   = False+    , essIndexSettings   = defaultIndexSettings prx+    , essIndexSharding   = DailyIndexSharding+    }+++-------------------------------------------------------------------------------+-- | Alias of 'defaultEsScribeCfgV1' to minimize API+-- breakage. Previous versions of katip-elasticsearch only supported+-- ES version 1.+defaultEsScribeCfg :: EsScribeCfg ESV1+defaultEsScribeCfg = defaultEsScribeCfgV1+++-------------------------------------------------------------------------------+-- | EsScribeCfg that will use ElasticSearch V1+defaultEsScribeCfgV1 :: EsScribeCfg ESV1+defaultEsScribeCfgV1 = defaultEsScribeCfg' (Typeable.Proxy :: Typeable.Proxy ESV1)+++-------------------------------------------------------------------------------+-- | EsScribeCfg that will use ElasticSearch V5+defaultEsScribeCfgV5 :: EsScribeCfg ESV5+defaultEsScribeCfgV5 = defaultEsScribeCfg' (Typeable.Proxy :: Typeable.Proxy ESV5)+++-------------------------------------------------------------------------------+-- | How should katip store your log data?+--+-- * NoIndexSharding will store all logs in one index name. This is+-- the simplest option but is not advised in production. In practice,+-- the index will grow very large and will get slower to+-- search. Deleting records based on some sort of retention period is+-- also extremely slow.+--+-- * MonthlyIndexSharding, DailyIndexSharding, HourlyIndexSharding,+-- EveryMinuteIndexSharding will generate indexes based on the time of+-- the log. Index name is treated as a prefix. So if your index name+-- is @foo@ and DailySharding is used, logs will be stored in+-- @foo-2016-2-25@, @foo-2016-2-26@ and so on. Index templating will+-- be used to set up mappings automatically. Deletes based on date are+-- very fast and queries can be restricted to date ranges for better+-- performance. Queries against all dates should use @foo-*@ as an+-- index name. Note that index aliasing's glob feature is not suitable+-- for these date ranges as it matches index names as they are+-- declared, so new dates will be excluded. DailyIndexSharding is a+-- reasonable choice. Changing index sharding strategies is not+-- advisable.+--+-- * CustomSharding: supply your own function that decomposes an item+-- into its index name hierarchy which will be appended to the index+-- name. So for instance if your function return ["arbitrary",+-- "prefix"], the index will be @foo-arbitrary-prefix@ and the index+-- template will be set to match @foo-*@. In general, you want to use+-- segments of increasing granularity (like year, month, day for+-- dates). This makes it easier to address groups of indexes+-- (e.g. @foo-2016-*@).+data IndexShardingPolicy = NoIndexSharding+                         | MonthlyIndexSharding+                         | WeeklyIndexSharding+                         -- ^ A special case of daily which shards to sunday+                         | DailyIndexSharding+                         | HourlyIndexSharding+                         | EveryMinuteIndexSharding+                         | CustomIndexSharding (forall a. Item a -> [IndexNameSegment])+++instance Show IndexShardingPolicy where+ show NoIndexSharding          = "NoIndexSharding"+ show MonthlyIndexSharding     = "MonthlyIndexSharding"+ show WeeklyIndexSharding      = "WeeklyIndexSharding"+ show DailyIndexSharding       = "DailyIndexSharding"+ show HourlyIndexSharding      = "HourlyIndexSharding"+ show EveryMinuteIndexSharding = "EveryMinuteIndexSharding"+ show (CustomIndexSharding _)  = "CustomIndexSharding λ"+++-------------------------------------------------------------------------------+newtype IndexNameSegment = IndexNameSegment {+      indexNameSegment :: Text+    } deriving (Show, Eq, Ord)+++-------------------------------------------------------------------------------+shardPolicySegs :: IndexShardingPolicy -> Item a -> [IndexNameSegment]+shardPolicySegs NoIndexSharding _ = []+shardPolicySegs MonthlyIndexSharding Item {..} = [sis y, sis m]+  where+    (y, m, _) = toGregorian (utctDay _itemTime)+shardPolicySegs WeeklyIndexSharding Item {..} = [sis y, sis m, sis d]+  where+    (y, m, d) = toGregorian (roundToSunday (utctDay _itemTime))+shardPolicySegs DailyIndexSharding Item {..} = [sis y, sis m, sis d]+  where+    (y, m, d) = toGregorian (utctDay _itemTime)+shardPolicySegs HourlyIndexSharding Item {..} = [sis y, sis m, sis d, sis h]+  where+    (y, m, d) = toGregorian (utctDay _itemTime)+    (h, _) = splitTime (utctDayTime _itemTime)+shardPolicySegs EveryMinuteIndexSharding Item {..} = [sis y, sis m, sis d, sis h, sis mn]+  where+    (y, m, d) = toGregorian (utctDay _itemTime)+    (h, mn) = splitTime (utctDayTime _itemTime)+shardPolicySegs (CustomIndexSharding f) i  = f i+++-------------------------------------------------------------------------------+-- | If the given day is sunday, returns the input, otherwise returns+-- the previous sunday+roundToSunday :: Day -> Day+roundToSunday d+    | dow == 7  = d+    | w > 1     = fromWeekDate y (w - 1) 7+    | otherwise = fromWeekDate (y - 1) 53 7+  where+    (y, w, dow) = toWeekDate d+++-------------------------------------------------------------------------------+chooseIxn :: ESVersion v => proxy v -> IndexName v -> IndexShardingPolicy -> Item a -> IndexName v+chooseIxn prx ixn p i =+  toIndexName prx (T.intercalate "-" ((fromIndexName prx ixn):segs))+  where+    segs = indexNameSegment A.<$> shardPolicySegs p i+++-------------------------------------------------------------------------------+sis :: Integral a => a -> IndexNameSegment+sis = IndexNameSegment . T.pack . fmt+  where+    fmt = printf "%02d" . toInteger+++-------------------------------------------------------------------------------+splitTime :: DiffTime -> (Int, Int)+splitTime t = asMins `divMod` 60+  where+    asMins = floor t `div` 60+++-------------------------------------------------------------------------------+data EsScribeSetupError = CouldNotCreateIndex !(Response ByteString)+                        | CouldNotCreateMapping !(Response ByteString) deriving (Typeable, Show)+++instance Exception EsScribeSetupError+++-------------------------------------------------------------------------------+-- | The Any field tagged with a @v@ corresponds to the type of the+-- same name in the corresponding @bloodhound@ module. For instance,+-- if you are configuring for ElasticSearch version 1, import+-- @Database.V1.Bloodhound@ and @BHEnv v@ will refer to @BHEnv@ from+-- that module, @IndexName v@ will repsond to @IndexName@ from that+-- module, etc.+mkEsScribe+    :: forall v. ( ESVersion v+                 , MonadIO (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+                 , Functor (BH v IO)+#endif+                 )+    => EsScribeCfg v+    -> BHEnv v+    -> IndexName v+    -- ^ Treated as a prefix if index sharding is enabled+    -> MappingName v+    -> Severity+    -> Verbosity+    -> IO Scribe+mkEsScribe cfg@EsScribeCfg {..} env ix mapping sev verb = do+  q <- newTBMQueueIO $ unEsQueueSize essQueueSize+  endSig <- newEmptyMVar++  runBH prx env $ do+    chk <- indexExists prx ix+    -- note that this doesn't update settings. That's not available+    -- through the Bloodhound API yet+    unless chk $ void $ do+      r1 <- createIndex prx essIndexSettings ix+      unless (statusIsSuccessful (responseStatus r1)) $+        liftIO $ throwIO (CouldNotCreateIndex r1)+      r2 <- if shardingEnabled+              then putTemplate prx tpl tplName+              else putMapping prx ix mapping base+      unless (statusIsSuccessful (responseStatus r2)) $+        liftIO $ throwIO (CouldNotCreateMapping r2)++  workers <- replicateM (unEsPoolSize essPoolSize) $ async $+    startWorker cfg env mapping q++  _ <- async $ do+    takeMVar endSig+    atomically $ closeTBMQueue q+    mapM_ waitCatch workers+    putMVar endSig ()++  let finalizer = putMVar endSig () >> takeMVar endSig+  return (Scribe (logger q) finalizer)+  where+    logger :: forall a. LogItem a => TBMQueue (IndexName v, Value) -> Item a -> IO ()+    logger q i = when (_itemSeverity i >= sev) $+      void $ atomically $ tryWriteTBMQueue q (chooseIxn prx ix essIndexSharding i, itemJson' i)+    prx :: Typeable.Proxy v+    prx = Typeable.Proxy+    tplName = toTemplateName prx ixn+    shardingEnabled = case essIndexSharding of+      NoIndexSharding -> False+      _               -> True+    tpl = toIndexTemplate prx (toTemplatePattern prx (ixn <> "-*")) (Just essIndexSettings) [toJSON base]+    base = baseMapping prx mapping+    ixn = fromIndexName prx ix+    itemJson' :: LogItem a => Item a -> Value+    itemJson' i+      | essAnnotateTypes = itemJson verb (TypeAnnotated <$> i)+      | otherwise        = itemJson verb i+++-------------------------------------------------------------------------------+baseMapping :: ESVersion v => proxy v -> MappingName v -> Value+baseMapping prx mn =+  object [ fromMappingName prx mn .= object ["properties" .= object prs] ]+  where prs = [ str "thread"+              , str "sev"+              , str "pid"+              , str "ns"+              , str "msg"+              , "loc" .= locType+              , str "host"+              , str "env"+              , "at" .= dateType+              , str "app"+              ]+        str k = k .= object ["type" .= String "string"]+        locType = object ["properties" .= object locPairs]+        locPairs = [ str "loc_pkg"+                   , str "loc_mod"+                   , str "loc_ln"+                   , str "loc_fn"+                   , str "loc_col"+                   ]+        dateType = object [ "format" .= esDateFormat+                          , "type" .= String "date"+                          ]+++-------------------------------------------------------------------------------+-- | Handle both old-style aeson and picosecond-level precision+esDateFormat :: Text+esDateFormat = "yyyy-MM-dd'T'HH:mm:ssZ||yyyy-MM-dd'T'HH:mm:ss.SSSZ||yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSSSSZ"+++-------------------------------------------------------------------------------+mkDocId :: ESVersion v => proxy v -> IO (DocId v)+mkDocId prx = (toDocId prx . T.decodeUtf8 . toASCIIBytes) `fmap` UUID4.nextRandom+++-------------------------------------------------------------------------------+newtype EsQueueSize = EsQueueSize {+       unEsQueueSize :: Int+     } deriving (Show, Eq, Ord)+++instance Bounded EsQueueSize where+  minBound = EsQueueSize 1+  maxBound = EsQueueSize maxBound+++mkEsQueueSize :: Int -> Maybe EsQueueSize+mkEsQueueSize = mkNonZero EsQueueSize+++-------------------------------------------------------------------------------+newtype EsPoolSize = EsPoolSize {+      unEsPoolSize :: Int+    } deriving (Show, Eq, Ord)+++instance Bounded EsPoolSize where+  minBound = EsPoolSize 1+  maxBound = EsPoolSize maxBound+++mkEsPoolSize :: Int -> Maybe EsPoolSize+mkEsPoolSize = mkNonZero EsPoolSize+++-------------------------------------------------------------------------------+mkNonZero :: (Int -> a) -> Int -> Maybe a+mkNonZero ctor n+  | n > 0     = Just $ ctor n+  | otherwise = Nothing+++-------------------------------------------------------------------------------+startWorker+    :: forall v. (ESVersion v)+    => EsScribeCfg v+    -> BHEnv v+    -> MappingName v+    -> TBMQueue (IndexName v, Value)+    -> IO ()+startWorker EsScribeCfg {..} env mapping q = go+  where+    go = do+      popped <- atomically $ readTBMQueue q+      case popped of+        Just (ixn, v) -> do+          sendLog ixn v `catchAny` eat+          go+        Nothing -> return ()+    prx :: Typeable.Proxy v+    prx = Typeable.Proxy+    sendLog :: IndexName v -> Value -> IO ()+    sendLog ixn v = void $ recovering essRetryPolicy [handler] $ const $ do+      did <- mkDocId prx+      res <- runBH prx env $ indexDocument prx ixn mapping (defaultIndexDocumentSettings prx) v did+      return res+    eat _ = return ()+    handler _ = Handler $ \e ->+      case fromException e of+        Just (_ :: AsyncException) -> return False+        _                          -> return True+++-------------------------------------------------------------------------------+-- We are spanning multiple versions of ES which use completely+-- separate types and APIs, but the subset we use is the same for both+-- versions. This will be kept up to date with bloodhound's supported+-- versions and should be minimally visible to the end user.+class ESVersion v where+  -- Types+  type BHEnv v+  type IndexSettings v+  defaultIndexSettings :: proxy v -> IndexSettings v+  type IndexName v+  toIndexName :: proxy v -> Text -> IndexName v+  fromIndexName :: proxy v -> IndexName v -> Text+  type MappingName v+  fromMappingName :: proxy v -> MappingName v -> Text+  type DocId v+  toDocId :: proxy v -> Text -> DocId v+  type BH v :: (* -> *) -> * -> *+  runBH :: proxy v -> BHEnv v -> BH v m a -> m a+  type TemplateName v+  toTemplateName :: proxy v -> Text -> TemplateName v+  type TemplatePattern v+  toTemplatePattern :: proxy v -> Text -> TemplatePattern v+  type IndexTemplate v+  toIndexTemplate :: proxy v -> TemplatePattern v -> Maybe (IndexSettings v) -> [Value] -> IndexTemplate v+  type IndexDocumentSettings v+  defaultIndexDocumentSettings :: proxy v -> IndexDocumentSettings v++  -- Operations+  -- We're deciding on IO here, but it isn't necessary+  indexExists :: proxy v -> IndexName v -> BH v IO Bool+  indexDocument :: ToJSON doc => proxy v -> IndexName v -> MappingName v -> IndexDocumentSettings v -> doc -> DocId v -> BH v IO (Response ByteString)+  createIndex :: proxy v -> IndexSettings v -> IndexName v -> BH v IO (Response ByteString)+  putTemplate :: proxy v -> IndexTemplate v -> TemplateName v -> BH v IO (Response ByteString)+  putMapping :: (ToJSON a) => proxy v -> IndexName v -> MappingName v -> a -> BH v IO (Response ByteString)+++data ESV1 = ESV1++instance ESVersion ESV1 where+  type BHEnv ESV1 = V1.BHEnv+  type IndexSettings ESV1 = V1.IndexSettings+  defaultIndexSettings _ = V1.defaultIndexSettings+  type IndexName ESV1 = V1.IndexName+  toIndexName _ = V1.IndexName+  fromIndexName _ (V1.IndexName x) = x+  type MappingName ESV1 = V1.MappingName+  fromMappingName _ (V1.MappingName x) = x+  type DocId ESV1 = V1.DocId+  toDocId _ = V1.DocId+  type BH ESV1 = V1.BH+  runBH _ = V1.runBH+  type TemplateName ESV1 = V1.TemplateName+  toTemplateName _ = V1.TemplateName+  type TemplatePattern ESV1 = V1.TemplatePattern+  toTemplatePattern _ = V1.TemplatePattern+  type IndexTemplate ESV1 = V1.IndexTemplate+  toIndexTemplate _ = V1.IndexTemplate+  type IndexDocumentSettings ESV1 = V1.IndexDocumentSettings+  defaultIndexDocumentSettings _ = V1.defaultIndexDocumentSettings+  indexExists _ = V1.indexExists+  indexDocument _ = V1.indexDocument+  createIndex _ = V1.createIndex+  putTemplate _ = V1.putTemplate+  putMapping _ = V1.putMapping+++data ESV5 = ESV5++instance ESVersion ESV5 where+  type BHEnv ESV5 = V5.BHEnv+  type IndexSettings ESV5 = V5.IndexSettings+  defaultIndexSettings _ = V5.defaultIndexSettings+  type IndexName ESV5 = V5.IndexName+  toIndexName _ = V5.IndexName+  fromIndexName _ (V5.IndexName x) = x+  type MappingName ESV5 = V5.MappingName+  fromMappingName _ (V5.MappingName x) = x+  type DocId ESV5 = V5.DocId+  toDocId _ = V5.DocId+  type BH ESV5 = V5.BH+  runBH _ = V5.runBH+  type TemplateName ESV5 = V5.TemplateName+  toTemplateName _ = V5.TemplateName+  type TemplatePattern ESV5 = V5.TemplatePattern+  toTemplatePattern _ = V5.TemplatePattern+  type IndexTemplate ESV5 = V5.IndexTemplate+  toIndexTemplate _ = V5.IndexTemplate+  type IndexDocumentSettings ESV5 = V5.IndexDocumentSettings+  defaultIndexDocumentSettings _ = V5.defaultIndexDocumentSettings+  indexExists _ = V5.indexExists+  indexDocument _ = V5.indexDocument+  createIndex _ = V5.createIndex+  putTemplate _ = V5.putTemplate+  putMapping _ = V5.putMapping
test/Main.hs view
@@ -1,7 +1,13 @@+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE DeriveDataTypeable  #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE KindSignatures      #-} {-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE RankNTypes          #-} {-# LANGUAGE RecordWildCards     #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell     #-}+{-# LANGUAGE TypeFamilies        #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Main     ( main@@ -9,117 +15,258 @@   --------------------------------------------------------------------------------import           Control.Applicative         as A+import           Control.Applicative                     as A import           Control.Concurrent.STM-import           Control.Lens                hiding (mapping, (.=))+import           Control.Lens                            hiding (mapping, (.=)) import           Control.Monad import           Control.Monad.IO.Class import           Data.Aeson import           Data.Aeson.Lens import           Data.Aeson.Types-import qualified Data.HashMap.Strict         as HM-import qualified Data.Map                    as M+import           Data.ByteString.Lazy                    (ByteString)+import qualified Data.HashMap.Strict                     as HM import           Data.Monoid import           Data.Scientific+import           Data.Tagged+import           Data.Text                               (Text) import           Data.Time import           Data.Time.Calendar.WeekDate-import qualified Data.Vector                 as V-import           Database.Bloodhound         hiding (key)+import           Data.Typeable                           as Typeable+import qualified Data.Vector                             as V+import qualified Database.V1.Bloodhound                  as V1+import qualified Database.V5.Bloodhound                  as V5 import           Network.HTTP.Client import           Network.HTTP.Types.Status-import           Test.QuickCheck.Instances   ()+import           Test.QuickCheck.Instances               () import           Test.Tasty import           Test.Tasty.HUnit+import           Test.Tasty.Options import           Test.Tasty.QuickCheck ------------------------------------------------------------------------------- import           Katip-import           Katip.Scribes.ElasticSearch+import           Katip.Scribes.ElasticSearch.Annotations+import           Katip.Scribes.ElasticSearch.Internal -------------------------------------------------------------------------------   main :: IO ()-main = defaultMain $ testGroup "katip-elasticsearch"+main = defaultMainWithIngredients ings $ askOption $ \vers -> testGroup "katip-elasticsearch"   [-    esTests+    case vers of+      TestV1 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV1)+      TestV5 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV5)   , typeAnnotatedTests   , roundToSundayTests   ]+  where+    ings = (includingOptions [Option (Typeable.Proxy :: Typeable.Proxy TestWithESVersion)]):defaultIngredients   --------------------------------------------------------------------------------setupSearch :: (EsScribeCfg -> EsScribeCfg) -> IO (Scribe, IO ())-setupSearch modScribeCfg = do-    bh dropESSchema+data TestWithESVersion = TestV1+                       | TestV5+                       deriving (Typeable)+++instance IsOption TestWithESVersion where+  defaultValue = TestV1+  parseValue "1" = Just TestV1+  parseValue "5" = Just TestV5+  parseValue _   = Nothing+  optionName = Tagged "es-version"+  optionHelp = Tagged "Version of ES to test against, either 1 or 5, defaulting to 1."+++class ESVersion v => TestESVersion v where+  type Server v+  toServer :: proxy v -> Text -> Server v+  toMappingName :: proxy v -> Text -> MappingName v+  type Search v+  type Query v+  type Filter v+  mkSearch :: proxy v -> Maybe (Query v) -> Maybe (Filter v) -> Search v+  mkBHEnv :: proxy v -> Server v -> Manager -> BHEnv v+  type ShardCount v+  toShardCount :: proxy v -> Int -> ShardCount v+  type ReplicaCount v+  toReplicaCount :: proxy v -> Int -> ReplicaCount v+  indexShards :: proxy v -> Lens' (IndexSettings v) (ShardCount v)+  indexReplicas :: proxy v -> Lens' (IndexSettings v) (ReplicaCount v)++  deleteIndex :: proxy v -> IndexName v -> BH v IO (Response ByteString)+  deleteTemplate :: proxy v -> TemplateName v -> BH v IO (Response ByteString)+  refreshIndex :: proxy v -> IndexName v -> BH v IO (Response ByteString)+  withBH :: proxy v -> ManagerSettings -> Server v -> BH v IO a -> IO a+  searchByIndex :: proxy v -> IndexName v -> Search v -> BH v IO (Response ByteString)+++instance TestESVersion ESV1 where+  type Server ESV1 = V1.Server+  toServer _ = V1.Server+  toMappingName _ = V1.MappingName+  type Search ESV1 = V1.Search+  type Query ESV1 = V1.Query+  type Filter ESV1 = V1.Filter+  type ShardCount ESV1 = V1.ShardCount+  toShardCount _ = V1.ShardCount+  type ReplicaCount ESV1 = V1.ReplicaCount+  toReplicaCount _ = V1.ReplicaCount+  mkSearch _ = V1.mkSearch+  mkBHEnv _ = V1.mkBHEnv+  indexShards _ = lens V1.indexShards (\s v -> s { V1.indexShards = v})+  indexReplicas _ = lens V1.indexReplicas (\r v -> r { V1.indexReplicas = v})++  deleteIndex _ = V1.deleteIndex+  deleteTemplate _ = V1.deleteTemplate+  refreshIndex _ = V1.refreshIndex+  withBH _ = V1.withBH+  searchByIndex _ = V1.searchByIndex+++instance TestESVersion ESV5 where+  type Server ESV5 = V5.Server+  toServer _ = V5.Server+  toMappingName _ = V5.MappingName+  type Search ESV5 = V5.Search+  type Query ESV5 = V5.Query+  type Filter ESV5 = V5.Filter+  type ShardCount ESV5 = V5.ShardCount+  toShardCount _ = V5.ShardCount+  type ReplicaCount ESV5 = V5.ReplicaCount+  toReplicaCount _ = V5.ReplicaCount+  mkSearch _ = V5.mkSearch+  mkBHEnv _ = V5.mkBHEnv+  indexShards _ = lens V5.indexShards (\s v -> s { V5.indexShards = v})+  indexReplicas _ = lens V5.indexReplicas (\r v -> r { V5.indexReplicas = v})++  deleteIndex _ = V5.deleteIndex+  deleteTemplate _ = V5.deleteTemplate+  refreshIndex _ = V5.refreshIndex+  withBH _ = V5.withBH+  searchByIndex _ = V5.searchByIndex+++-------------------------------------------------------------------------------+setupSearch+  :: forall proxy v. ( TestESVersion v+                     , MonadIO (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+                     , Functor (BH v IO)+#endif+                     )+  => proxy v+  -> (EsScribeCfg v -> EsScribeCfg v)+  -> IO Scribe+setupSearch prx modScribeCfg = do+    bh prx (dropESSchema prx)     mgr <- newManager defaultManagerSettings-    mkEsScribe cfg (mkBHEnv svr mgr) ixn mn DebugS V3+    mkEsScribe cfg (mkBHEnv prx (svr prx) mgr) (ixn prx) (mn prx) DebugS V3   where-    cfg = modScribeCfg (defaultEsScribeCfg { essAnnotateTypes = True-                                           , essIndexSettings = ixs-                                           })+    cfg :: EsScribeCfg v+    cfg = modScribeCfg $+      (defaultEsScribeCfg' prx)+        { essAnnotateTypes = True+        , essIndexSettings = ixs prx+        }   --------------------------------------------------------------------------------teardownSearch :: (Scribe, IO ()) -> IO ()-teardownSearch (_, finalizer) = do-  finalizer-  bh $ do-    when False $ dropESSchema-    when False $ dropESSTemplate --TODO: drop+teardownSearch+  :: ( TestESVersion v+     , Monad (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+     , Functor (BH v IO)+#endif+     )+  => proxy v+  -> IO ()+teardownSearch prx = do+  bh prx $ do+    dropESSchema prx+    dropESSTemplate prx   --------------------------------------------------------------------------------withSearch :: (IO (Scribe, IO ()) -> TestTree) -> TestTree+withSearch+  :: ( TestESVersion v+     , MonadIO (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+     , Functor (BH v IO)+#endif+     )+  => proxy v+  -> (IO Scribe -> TestTree)+  -> TestTree withSearch = withSearch' id   --------------------------------------------------------------------------------withSearch' :: (EsScribeCfg -> EsScribeCfg) -> (IO (Scribe, IO ()) -> TestTree) -> TestTree-withSearch' modScribeCfg = withResource (setupSearch modScribeCfg) teardownSearch+withSearch'+  :: ( TestESVersion v+     , MonadIO (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+     , Functor (BH v IO)+#endif+     )+  => (EsScribeCfg v -> EsScribeCfg v)+  -> proxy v+  -> (IO Scribe -> TestTree)+  -> TestTree+withSearch' modScribeCfg prx = withResource (setupSearch prx modScribeCfg) (const (teardownSearch prx))   --------------------------------------------------------------------------------esTests :: TestTree-esTests = testGroup "elasticsearch scribe"+esTests+  :: ( TestESVersion v+     , MonadIO (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+     , Functor (BH v IO)+#endif+     , Show (IndexName v)+     )+  => proxy v+  -> TestTree+esTests prx = testGroup "elasticsearch scribe"   [-    withSearch' (\c -> c { essIndexSharding = NoIndexSharding}) $ \setup -> testCase "it flushes to elasticsearch" $ withTestLogging setup $ \done -> do+    withSearch' (\c -> c { essIndexSharding = NoIndexSharding}) prx $ \setup -> testCase "it flushes to elasticsearch" $ withTestLogging prx setup $ \done -> do        $(logT) (ExampleCtx True) mempty InfoS "A test message"        liftIO $ do          void done-         logs <- getLogs+         logs <- getLogs prx          length logs @?= 1          let l = head logs          l ^? key "_source" . key "msg" . _String @?= Just "A test message"          l ^? key "_source" . key "data" . key "whatever::b" . _Bool @?= Just True-  , withSearch $ \setup -> testCase "date-based index sharding" $ do+  , withSearch prx $ \setup -> testCase "date-based index sharding" $ do       let t1 = mkTime 2016 1 2 3 4 5       fakeClock <- newTVarIO t1-      withTestLogging' (set logEnvTimer (readTVarIO fakeClock)) setup $ \done -> do+      withTestLogging' (set logEnvTimer (readTVarIO fakeClock)) prx setup $ \done -> do         $(logT) (ExampleCtx True) mempty InfoS "today"         let t2 = mkTime 2016 1 3 3 4 5         liftIO (atomically (writeTVar fakeClock t2))         $(logT) (ExampleCtx True) mempty InfoS "tomorrow"         liftIO $ do           void done-          todayLogs <- getLogsByIndex (IndexName "katip-elasticsearch-tests-2016-01-02")-          tomorrowLogs <- getLogsByIndex (IndexName "katip-elasticsearch-tests-2016-01-03")+          todayLogs <- getLogsByIndex prx (toIndexName prx "katip-elasticsearch-tests-2016-01-02")+          tomorrowLogs <- getLogsByIndex prx (toIndexName prx "katip-elasticsearch-tests-2016-01-03")           assertBool ("todayLogs has " <> show (length todayLogs) <> " items") (length todayLogs == 1)           assertBool ("tomorrowLogs has " <> show (length tomorrowLogs) <> " items") (length tomorrowLogs == 1)           let logToday = head todayLogs           let logTomorrow = head tomorrowLogs           logToday ^? key "_source" . key "msg" . _String @?= Just "today"           logTomorrow ^? key "_source" . key "msg" . _String @?= Just "tomorrow"-  , withSearch' (\c -> c { essIndexSharding = WeeklyIndexSharding}) $ \setup -> testCase "weekly index sharding rounds to previous sunday" $ do+  , withSearch' (\c -> c { essIndexSharding = WeeklyIndexSharding}) prx $ \setup -> testCase "weekly index sharding rounds to previous sunday" $ do       let t1 = mkTime 2016 3 5 0 0 0 -- saturday, march 5th       fakeClock <- newTVarIO t1-      withTestLogging' (set logEnvTimer (readTVarIO fakeClock)) setup $ \done -> do+      withTestLogging' (set logEnvTimer (readTVarIO fakeClock)) prx setup $ \done -> do         $(logT) (ExampleCtx True) mempty InfoS "today"         let t2 = mkTime 2016 3 6 0 0 0 -- sunday march 6th         liftIO (atomically (writeTVar fakeClock t2))         $(logT) (ExampleCtx True) mempty InfoS "tomorrow"         liftIO $ do           void done-          todayLogs <- getLogsByIndex (IndexName "katip-elasticsearch-tests-2016-02-28") -- rounds back to previous sunday-          tomorrowLogs <- getLogsByIndex (IndexName "katip-elasticsearch-tests-2016-03-06") -- is on sunday, so uses current date+          todayLogs <- getLogsByIndex prx (toIndexName prx "katip-elasticsearch-tests-2016-02-28") -- rounds back to previous sunday+          tomorrowLogs <- getLogsByIndex prx (toIndexName prx "katip-elasticsearch-tests-2016-03-06") -- is on sunday, so uses current date           assertBool ("todayLogs has " <> show (length todayLogs) <> " items") (length todayLogs == 1)           assertBool ("tomorrowLogs has " <> show (length tomorrowLogs) <> " items") (length tomorrowLogs == 1)           let logToday = head todayLogs@@ -232,76 +379,127 @@   --------------------------------------------------------------------------------getLogs :: IO [Value]-getLogs = getLogsByIndex ixn+getLogs+  :: ( TestESVersion v+     , Monad (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+     , Functor (BH v IO)+#endif+     , Show (IndexName v)+     )+  => proxy v+  -> IO [Value]+getLogs prx = getLogsByIndex prx (ixn prx)   --------------------------------------------------------------------------------getLogsByIndex :: IndexName -> IO [Value]-getLogsByIndex i = do-  r <- bh $ do-    void (refreshIndex i)-    searchByIndex i (mkSearch Nothing Nothing)+getLogsByIndex+  :: ( TestESVersion v+     , Monad (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+     , Functor (BH v IO)+#endif+     , Show (IndexName v)+     )+  => proxy v+  -> IndexName v+  -> IO [Value]+getLogsByIndex prx i = do+  r <- bh prx $ do+    void (refreshIndex prx i)+    searchByIndex prx i (mkSearch prx Nothing Nothing)   let actualCode = statusCode (responseStatus r)   assertBool ("search by " <> show i <> " " <> show actualCode <> " /= 200") (actualCode == 200)   return $ responseBody r ^.. key "hits" . key "hits" . values   --------------------------------------------------------------------------------bh :: BH IO a -> IO a-bh = withBH defaultManagerSettings svr+bh :: TestESVersion v => proxy v -> BH v IO a -> IO a+bh prx = withBH prx defaultManagerSettings (svr prx)   ------------------------------------------------------------------------------- withTestLogging-  :: IO (Scribe, IO a) -> (IO Reply -> KatipT IO b) -> IO b+  :: TestESVersion v+  => proxy v+  -> IO Scribe+  -> (IO (Response ByteString) -> KatipT IO b)+  -> IO b withTestLogging = withTestLogging' id   ------------------------------------------------------------------------------- withTestLogging'-  :: (LogEnv -> LogEnv)-  -> IO (Scribe, IO a)-  -> (IO Reply -> KatipT IO b)+  :: (TestESVersion v)+  => (LogEnv -> LogEnv)+  -> proxy v+  -> IO Scribe+  -> (IO (Response ByteString) -> KatipT IO b)   -> IO b-withTestLogging' modEnv setup f = do-  (scr, done) <- setup+withTestLogging' modEnv prx setup f = do+  scr <- setup   le <- modEnv <$> initLogEnv ns env-  let done' = done >> bh (refreshIndex ixn)-  runKatipT le { _logEnvScribes = M.singleton "es" scr} (f done')+  le' <- registerScribe "es" scr defaultScribeSettings le+  let done' = do+        _ <- closeScribes le'+        bh prx (refreshIndex prx (ixn prx))+  runKatipT le' (f done')   where     ns = Namespace ["katip-test"]     env = Environment "test"   --------------------------------------------------------------------------------svr :: Server-svr = Server "http://localhost:9200"+svr :: TestESVersion v => proxy v -> Server v+svr prx = toServer prx  "http://localhost:9200"   --------------------------------------------------------------------------------ixn :: IndexName-ixn = IndexName "katip-elasticsearch-tests"+ixn :: TestESVersion v => proxy v -> IndexName v+ixn prx = toIndexName prx "katip-elasticsearch-tests"   --------------------------------------------------------------------------------ixs :: IndexSettings-ixs = defaultIndexSettings { indexShards = ShardCount 1-                           , indexReplicas = ReplicaCount 1}+ixs :: TestESVersion v => proxy v -> IndexSettings v+ixs prx = defaultIndexSettings prx+  & indexShards prx .~ toShardCount prx 1+  & indexReplicas prx .~ toReplicaCount prx 1 + --------------------------------------------------------------------------------mn :: MappingName-mn = MappingName "logs"+tn :: TestESVersion v => proxy v -> TemplateName v+tn prx = toTemplateName prx "katip-elasticsearch-tests"   --------------------------------------------------------------------------------dropESSchema :: BH IO ()-dropESSchema = void $ deleteIndex (IndexName "katip-elasticsearch-tests*")+mn :: TestESVersion v => proxy v -> MappingName v+mn prx = toMappingName prx "logs"   --------------------------------------------------------------------------------dropESSTemplate :: BH IO ()-dropESSTemplate = void $ deleteTemplate (TemplateName "katip-elasticsearch-tests")+dropESSchema+  :: ( TestESVersion v+     , Monad (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+     , Functor (BH v IO)+#endif+     )+  => proxy v+  -> BH v IO ()+dropESSchema prx = void $ deleteIndex prx (toIndexName prx "katip-elasticsearch-tests*")+++-------------------------------------------------------------------------------+dropESSTemplate+  :: ( TestESVersion v+     , Monad (BH v IO)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 800+     , Functor (BH v IO)+#endif+     )+  => proxy v+  -> BH v IO ()+dropESSTemplate prx = void $ deleteTemplate prx (tn prx)   -------------------------------------------------------------------------------