on b4fd16f help sign in
23 files changed+436−319
since splitsnapshots
unifiedsplit
.gitignore−1 .travis.yml+9−8 katip-datadog/changelog.md−4 katip-datadog/katip-datadog.cabal+1−1 katip-elasticsearch/bench/Main.hs+3−3 katip-elasticsearch/changelog.md−10 katip-elasticsearch/docker-compose.yml−10 katip-elasticsearch/examples/example.hs+1−1 katip-elasticsearch/katip-elasticsearch.cabal+4−1 katip-elasticsearch/src/Katip/Scribes/ElasticSearch.hs+1−12 katip-elasticsearch/src/Katip/Scribes/ElasticSearch/Internal.hs+98−104 katip-elasticsearch/test/Main.hs+12−26 katip/README.md+1−1 katip/changelog.md−13 katip/katip.cabal+31−28 katip/src/Katip.hs+2−1 katip/src/Katip/Core.hs+23−36 katip/src/Katip/Monadic.hs+6−20 katip/src/Katip/Scribes/Handle.hs+64−26 katip/src/Katip/Scribes/Handle/FileOwner.hs+143 katip/test/Katip/Tests/Scribes/Handle.hs+6−8 stack-old.yaml+21 stack.yaml+10−5
.gitignore−1
@@ -7,4 +7,3 @@ TAGS
77*.hi
88*.dyn_o
99*.dyn_hi
10−stack.yaml.lock
.travis.yml+9−8
@@ -9,19 +9,21 @@ addons:
99 apt:
1010 packages:
1111 - libgmp-dev
12+ - oracle-java8-installer
1213
1314# The different configurations we want to test. You could also do things like
1415# change flags or use --stack-yaml to point to a different file.
1516matrix:
1617 include:
17− - env: ARGS="--resolver=lts-16" ESVERSION=5.5.0 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m"
18− - env: ARGS="--resolver=lts-16" ESVERSION=6.2.2 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m"
19− - env: ARGS="--resolver=nightly" ESVERSION=5.5.0 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m"
20− - env: ARGS="--resolver=nightly" ESVERSION=6.2.2 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m"
21− # nightlies may fail usually due to lagging dependencies
18+ - env: ARGS="--resolver=lts-10" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
19+ - env: ARGS="--resolver=lts-11" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
20+ - env: ARGS="--resolver=lts-12" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
21+ - env: ARGS="--resolver=nightly" ESVERSION=5.5.0 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m" JAVA_HOME=/usr/lib/jvm/java-8-oracle
22+ - env: ARGS="--resolver=nightly" ESVERSION=6.2.2 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m" JAVA_HOME=/usr/lib/jvm/java-8-oracle
23+ # nightlies may fail from time to time
2224 allow_failures:
23− - env: ARGS="--resolver=nightly" ESVERSION=5.5.0 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m"
24− - env: ARGS="--resolver=nightly" ESVERSION=6.2.2 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m"
25+ - env: ARGS="--resolver=nightly" ESVERSION=5.5.0 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m" JAVA_HOME=/usr/lib/jvm/java-8-oracle
26+ - env: ARGS="--resolver=nightly" ESVERSION=6.2.2 ESURL=https://artifacts.elastic.co/downloads/elasticsearch ESTESTARGS="--es-version 5" ES_JAVA_OPTS="-Xms500m -Xmx750m" JAVA_HOME=/usr/lib/jvm/java-8-oracle
2527
2628before_install:
2729# Download and unpack the stack executable
@@ -34,7 +36,6 @@ install:
3436 - tar xzf elasticsearch-$ESVERSION.tar.gz
3537 # set up a repo for snapshot testing. Required in ES >= 1.6
3638 - echo "path.repo = [\"/tmp\"]" >> ./elasticsearch-$ESVERSION/elasticsearch.yml
37− - java -version
3839 - ./elasticsearch-$ESVERSION/bin/elasticsearch &
3940 # Bloodhound seems to take a while to build and can sometimes stall out the build when travis detects no output.
4041
katip-datadog/changelog.md−4
@@ -1,7 +1,3 @@
1−0.2.0.0
2−==========
3−* Remove import of deprecated Network module
4−
510.1.0.0
62==========
73* Initial release
katip-datadog/katip-datadog.cabal+1−1
@@ -1,5 +1,5 @@
11name: katip-datadog
2−version: 0.2.0.0
2+version: 0.1.0.0
33synopsis: Datadog scribe for the Katip logging framework
44description: See README.md for more details.
55license: BSD3
katip-elasticsearch/bench/Main.hs+3−3
@@ -15,11 +15,11 @@ import qualified Data.HashMap.Strict as HM
1515import Data.Proxy (Proxy (..))
1616import System.Random
1717import qualified Data.Text as T
18−import Database.Bloodhound.Types
18+import Database.V1.Bloodhound.Types
1919import Numeric
2020-------------------------------------------------------------------------------
2121import Katip.Scribes.ElasticSearch
22−import Katip.Scribes.ElasticSearch.Internal (ESV5)
22+import Katip.Scribes.ElasticSearch.Internal (ESV1)
2323-------------------------------------------------------------------------------
2424
2525main :: IO ()
@@ -34,7 +34,7 @@ main = do
3434mkDocIdBenchmark :: Benchmark
3535mkDocIdBenchmark = bgroup "mkDocId"
3636 [
37− bench "mkDocId (randomIO)" $ nfIO (mkDocId (Proxy :: Proxy ESV5))
37+ bench "mkDocId (randomIO)" $ nfIO (mkDocId (Proxy :: Proxy ESV1))
3838 , bench "mkDocId' (shared )" $ nfIO mkDocId'
3939 ]
4040
katip-elasticsearch/changelog.md−10
@@ -1,13 +1,3 @@
1−0.7.0.0
2−=======
3−* Make compatible with unreleased bloodhound 0.17
4−* Deprecate use of ES version 1 which has been removed from bloodhound
5− 0.17. All users should move over to ESV5 which should work for
6− versions 5 and up.
7−* Default configuration to ESV5
8−* Add deprecation warnings for ESV1. Support for ESV1 will be removed
9− in the future.
10−
1110.6.0.0
122=======
133* Update to katip-0.8.0.0 `PermitFunc` convention. To update, for
katip-elasticsearch/docker-compose.ymldeleted−10
@@ -1,10 +0,0 @@
1−version: "3.7"
2−services:
3− # https://hub.docker.com/_/elasticsearch
4− elasticsearch:
5− image: elasticsearch:5-alpine
6− ports:
7− - target: 9200
8− published: 9200
9− protocol: tcp
10− mode: host
katip-elasticsearch/examples/example.hs+1−1
@@ -6,7 +6,7 @@ module Main
66
77-------------------------------------------------------------------------------
88import Control.Exception
9−import Database.Bloodhound
9+import Database.V5.Bloodhound
1010import Network.HTTP.Client
1111-------------------------------------------------------------------------------
1212import Katip
katip-elasticsearch/katip-elasticsearch.cabal+4−1
@@ -1,7 +1,7 @@
11name: katip-elasticsearch
22synopsis: ElasticSearch scribe for the Katip logging framework.
33description: See README.md for more details.
4−version: 0.7.0.0
4+version: 0.6.0.0
55license: BSD3
66license-file: LICENSE
77author: Ozgun Ataman, Michael Xavier
@@ -108,4 +108,7 @@ test-suite test
108108 , bytestring
109109 , tagged
110110
111+ if flag(lib-Werror)
112+ ghc-options: -Wall -Werror
113+
111114 ghc-options: -threaded -rtsopts
katip-elasticsearch/src/Katip/Scribes/ElasticSearch.hs+1−12
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
21-- | Includes a scribe that can be used to log structured, JSON log
32-- messages to ElasticSearch. These logs can be explored easily using
43-- <https://www.elastic.co/products/kibana kibana> or your tool of
@@ -60,24 +59,14 @@ module Katip.Scribes.ElasticSearch
6059 , IndexShardingPolicy(..)
6160 , IndexNameSegment(..)
6261 -- ** EsScribeCfg and fields
63− , EsScribeCfg
64− , essRetryPolicy
65− , essQueueSize
66− , essPoolSize
67− , essAnnotateTypes
68− , essIndexSettings
69− , essIndexSharding
62+ , EsScribeCfg(..)
7063 , defaultEsScribeCfg
71−#if !MIN_VERSION_bloodhound(0,17,0)
7264 , defaultEsScribeCfgV1
73−#endif
7465 , defaultEsScribeCfgV5
7566 -- ** Version-Proxied APIS
7667 -- $versionproxies
7768 , defaultEsScribeCfg'
78−#if !MIN_VERSION_bloodhound(0,17,0)
7969 , ESV1
80−#endif
8170 , ESV5
8271 -- * Utilities
8372 , mkDocId
katip-elasticsearch/src/Katip/Scribes/ElasticSearch/Internal.hs+98−104
@@ -28,7 +28,7 @@ import Control.Retry (RetryPolicy,
2828import Data.Aeson
2929import Data.ByteString.Lazy (ByteString)
3030import Data.List.NonEmpty (NonEmpty (..))
31−import Data.Monoid as Monoid
31+import Data.Monoid ((<>))
3232import Data.Text (Text)
3333import qualified Data.Text as T
3434import qualified Data.Text.Encoding as T
@@ -37,12 +37,8 @@ import Data.Time.Calendar.WeekDate
3737import Data.Typeable as Typeable
3838import Data.UUID
3939import qualified Data.UUID.V4 as UUID4
40−#if MIN_VERSION_bloodhound(0,17,0)
41−import qualified Database.Bloodhound as V5
42−#else
4340import qualified Database.V1.Bloodhound as V1
4441import qualified Database.V5.Bloodhound as V5
45−#endif
4642import Network.HTTP.Client
4743import Network.HTTP.Types.Status
4844import Text.Printf (printf)
@@ -57,30 +53,26 @@ import Katip.Scribes.ElasticSearch.Annotations
5753-- 'defaultEsScribeCfgV1' and 'defaultESScribeCfgV5' for a good
5854-- starting point depending on the ES version you have.
5955data EsScribeCfg v = EsScribeCfg {
60− essRetryPolicy :: RetryPolicy
56+ essRetryPolicy :: RetryPolicy
6157 -- ^ Retry policy when there are errors sending logs to the server
62− , essQueueSize :: EsQueueSize
58+ , essQueueSize :: EsQueueSize
6359 -- ^ Maximum size of the bounded log queue
64− , essPoolSize :: EsPoolSize
60+ , essPoolSize :: EsPoolSize
6561 -- ^ Worker pool size limit for sending data to the
66− , essAnnotateTypes :: Bool
67− -- ^ Different payload items coexist in the "data" attribute in
68− -- ES. It is possible for different payloads to have different
69− -- types for the same key, e.g. an "id" key that is sometimes a
70− -- number and sometimes a string. If you're having ES do dynamic
71− -- mapping, the first log item will set the type and any that
72− -- don't conform will be *discarded*. If you set this to True,
73− -- keys will recursively be appended with their ES core
74− -- type. e.g. "id" would become "id::l" and "id::s"
75− -- automatically, so they won't conflict. When this library
76− -- exposes a querying API, we will try to make deserialization and
77− -- querying transparently remove the type annotations if this is
78− -- enabled.
79− , essIndexSettings :: IndexSettings v
62+ , essItemFormatter :: Verbosity -> ItemFunc Value
63+ -- ^ The formatter of an item. Must format any item to json
64+ -- Value. You can use 'annotateValue' to get an effect of
65+ -- @essAnnotateTypes@ (removed) here.
66+ , essIndexName :: IndexName v
67+ , essIndexMapping :: MappingName v
68+ , essIndexMappingValue :: Value
69+ -- ^ The value of index mapping. The default is 'baseMapping' but
70+ -- user can override.
71+ , essIndexSettings :: IndexSettings v
8072 -- ^ This will be the IndexSettings type from the appropriate
8173 -- bloodhound module, either @Database.V1.Bloodhound@ or
8274 -- @Database.V5.Bloodhound@
83− , essIndexSharding :: IndexShardingPolicy
75+ , essIndexSharding :: IndexShardingPolicy
8476 } deriving (Typeable)
8577
8678
@@ -97,37 +89,47 @@ data EsScribeCfg v = EsScribeCfg {
9789-- * Annotate types set to False
9890--
9991-- * DailyIndexSharding
100−defaultEsScribeCfg' :: ESVersion v => proxy v -> EsScribeCfg v
101−defaultEsScribeCfg' prx = EsScribeCfg {
102− essRetryPolicy = exponentialBackoff 25000 Monoid.<> limitRetries 5
103− , essQueueSize = EsQueueSize 1000
104− , essPoolSize = EsPoolSize 2
105− , essAnnotateTypes = False
106− , essIndexSettings = defaultIndexSettings prx
107− , essIndexSharding = DailyIndexSharding
108− }
109−
110−
111−
92+defaultEsScribeCfg'
93+ :: ESVersion v
94+ => proxy v
95+ -> IndexName v
96+ -> MappingName v
97+ -> EsScribeCfg v
98+defaultEsScribeCfg' prx index mapping = EsScribeCfg
99+ { essRetryPolicy = exponentialBackoff 25000 <> limitRetries 5
100+ , essQueueSize = EsQueueSize 1000
101+ , essPoolSize = EsPoolSize 2
102+ , essItemFormatter = defaultItemFormatter
103+ , essIndexName = index
104+ , essIndexMapping = mapping
105+ , essIndexMappingValue = baseMapping prx mapping
106+ , essIndexSettings = defaultIndexSettings prx
107+ , essIndexSharding = DailyIndexSharding
108+ }
109+
110+defaultItemFormatter :: Verbosity -> ItemFunc Value
111+defaultItemFormatter verb = ItemFunc $ \item -> itemJson verb item
112+
113+annotatedItemFormatter :: Verbosity -> ItemFunc Value
114+annotatedItemFormatter verb = ItemFunc $ \item -> itemJson verb (TypeAnnotated <$> item)
112115
113116-------------------------------------------------------------------------------
114−-- | Alias of 'defaultEsScribeCfgV5' to minimize API
117+-- | Alias of 'defaultEsScribeCfgV1' to minimize API
115118-- breakage. Previous versions of katip-elasticsearch only supported
116−-- ES version 1 and defaulted to it.
117−defaultEsScribeCfg :: EsScribeCfg ESV5
118−defaultEsScribeCfg = defaultEsScribeCfgV5
119+-- ES version 1.
120+defaultEsScribeCfg :: IndexName ESV1 -> MappingName ESV1 -> EsScribeCfg ESV1
121+defaultEsScribeCfg = defaultEsScribeCfgV1
119122
120123
121124-------------------------------------------------------------------------------
122−#if !MIN_VERSION_bloodhound(0,17,0)
123125-- | EsScribeCfg that will use ElasticSearch V1
124−defaultEsScribeCfgV1 :: EsScribeCfg ESV1
126+defaultEsScribeCfgV1 :: IndexName ESV1 -> MappingName ESV1 -> EsScribeCfg ESV1
125127defaultEsScribeCfgV1 = defaultEsScribeCfg' (Typeable.Proxy :: Typeable.Proxy ESV1)
126−#endif
128+
127129
128130-------------------------------------------------------------------------------
129131-- | EsScribeCfg that will use ElasticSearch V5
130−defaultEsScribeCfgV5 :: EsScribeCfg ESV5
132+defaultEsScribeCfgV5 :: IndexName ESV5 -> MappingName ESV5 -> EsScribeCfg ESV5
131133defaultEsScribeCfgV5 = defaultEsScribeCfg' (Typeable.Proxy :: Typeable.Proxy ESV5)
132134
133135
@@ -272,66 +274,62 @@ mkEsScribe
272274 )
273275 => EsScribeCfg v
274276 -> BHEnv v
275− -> IndexName v
276− -- ^ Treated as a prefix if index sharding is enabled
277− -> MappingName v
278277 -> PermitFunc
279278 -> Verbosity
280279 -> IO Scribe
281−mkEsScribe cfg@EsScribeCfg {..} env ix mapping permit verb = do
282− q <- newTBMQueueIO $ unEsQueueSize essQueueSize
283− endSig <- newEmptyMVar
284−
285− runBH prx env $ do
286− if shardingEnabled
287− then do
288− -- create or update
289− res <- putTemplate prx tpl tplName
290− unless (statusIsSuccessful (responseStatus res)) $
291− liftIO $ EX.throwIO (CouldNotPutTemplate res)
292− else do
293− ixExists <- indexExists prx ix
294− if ixExists
295− then do
296− res <- updateIndexSettings prx (toUpdatabaleIndexSettings prx essIndexSettings) ix
297− unless (statusIsSuccessful (responseStatus res)) $
298− liftIO $ EX.throwIO (CouldNotUpdateIndexSettings res)
299− else do
300− r1 <- createIndex prx essIndexSettings ix
301− unless (statusIsSuccessful (responseStatus r1)) $
302− liftIO $ EX.throwIO (CouldNotCreateIndex r1)
303− r2 <- putMapping prx ix mapping base
304− unless (statusIsSuccessful (responseStatus r2)) $
305− liftIO $ EX.throwIO (CouldNotCreateMapping r2)
306−
307− workers <- replicateM (unEsPoolSize essPoolSize) $ async $
308− startWorker cfg env mapping q
309−
310− _ <- async $ do
311− takeMVar endSig
312− atomically $ closeTBMQueue q
313− mapM_ waitCatch workers
314− putMVar endSig ()
315−
316− let finalizer = putMVar endSig () >> takeMVar endSig
317− return (Scribe (logger q) finalizer permit)
318− where
319− logger :: forall a. LogItem a => TBMQueue (IndexName v, Value) -> Item a -> IO ()
320− logger q i =
321− void $ atomically $ tryWriteTBMQueue q (chooseIxn prx ix essIndexSharding i, itemJson' i)
322− prx :: Typeable.Proxy v
323− prx = Typeable.Proxy
324− tplName = toTemplateName prx ixn
325− shardingEnabled = case essIndexSharding of
326− NoIndexSharding -> False
327− _ -> True
328− tpl = toIndexTemplate prx (toTemplatePattern prx (ixn <> "-*")) (Just essIndexSettings) [toJSON base]
329− base = baseMapping prx mapping
330− ixn = fromIndexName prx ix
331− itemJson' :: LogItem a => Item a -> Value
332− itemJson' i
333− | essAnnotateTypes = itemJson verb (TypeAnnotated <$> i)
334− | otherwise = itemJson verb i
280+mkEsScribe cfg@EsScribeCfg {..} env permit verb = error "fuck"
281+ -- q <- newTBMQueueIO $ unEsQueueSize essQueueSize
282+ -- endSig <- newEmptyMVar
283+
284+ -- runBH prx env $ do
285+ -- if shardingEnabled
286+ -- then do
287+ -- -- create or update
288+ -- res <- putTemplate prx tpl tplName
289+ -- unless (statusIsSuccessful (responseStatus res)) $
290+ -- liftIO $ EX.throwIO (CouldNotPutTemplate res)
291+ -- else do
292+ -- ixExists <- indexExists prx ix
293+ -- if ixExists
294+ -- then do
295+ -- res <- updateIndexSettings prx (toUpdatabaleIndexSettings prx essIndexSettings) ix
296+ -- unless (statusIsSuccessful (responseStatus res)) $
297+ -- liftIO $ EX.throwIO (CouldNotUpdateIndexSettings res)
298+ -- else do
299+ -- r1 <- createIndex prx essIndexSettings ix
300+ -- unless (statusIsSuccessful (responseStatus r1)) $
301+ -- liftIO $ EX.throwIO (CouldNotCreateIndex r1)
302+ -- r2 <- putMapping prx ix mapping mappingValue
303+ -- unless (statusIsSuccessful (responseStatus r2)) $
304+ -- liftIO $ EX.throwIO (CouldNotCreateMapping r2)
305+
306+ -- workers <- replicateM (unEsPoolSize essPoolSize) $ async $
307+ -- startWorker cfg env mapping q
308+
309+ -- _ <- async $ do
310+ -- takeMVar endSig
311+ -- atomically $ closeTBMQueue q
312+ -- mapM_ waitCatch workers
313+ -- putMVar endSig ()
314+
315+ -- let finalizer = putMVar endSig () >> takeMVar endSig
316+ -- return (Scribe (logger q) finalizer permit)
317+ -- where
318+ -- logger :: forall a. LogItem a => TBMQueue (IndexName v, Value) -> Item a -> IO ()
319+ -- logger q i =
320+ -- void $ atomically $ tryWriteTBMQueue q (chooseIxn prx ix essIndexSharding i, itemJson' i)
321+ -- prx :: Typeable.Proxy v
322+ -- prx = Typeable.Proxy
323+ -- tplName = toTemplateName prx ixn
324+ -- shardingEnabled = case essIndexSharding of
325+ -- NoIndexSharding -> False
326+ -- _ -> True
327+ -- tpl = toIndexTemplate prx (toTemplatePattern prx (ixn <> "-*")) (Just essIndexSettings) [mappingValue]
328+ -- ixn = fromIndexName prx ix
329+ -- itemJson' :: LogItem a => Item a -> Value
330+ -- itemJson' i
331+ -- | essAnnotateTypes = itemJson verb (TypeAnnotated <$> i)
332+ -- | otherwise = itemJson verb i
335333
336334
337335-------------------------------------------------------------------------------
@@ -491,10 +489,7 @@ class ESVersion v where
491489
492490
493491
494−#if !MIN_VERSION_bloodhound(0,17,0)
495492data ESV1 = ESV1
496−{-# DEPRECATED ESV1 "ESV1 is deprecated and removed in bloodhound >= 0.17.0" #-}
497−
498493
499494instance ESVersion ESV1 where
500495 type BHEnv ESV1 = V1.BHEnv
@@ -534,7 +529,6 @@ instance ESVersion ESV1 where
534529 [ "type" .= String "string"
535530 , "index" .= String "analyzed"
536531 ]
537−#endif
538532
539533
540534data ESV5 = ESV5
katip-elasticsearch/test/Main.hs+12−26
@@ -34,12 +34,8 @@ import Data.Time
3434import Data.Time.Calendar.WeekDate
3535import Data.Typeable as Typeable
3636import qualified Data.Vector as V
37−#if MIN_VERSION_bloodhound(0,17,0)
38−import qualified Database.Bloodhound as V5
39−#else
4037import qualified Database.V1.Bloodhound as V1
4138import qualified Database.V5.Bloodhound as V5
42−#endif
4339import Network.HTTP.Client
4440import Network.HTTP.Types.Status
4541import Test.QuickCheck.Instances ()
@@ -58,10 +54,8 @@ main :: IO ()
5854main = defaultMainWithIngredients ings $ askOption $ \vers -> testGroup "katip-elasticsearch"
5955 [
6056 case vers of
61−#if !MIN_VERSION_bloodhound(0,17,0)
62− TestV1 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV1)
63−#endif
64− TestV5 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV5)
57+ TestV1 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV1)
58+ TestV5 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV5)
6559 , typeAnnotatedTests
6660 , roundToSundayTests
6761 ]
@@ -70,26 +64,18 @@ main = defaultMainWithIngredients ings $ askOption $ \vers -> testGroup "katip-e
7064
7165
7266-------------------------------------------------------------------------------
73−data TestWithESVersion = TestV5
74−#if !MIN_VERSION_bloodhound(0,17,0)
75− | TestV1
76−#endif
77− deriving (Typeable)
67+data TestWithESVersion = TestV1
68+ | TestV5
69+ deriving (Typeable)
7870
7971
8072instance IsOption TestWithESVersion where
81− defaultValue = TestV5
82−#if !MIN_VERSION_bloodhound(0,17,0)
73+ defaultValue = TestV1
8374 parseValue "1" = Just TestV1
84−#endif
8575 parseValue "5" = Just TestV5
8676 parseValue _ = Nothing
8777 optionName = Tagged "es-version"
88−#if MIN_VERSION_bloodhound(0,17,0)
89− optionHelp = Tagged "Version of ES to test against, either 1 or 5, defaulting to 5."
90−#else
91− optionHelp = Tagged "Version of ES to test against, only v5 is supported, but should work for higher versions."
92−#endif
78+ optionHelp = Tagged "Version of ES to test against, either 1 or 5, defaulting to 1."
9379
9480
9581class ESVersion v => TestESVersion v where
@@ -115,7 +101,6 @@ class ESVersion v => TestESVersion v where
115101 searchByIndex :: proxy v -> IndexName v -> Search v -> BH v IO (Response ByteString)
116102
117103
118−#if !MIN_VERSION_bloodhound(0,17,0)
119104instance TestESVersion ESV1 where
120105 type Server ESV1 = V1.Server
121106 toServer _ = V1.Server
@@ -137,7 +122,6 @@ instance TestESVersion ESV1 where
137122 refreshIndex _ = V1.refreshIndex
138123 withBH _ = V1.withBH
139124 searchByIndex _ = V1.searchByIndex
140−#endif
141125
142126
143127instance TestESVersion ESV5 where
@@ -177,12 +161,14 @@ setupSearch
177161setupSearch prx modScribeCfg = do
178162 bh prx (dropESSchema prx)
179163 mgr <- newManager defaultManagerSettings
180− mkEsScribe cfg (mkBHEnv prx (svr prx) mgr) (ixn prx) (mn prx) (permitItem DebugS) V3
164+ mkEsScribe cfg (mkBHEnv prx (svr prx) mgr)
165+ (permitItem DebugS) V3
181166 where
167+ mname = mn prx
182168 cfg :: EsScribeCfg v
183169 cfg = modScribeCfg $
184− (defaultEsScribeCfg' prx)
185− { essAnnotateTypes = True
170+ (defaultEsScribeCfg' prx (ixn prx) mname)
171+ { essItemFormatter = annotatedItemFormatter
186172 , essIndexSettings = ixs prx
187173 }
188174
katip/README.md+1−1
@@ -16,7 +16,7 @@ Kâtip (pronounced kah-tip) is the Turkish word for scribe.
1616 add context that will be automatically merged in with existing log
1717 context.
1818
19−* *Easy Integration:* Katip was designed to be easily integrated
19+* *Easy to Integration:* Katip was designed to be easily integrated
2020 into existing monads. By using typeclasses for logging facilities,
2121 individual subsystems and even libraries can easily add their own
2222 namespacing and context without having any knowledge of their
katip/changelog.md−13
@@ -1,16 +1,3 @@
1−0.8.5.0
2−=======
3−* Add MonadResource instances [#121](https://github.com/Soostone/katip/pull/121)
4−
5−0.8.4.0
6−=======
7−* Add support for unliftio-core 0.2 while maintaining backwards-compatible support for 0.1
8−
9−0.8.3.0
10−=======
11−
12−* Add `logKatipItem` function and reimplement `logItem` to use it. [vlatkoB](https://github.com/vlatkoB)
13−
1410.8.2.0
152=======
163* Add `MonadFail` instances for `base` >= 4.9.0.0 [Koray Al](https://github.com/korayal)
katip/katip.cabal+31−28
@@ -1,5 +1,5 @@
11name: katip
2−version: 0.8.5.0
2+version: 0.8.2.0
33synopsis: A structured logging framework.
44description:
55 Katip is a structured logging framework. See README.md for more details.
@@ -36,22 +36,24 @@ flag lib-Werror
3636 manual: True
3737
3838library
39− exposed-modules:
40− Katip
41− Katip.Core
42− Katip.Format.Time
43− Katip.Monadic
44− Katip.Scribes.Handle
39+ exposed-modules: Katip
40+ , Katip.Core
41+ , Katip.Format.Time
42+ , Katip.Monadic
43+ , Katip.Scribes.Handle
44+ , Katip.Scribes.Handle.FileOwner
4545
46− default-extensions:
47− DeriveGeneric
48− FlexibleContexts
49− GeneralizedNewtypeDeriving
50− MultiParamTypeClasses
51− RankNTypes
52− RecordWildCards
53− TemplateHaskell
54− OverloadedStrings
46+ default-extensions: CPP
47+ , DeriveGeneric
48+ , FlexibleContexts
49+ , GeneralizedNewtypeDeriving
50+ , LambdaCase
51+ , MultiParamTypeClasses
52+ , OverloadedStrings
53+ , RankNTypes
54+ , RecordWildCards
55+ , ScopedTypeVariables
56+ , TemplateHaskell
5557
5658 build-depends: base >=4.9.0.0 && <5
5759 , aeson >=0.6
@@ -59,27 +61,28 @@ library
5961 , auto-update >= 0.1
6062 , bytestring >= 0.9
6163 , containers >=0.4
64+ , deepseq
6265 , either >= 4
63− , safe-exceptions >= 0.1.0.0
6466 , hostname >=1.0
67+ , microlens >= 0.2.0.0
68+ , microlens-th >= 0.1.0.0
69+ , monad-control >= 1.0
70+ , mtl >= 2.0
6571 , old-locale >= 1.0
72+ , resourcet >= 1.1
73+ , safe-exceptions >= 0.1.0.0
74+ , scientific >= 0.3.3.0
75+ , semigroups
76+ , stm >= 2.4
6677 , string-conv >= 0.1
6778 , template-haskell >= 2.8
6879 , text >= 0.11
6980 , time >= 1
7081 , transformers >= 0.3
71− , transformers-compat
72− , unordered-containers >= 0.2
73− , monad-control >= 1.0
74− , mtl >= 2.0
7582 , transformers-base >= 0.3
76− , resourcet >= 1.1
77− , scientific >= 0.3.3.0
78− , microlens >= 0.2.0.0
79− , microlens-th >= 0.1.0.0
80− , semigroups
83+ , transformers-compat
8184 , unliftio-core >= 0.1
82− , stm >= 2.4
85+ , unordered-containers >= 0.2
8386
8487 hs-source-dirs: src
8588 default-language: Haskell2010
@@ -87,7 +90,7 @@ library
8790 if flag(lib-Werror)
8891 ghc-options: -Werror
8992 if os(windows)
90− build-depends: Win32 >=2.3 && <2.9
93+ build-depends: Win32 >=2.3 && <2.6
9194 exposed-modules: Katip.Compat
9295 else
9396 build-depends: unix >= 2.5 && <2.8
katip/src/Katip.hs+2−1
@@ -161,7 +161,6 @@ module Katip
161161 , logT
162162 , logLoc
163163 , logItem
164− , logKatipItem
165164 , logException
166165 -- ** 'KatipContext': Logging With Context
167166 -- $katipcontextlogging
@@ -182,6 +181,8 @@ module Katip
182181 , mkHandleScribe
183182 , mkHandleScribeWithFormatter
184183 , mkFileScribe
184+ , mkFileScribeWithFormatter
185+ , mkFileOwnerScribeWithFormatter
185186 , ColorStrategy (..)
186187 , ItemFormatter
187188 , bracketFormat
katip/src/Katip/Core.hs+23−36
@@ -44,8 +44,7 @@ import Control.Monad.Trans.Either
4444import Control.Monad.Trans.Except
4545import Control.Monad.Trans.Maybe
4646import Control.Monad.Trans.Reader
47−import Control.Monad.Trans.Resource (MonadResource, ResourceT,
48− transResourceT)
47+import Control.Monad.Trans.Resource (ResourceT, transResourceT)
4948import Control.Monad.Trans.RWS.Lazy (RWST, mapRWST)
5049import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST, mapRWST)
5150import Control.Monad.Trans.State.Lazy (StateT, mapStateT)
@@ -575,6 +574,10 @@ itemJson verb a = toJSON $ a & itemPayload %~ payloadObject verb
575574-- combined with other, custom logic with 'permitAND' and 'permitOR'
576575type PermitFunc = forall a. Item a -> IO Bool
577576
577+-- | Generalized version of some function which takes any item and
578+-- produces something. Must replace the 'PermitFunc' and maybe
579+-- 'ItemFormatter'
580+newtype ItemFunc r = ItemFunc (forall a. LogItem a => Item a -> r)
578581
579582-- | AND together 2 permit functions
580583permitAND :: PermitFunc -> PermitFunc -> PermitFunc
@@ -665,7 +668,7 @@ makeLenses ''LogEnv
665668
666669
667670-------------------------------------------------------------------------------
668−-- | Create a reasonable default InitLogEnv. Uses an 'AutoUpdate' which
671+-- | Create a reasonable default InitLogEnv. Uses an 'AutoUdate' which
669672-- updates the timer every 1ms. If you need even more timestamp
670673-- precision at the cost of performance, consider setting
671674-- '_logEnvTimer' with 'getCurrentTime'.
@@ -880,8 +883,7 @@ instance (Katip m) => Katip (ResourceT m) where
880883-- opposed to implementing 'Katip' for your monad.
881884newtype KatipT m a = KatipT { unKatipT :: ReaderT LogEnv m a }
882885 deriving ( Functor, Applicative, Monad, MonadIO
883− , MonadMask, MonadCatch, MonadThrow, MonadResource, MonadTrans
884− , MonadBase b)
886+ , MonadMask, MonadCatch, MonadThrow, MonadTrans, MonadBase b)
885887
886888
887889instance MonadIO m => Katip (KatipT m) where
@@ -903,14 +905,9 @@ instance (MonadBaseControl b m) => MonadBaseControl b (KatipT m) where
903905 restoreM = defaultRestoreM
904906
905907instance MonadUnliftIO m => MonadUnliftIO (KatipT m) where
906−#if MIN_VERSION_unliftio_core(0, 2, 0)
907− withRunInIO inner = KatipT $ ReaderT $ \le -> withRunInIO $ \run ->
908− inner (run . runKatipT le)
909−#else
910908 askUnliftIO = KatipT $
911− withUnliftIO $ \u ->
912− pure (UnliftIO (unliftIO u . unKatipT))
913−#endif
909+ withUnliftIO $ \u ->
910+ pure (UnliftIO (unliftIO u . unKatipT))
914911
915912#if MIN_VERSION_base(4, 9, 0)
916913instance MF.MonadFail m => MF.MonadFail (KatipT m) where
@@ -948,29 +945,19 @@ logItem
948945 -> m ()
949946logItem a ns loc sev msg = do
950947 LogEnv{..} <- getLogEnv
951− logKatipItem =<< liftIO
952− (Item <$> pure _logEnvApp
953− <*> pure _logEnvEnv
954− <*> pure sev
955− <*> (mkThreadIdText <$> myThreadId)
956− <*> pure _logEnvHost
957− <*> pure _logEnvPid
958− <*> pure a
959− <*> pure msg
960− <*> _logEnvTimer
961− <*> pure (_logEnvApp <> ns)
962− <*> pure loc)
963−
964−-- | Log already constructed 'Item'. This is the lowest level function that other log*
965−-- functions use.
966−-- It can be useful when implementing centralised logging services.
967−logKatipItem
968− :: (A.Applicative m, LogItem a, Katip m)
969− => Item a
970− -> m ()
971−logKatipItem item = do
972− LogEnv{..} <- getLogEnv
973− liftIO $
948+ liftIO $ do
949+ item <- Item
950+ <$> pure _logEnvApp
951+ <*> pure _logEnvEnv
952+ <*> pure sev
953+ <*> (mkThreadIdText <$> myThreadId)
954+ <*> pure _logEnvHost
955+ <*> pure _logEnvPid
956+ <*> pure a
957+ <*> pure msg
958+ <*> _logEnvTimer
959+ <*> pure (_logEnvApp <> ns)
960+ <*> pure loc
974961 FT.forM_ (M.elems _logEnvScribes) $ \ ScribeHandle {..} -> do
975962 whenM (scribePermitItem shScribe item) $
976963 void $ atomically (tryWriteTBQueue shChan (NewItem item))
@@ -1019,7 +1006,7 @@ logException
10191006logException a ns sev action = action `catchAny` \e -> f e >> throwM e
10201007 where
10211008 f e = logF a ns sev (msg e)
1022− msg e = ls (T.pack "An exception has occurred: ") <> showLS e
1009+ msg e = ls (T.pack "An exception has occured: ") <> showLS e
10231010
10241011
10251012-------------------------------------------------------------------------------
katip/src/Katip/Monadic.hs+6−20
@@ -58,8 +58,7 @@ import Control.Monad.Trans.Either (EitherT, mapEitherT)
5858import Control.Monad.Trans.Except (ExceptT, mapExceptT)
5959import Control.Monad.Trans.Identity (IdentityT, mapIdentityT)
6060import Control.Monad.Trans.Maybe (MaybeT, mapMaybeT)
61−import Control.Monad.Trans.Resource (MonadResource,
62− ResourceT, transResourceT)
61+import Control.Monad.Trans.Resource (ResourceT, transResourceT)
6362import Control.Monad.Trans.RWS (RWST, mapRWST)
6463import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST, mapRWST)
6564import qualified Control.Monad.Trans.State.Strict as Strict (StateT, mapStateT)
@@ -316,7 +315,7 @@ logExceptionM
316315logExceptionM action sev = action `catchAny` \e -> f e >> throwM e
317316 where
318317 f e = logFM sev (msg e)
319− msg e = ls ("An exception has occurred: " :: Text) Semi.<> showLS e
318+ msg e = ls ("An exception has occured: " :: Text) Semi.<> showLS e
320319
321320
322321-------------------------------------------------------------------------------
@@ -350,7 +349,6 @@ newtype KatipContextT m a = KatipContextT {
350349 , MonadWriter w
351350 , MonadError e
352351 , MonadPlus
353− , MonadResource
354352 , Alternative
355353 , MonadFix
356354 , MonadTrans
@@ -397,15 +395,9 @@ instance (MonadIO m) => KatipContext (KatipContextT m) where
397395 localKatipNamespace f (KatipContextT m) = KatipContextT $ local (\s -> s { ltsNamespace = f (ltsNamespace s)}) m
398396
399397instance MonadUnliftIO m => MonadUnliftIO (KatipContextT m) where
400−#if MIN_VERSION_unliftio_core(0, 2, 0)
401− withRunInIO inner = KatipContextT $ ReaderT $ \lts -> withRunInIO $ \run ->
402− inner (run . runKatipContextT (ltsLogEnv lts) (ltsContext lts) (ltsNamespace lts))
403−#else
404398 askUnliftIO = KatipContextT $
405− withUnliftIO $ \u ->
406− pure (UnliftIO (unliftIO u . unKatipContextT))
407−#endif
408−
399+ withUnliftIO $ \u ->
400+ pure (UnliftIO (unliftIO u . unKatipContextT))
409401
410402#if MIN_VERSION_base(4, 9, 0)
411403instance MF.MonadFail m => MF.MonadFail (KatipContextT m) where
@@ -490,15 +482,9 @@ instance MonadBaseControl b m => MonadBaseControl b (NoLoggingT m) where
490482 restoreM = NoLoggingT . restoreM
491483
492484instance MonadUnliftIO m => MonadUnliftIO (NoLoggingT m) where
493−#if MIN_VERSION_unliftio_core(0, 2, 0)
494− withRunInIO inner = NoLoggingT $ withRunInIO $ \run ->
495− inner (run . runNoLoggingT)
496−#else
497485 askUnliftIO = NoLoggingT $
498− withUnliftIO $ \u ->
499− pure (UnliftIO (unliftIO u . runNoLoggingT))
500−#endif
501−
486+ withUnliftIO $ \u ->
487+ pure (UnliftIO (unliftIO u . runNoLoggingT))
502488
503489instance MonadIO m => Katip (NoLoggingT m) where
504490 getLogEnv = liftIO (initLogEnv "NoLoggingT" "no-logging")
katip/src/Katip/Scribes/Handle.hs+64−26
@@ -3,33 +3,42 @@ module Katip.Scribes.Handle where
33-------------------------------------------------------------------------------
44import Control.Applicative as A
55import Control.Concurrent
6−import Control.Exception (bracket_, finally)
6+import Control.Exception (finally)
77import Data.Aeson
8−import Data.Text.Lazy (toStrict)
9−import Data.Text.Lazy.Encoding (decodeUtf8)
10−import qualified Data.HashMap.Strict as HM
8+import Data.Aeson.Text
9+import Data.ByteString.Builder as B
10+import Data.ByteString.Lazy as BL (hPutStr)
11+import Data.ByteString.Lazy.Char8 as BL (singleton)
1112import Data.Monoid as M
1213import Data.Scientific as S
1314import Data.Text (Text)
14−import Data.Text.Lazy.Builder
15−import Data.Text.Lazy.IO as T
15+import Data.Text.Lazy.Builder as TL
16+import Data.Text.Lazy.Encoding (encodeUtf8)
1617import System.IO
18+import qualified Data.HashMap.Strict as HM
19+
1720-------------------------------------------------------------------------------
1821import Katip.Core
1922import Katip.Format.Time (formatAsLogTime)
23+import Katip.Scribes.Handle.FileOwner
2024-------------------------------------------------------------------------------
2125
2226
2327-------------------------------------------------------------------------------
24−brackets :: Builder -> Builder
28+brackets :: TL.Builder -> TL.Builder
2529brackets m = fromText "[" M.<> m <> fromText "]"
2630
31+textBuilderToBS :: TL.Builder -> B.Builder
32+textBuilderToBS = B.lazyByteString . encodeUtf8 . toLazyText
33+
34+appendNL :: B.Builder -> B.Builder
35+appendNL b = b <> B.lazyByteString (BL.singleton '\n')
2736
2837-------------------------------------------------------------------------------
29−getKeys :: LogItem s => Verbosity -> s -> [Builder]
38+getKeys :: LogItem s => Verbosity -> s -> [TL.Builder]
3039getKeys verb a = concat (renderPair A.<$> HM.toList (payloadObject verb a))
3140 where
32− renderPair :: (Text, Value) -> [Builder]
41+ renderPair :: (Text, Value) -> [TL.Builder]
3342 renderPair (k,v) =
3443 case v of
3544 Object o -> concat [renderPair (k <> "." <> k', v') | (k', v') <- HM.toList o]
@@ -65,6 +74,19 @@ data ColorStrategy
6574mkHandleScribe :: ColorStrategy -> Handle -> PermitFunc -> Verbosity -> IO Scribe
6675mkHandleScribe = mkHandleScribeWithFormatter bracketFormat
6776
77+
78+mkFileScribeWithFormatter :: (forall a . LogItem a => ItemFormatter a)
79+ -> ColorStrategy
80+ -> FilePath
81+ -> PermitFunc
82+ -> Verbosity
83+ -> IO Scribe
84+mkFileScribeWithFormatter formatter color f permitF verb = do
85+ h <- openBinaryFile f AppendMode
86+ Scribe logger finalizer permit <- mkHandleScribeWithFormatter formatter color h permitF verb
87+ return (Scribe logger (finalizer `finally` hClose h) permit)
88+
89+
6890-- | Logs to a file handle such as stdout, stderr, or a file. Takes a custom
6991-- `ItemFormatter` that can be used to format `Item` as needed.
7092--
@@ -77,16 +99,34 @@ mkHandleScribeWithFormatter :: (forall a . LogItem a => ItemFormatter a)
7799 -> Verbosity
78100 -> IO Scribe
79101mkHandleScribeWithFormatter itemFormatter cs h permitF verb = do
80− hSetBuffering h LineBuffering
81102 colorize <- case cs of
82103 ColorIfTerminal -> hIsTerminalDevice h
83104 ColorLog b -> return b
84105 lock <- newMVar ()
85− let logger i@Item{..} = do
86− bracket_ (takeMVar lock) (putMVar lock ()) $
87− T.hPutStrLn h $ toLazyText $ itemFormatter colorize verb i
106+ let logger i@Item{..} = withMVar lock $ \() -> do
107+ BL.hPutStr h $ toLazyByteString $ itemFormatter colorize verb i
88108 return $ Scribe logger (hFlush h) permitF
89109
110+-- | Just like mkFileScribeWithFormatter but does it with FileOwner. You can
111+-- rotate your logs with commands to the FileOwner directly. This scribe will
112+-- send messages to the given FileOwner
113+mkFileOwnerScribeWithFormatter :: (forall a . LogItem a => ItemFormatter a)
114+ -> ColorStrategy
115+ -> FileOwner
116+ -> PermitFunc
117+ -> Verbosity
118+ -> IO Scribe
119+mkFileOwnerScribeWithFormatter itemFormatter cs owner permitF verb = do
120+ let
121+ colorize = case cs of
122+ ColorIfTerminal -> False
123+ -- File is never a terminal
124+ ColorLog b -> b
125+ logger item = do
126+ writeFileOwner owner $ toLazyByteString
127+ $ itemFormatter colorize verb item
128+ scribe = Scribe logger (fileOwnerControl owner CloseMsg) permitF
129+ return scribe
90130
91131-------------------------------------------------------------------------------
92132-- | A specialization of 'mkHandleScribe' that takes a 'FilePath'
@@ -95,19 +135,17 @@ mkHandleScribeWithFormatter itemFormatter cs h permitF verb = do
95135-- 'closeScribe'/'closeScribes'. Does not do log coloring. Sets handle
96136-- to 'LineBuffering' mode.
97137mkFileScribe :: FilePath -> PermitFunc -> Verbosity -> IO Scribe
98−mkFileScribe f permitF verb = do
99− h <- openFile f AppendMode
100− Scribe logger finalizer permit <- mkHandleScribe (ColorLog False) h permitF verb
101− return (Scribe logger (finalizer `finally` hClose h) permit)
138+mkFileScribe = mkFileScribeWithFormatter bracketFormat (ColorLog False)
102139
103140
104141-------------------------------------------------------------------------------
105142-- | A custom ItemFormatter for logging `Item`s. Takes a `Bool` indicating
106143-- whether to colorize the output, `Verbosity` of output, and an `Item` to
107−-- format.
144+-- format. Note that formatter is responsible for appending the newline at the
145+-- end of the string if required
108146--
109147-- See `bracketFormat` and `jsonFormat` for examples.
110−type ItemFormatter a = Bool -> Verbosity -> Item a -> Builder
148+type ItemFormatter a = Bool -> Verbosity -> Item a -> B.Builder
111149
112150
113151formatItem :: LogItem a => ItemFormatter a
@@ -121,10 +159,10 @@ formatItem = bracketFormat
121159-- > [2016-05-11 21:01:15][MyApp.confrabulation][Debug][myhost.example.com][PID 1724][ThreadId 1154][confrab_factor:42.0][main:Helpers.Logging Helpers/Logging.hs:41:9] Confrabulating widgets, with extra namespace and context
122160-- > [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][PID 1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:43:7] Namespace and context are back to normal
123161bracketFormat :: LogItem a => ItemFormatter a
124−bracketFormat withColor verb Item{..} =
162+bracketFormat withColor verb Item{..} = appendNL $ textBuilderToBS $
125163 brackets nowStr <>
126164 brackets (mconcat $ map fromText $ intercalateNs _itemNamespace) <>
127− brackets (fromText (renderSeverity' _itemSeverity)) <>
165+ brackets (renderSeverity' _itemSeverity) <>
128166 brackets (fromString _itemHost) <>
129167 brackets ("PID " <> fromString (show _itemProcess)) <>
130168 brackets ("ThreadId " <> fromText (getThreadIdText _itemThread)) <>
@@ -135,7 +173,7 @@ bracketFormat withColor verb Item{..} =
135173 nowStr = fromText (formatAsLogTime _itemTime)
136174 ks = map brackets $ getKeys verb _itemPayload
137175 renderSeverity' severity =
138− colorBySeverity withColor severity (renderSeverity severity)
176+ colorBySeverity withColor severity (fromText $ renderSeverity severity)
139177
140178-- | Logs items as JSON. This can be useful in circumstances where you already
141179-- have infrastructure that is expecting JSON to be logged to a standard stream
@@ -145,15 +183,15 @@ bracketFormat withColor verb Item{..} =
145183-- > {"at":"2018-10-02T21:50:30.4523848Z","env":"production","ns":["MyApp","confrabulation"],"data":{"confrab_factor":42},"app":["MyApp"],"msg":"Confrabulating widgets, with extra namespace and context","pid":"10456","loc":{"loc_col":11,"loc_pkg":"main","loc_mod":"Helpers.Logging","loc_fn":"Helpers\\Logging.hs","loc_ln":53},"host":"myhost.example.com","sev":"Debug","thread":"ThreadId 139"}
146184-- > {"at":"2018-10-02T21:50:30.4523848Z","env":"production","ns":["MyApp"],"data":{},"app":["MyApp"],"msg":"Namespace and context are back to normal","pid":"10456","loc":{"loc_col":9,"loc_pkg":"main","loc_mod":"Helpers.Logging","loc_fn":"Helpers\\Logging.hs","loc_ln":55},"host":"myhost.example.com","sev":"Info","thread":"ThreadId 139"}
147185jsonFormat :: LogItem a => ItemFormatter a
148−jsonFormat withColor verb i =
149− fromText $
186+jsonFormat withColor verb i = appendNL $
187+ textBuilderToBS $
150188 colorBySeverity withColor (_itemSeverity i) $
151− toStrict $ decodeUtf8 $ encode $ itemJson verb i
189+ encodeToTextBuilder $ itemJson verb i
152190
153191-- | Color a text message based on `Severity`. `ErrorS` and more severe errors
154192-- are colored red, `WarningS` is colored yellow, and all other messages are
155193-- rendered in the default color.
156−colorBySeverity :: Bool -> Severity -> Text -> Text
194+colorBySeverity :: Bool -> Severity -> TL.Builder -> TL.Builder
157195colorBySeverity withColor severity msg = case severity of
158196 EmergencyS -> red msg
159197 AlertS -> red msg
katip/src/Katip/Scribes/Handle/FileOwner.hsnew file+143
@@ -0,0 +1,143 @@
1+module Katip.Scribes.Handle.FileOwner where
2+
3+import Control.Applicative
4+import Control.Concurrent
5+import Control.Concurrent.Async
6+import Control.Concurrent.STM
7+import Control.Debounce
8+import Control.DeepSeq
9+import Control.Exception
10+import Control.Monad
11+import Data.ByteString.Lazy as BL
12+import Data.IORef
13+import GHC.IO.Handle
14+import GHC.IO.Handle.FD
15+import GHC.IO.IOMode
16+
17+#if MIN_VERSION_stm(2,5,0)
18+import Numeric.Natural
19+#endif
20+
21+
22+-- | File owner struct writing data to the file
23+-- sequentially. Automatic buffer flushing and log rotating.
24+data FileOwner = FileOwner
25+ { foDataQueune :: TBQueue BL.ByteString
26+ , foControlQueue :: TBQueue ControlMsg
27+ , foAsync :: Async ()
28+ -- ^ Wait for to be sure that worker thread is closed.
29+ }
30+
31+#if MIN_VERSION_stm(2,5,0)
32+type QueueLen = Natural
33+#else
34+type QueueLen = Int
35+#endif
36+
37+data FileOwnerSettings = FileOwnerSettings
38+ { fosDebounceFreq :: Maybe Int
39+ , fosDataQueueLen :: QueueLen
40+ , fosControlQueueLen :: QueueLen
41+ }
42+
43+defaultFileOwnerSettings :: FileOwnerSettings
44+defaultFileOwnerSettings = FileOwnerSettings
45+ { fosDebounceFreq = Just 200000 -- every 200ms
46+ , fosDataQueueLen = 1000
47+ , fosControlQueueLen = 100
48+ }
49+
50+data ControlMsg
51+ = CloseMsg
52+ | FlushMsg
53+ | ReopenMsg
54+ deriving (Eq, Ord, Show)
55+
56+newFileOwner :: FilePath -> FileOwnerSettings -> IO FileOwner
57+newFileOwner fp s = do
58+ dqueue <- newTBQueueIO $ fosDataQueueLen s
59+ cqueue <- newTBQueueIO $ fosControlQueueLen s
60+ let
61+ newResource = do
62+ openBinaryFile fp AppendMode
63+ ack = newResource >>= newIORef
64+ release ref = do
65+ h <- readIORef ref
66+ hFlush h
67+ hClose h
68+ go ref = do
69+ let
70+ readAllData = do
71+ a <- readTBQueue dqueue
72+ -- flushTBQueue never retries
73+ as <- flushTBQueue dqueue
74+ return $ a:as
75+ readMsg
76+ = (Left <$> readTBQueue cqueue)
77+ <|> (Right <$> readAllData)
78+ debounce action = case fosDebounceFreq s of
79+ Nothing -> do
80+ return $ return ()
81+ Just freq -> mkDebounce $ defaultDebounceSettings
82+ { debounceAction = action
83+ , debounceFreq = freq }
84+ flush <- debounce $ readIORef ref >>= hFlush
85+ let
86+ recur = atomically readMsg >>= \case
87+ Right bs -> do
88+ h <- readIORef ref
89+ BL.hPutStr h $ mconcat bs
90+ flush -- auto debounced flush
91+ recur
92+ Left c -> case c of
93+ CloseMsg -> do
94+ lastMsgs <- atomically $ flushTBQueue dqueue
95+ -- flush never blocks
96+ case lastMsgs of
97+ [] -> return ()
98+ _ -> do
99+ h <- readIORef ref
100+ BL.hPutStr h $ mconcat lastMsgs
101+ return () -- release will flush and close the handler
102+ FlushMsg -> do
103+ readIORef ref >>= hFlush
104+ recur
105+ ReopenMsg -> do
106+ newH <- newResource
107+ oldH <- atomicModifyIORef' ref (\oldH -> (newH, oldH))
108+ hFlush oldH
109+ hClose oldH
110+ recur
111+ recur
112+ worker :: IO ()
113+ worker = try (bracket ack release go) >>= \case
114+ Left (_ :: SomeException) -> do
115+ threadDelay 100000
116+ -- to not restart the worker function too often
117+ worker
118+ -- We dont want the worker thread become unavailable because
119+ -- of exceptions. E.g. could not create or write the file.
120+ Right () -> return ()
121+ asyncRet <- async worker
122+ return $ FileOwner
123+ { foDataQueune = dqueue
124+ , foControlQueue = cqueue
125+ , foAsync = asyncRet
126+ }
127+
128+fileOwnerControl :: FileOwner -> ControlMsg -> IO ()
129+fileOwnerControl fo msg = do
130+ atomically $ writeTBQueue (foControlQueue fo) msg
131+ when (msg == CloseMsg) $ do
132+ -- Wait for worker thread to finish
133+ void $ waitCatch $ foAsync fo
134+
135+writeFileOwner :: FileOwner -> BL.ByteString -> IO ()
136+writeFileOwner fo bs = do
137+ a <- return $!! bs
138+ -- The deepseq here is to be sure that writing thread will not
139+ -- calculate thunks and will not get into the blackhole or
140+ -- something. Precalculating is the responsibility of the sending
141+ -- thread, because there may be several sending threads and only one
142+ -- writing.
143+ atomically $ writeTBQueue (foDataQueune fo) $!! a
katip/test/Katip/Tests/Scribes/Handle.hs+6−8
@@ -1,5 +1,5 @@
11{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE RankNTypes #-}
2+{-# LANGUAGE RankNTypes #-}
33module Katip.Tests.Scribes.Handle
44 ( tests
55 ) where
@@ -7,12 +7,11 @@ module Katip.Tests.Scribes.Handle
77-------------------------------------------------------------------------------
88import Control.Monad
99import Data.Aeson
10−import qualified Data.ByteString.Char8 as B
10+import Data.ByteString.Builder as B
1111import qualified Data.ByteString.Lazy as BL
1212import Data.Monoid as M
1313import Data.Text (Text)
1414import qualified Data.Text as T
15−import qualified Data.Text.Encoding as T
1615import qualified Data.Text.Lazy as LT
1716import qualified Data.Text.Lazy.Builder as LT
1817import Data.Time
@@ -245,14 +244,13 @@ writeJsonLog = writeFormattedLog jsonFormat
245244
246245writeFormattedLog :: (forall a . LogItem a => ItemFormatter a) -> (FilePath, Handle) -> IO (BL.ByteString)
247246writeFormattedLog format (path, h) = do
248− mapM_ (put . formatOne) genItems
249− mapM_ (put . formatOne) genTypedItems
247+ mapM_ put genItems
248+ mapM_ put genTypedItems
250249 hClose h
251250 BL.readFile path
252251 where
253− formatOne :: LogItem a => Item a -> Text
254− formatOne = LT.toStrict . LT.toLazyText . format False V3
255− put = B.hPutStrLn h . T.encodeUtf8
252+ put :: (LogItem a) => Item a -> IO ()
253+ put = BL.hPutStr h . toLazyByteString . format False V3
256254
257255setupTempFile :: IO (FilePath, Handle)
258256setupTempFile = do
stack-old.yamlnew file+21
@@ -0,0 +1,21 @@
1+nix:
2+ packages:
3+ - zlib
4+resolver: lts-12.26
5+packages:
6+ - katip
7+ - katip-elasticsearch
8+ - katip-datadog
9+ - katip-logzio
10+extra-deps:
11+ - bloodhound-0.16.0.0
12+ - unliftio-core-0.1.2.0
13+flags:
14+ katip:
15+ lib-Werror: true
16+ katip-elasticsearch:
17+ lib-Werror: true
18+ katip-datadog:
19+ lib-Werror: true
20+ katip-logzio:
21+ lib-Werror: true
stack.yaml+10−5
@@ -1,14 +1,19 @@
1−resolver: lts-16.18
1+nix:
2+ packages:
3+ - zlib
4+resolver: lts-13.21
25packages:
36 - katip
47 - katip-elasticsearch
58 - katip-datadog
69 - katip-logzio
710extra-deps:
8− # ghc 8.8 support
9− - github: wireapp/bloodhound
10− commit: b409f52b518f20a4c20651b5893c31b7b85cb9b1
11−allow-newer: true
11+ - binary-0.8.7.0
12+ - bloodhound-0.16.0.0
13+ - containers-0.5.11.0
14+ - hedgehog-1.0
15+ - tasty-hedgehog-1.0.0.1
16+ - unliftio-core-0.1.2.0
1217flags:
1318 katip:
1419 lib-Werror: true