on 336da78 help sign in
.gitignore−1
since splitsnapshots
unifiedsplit
@@ -7,4 +7,3 @@ TAGS
7*.hi
7*.hi
8*.dyn_o
8*.dyn_o
9*.dyn_hi
9*.dyn_hi
10−stack.yaml.lock
.travis.yml+12−8
@@ -9,19 +9,24 @@ addons:
9 apt:
9 apt:
10 packages:
10 packages:
11 - libgmp-dev
11 - libgmp-dev
12+ - oracle-java8-installer
12
13
13# The different configurations we want to test. You could also do things like
14# The different configurations we want to test. You could also do things like
14# change flags or use --stack-yaml to point to a different file.
15# change flags or use --stack-yaml to point to a different file.
15matrix:
16matrix:
16 include:
17 include:
18+ - env: ARGS="--resolver=lts-7" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
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"
19+ - env: ARGS="--resolver=lts-8" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
20+ - env: ARGS="--resolver=lts-9" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
21+ - env: ARGS="--resolver=lts-10" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
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"
22+ - env: ARGS="--resolver=lts-11" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
23+ - env: ARGS="--resolver=lts-12" ESVERSION=1.5.2 ESURL=https://download.elastic.co/elasticsearch/elasticsearch ESTESTARGS="--es-version 1"
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"
24+ - 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
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"
25+ - 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
21− # nightlies may fail usually due to lagging dependencies
26+ # nightlies may fail from time to time
22 allow_failures:
27 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"
28+ - 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
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"
29+ - 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
25
30
26before_install:
31before_install:
27# Download and unpack the stack executable
32# Download and unpack the stack executable
@@ -34,7 +39,6 @@ install:
34 - tar xzf elasticsearch-$ESVERSION.tar.gz
39 - tar xzf elasticsearch-$ESVERSION.tar.gz
35 # set up a repo for snapshot testing. Required in ES >= 1.6
40 # set up a repo for snapshot testing. Required in ES >= 1.6
36 - echo "path.repo = [\"/tmp\"]" >> ./elasticsearch-$ESVERSION/elasticsearch.yml
41 - echo "path.repo = [\"/tmp\"]" >> ./elasticsearch-$ESVERSION/elasticsearch.yml
37− - java -version
38 - ./elasticsearch-$ESVERSION/bin/elasticsearch &
42 - ./elasticsearch-$ESVERSION/bin/elasticsearch &
39 # Bloodhound seems to take a while to build and can sometimes stall out the build when travis detects no output.
43 # Bloodhound seems to take a while to build and can sometimes stall out the build when travis detects no output.
40
44
katip-datadog/README.md+5−3
@@ -1,6 +1,8 @@
1# Katip Datadog [![Build Status](https://travis-ci.org/Soostone/katip.svg?branch=master)](https://travis-ci.org/Soostone/katip)
1# Katip Datadog [![Build Status](https://travis-ci.org/Soostone/katip.svg?branch=master)](https://travis-ci.org/Soostone/katip)
2
2
3katip-datadog is a scribe for the Katip logging framework that sends
3katip-datadog is a scribe for the Katip logging framework that sends
4−structured logs to the Datadog logging service. It supports both
4+structured logs to the Datadog logging service.
5−directly sending to the ingestion API and through a locally installed
5+
6−datadog agent.
6+## Warning
7+
8+This is an unreleased library and is not yet production-ready.
katip-datadog/changelog.md+1−6
@@ -1,7 +1,2 @@
1−0.2.0.0
1+Unreleased
2==========
2==========
3−* Remove import of deprecated Network module
4−
5−0.1.0.0
6−==========
7−* Initial release
katip-datadog/katip-datadog.cabal+1−2
@@ -1,6 +1,5 @@
1name: katip-datadog
1name: katip-datadog
2−version: 0.2.0.0
2+version: 0.0.1.0
3−synopsis: Datadog scribe for the Katip logging framework
4description: See README.md for more details.
3description: See README.md for more details.
5license: BSD3
4license: BSD3
6license-file: LICENSE
5license-file: LICENSE
katip-datadog/src/Katip/Scribes/Datadog/TCP.hs+33−35
@@ -1,4 +1,3 @@
1−{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RankNTypes #-}
2{-# LANGUAGE RankNTypes #-}
4-- | Creates a scribe as a Custom Forwarder for Datadog that sends log
3-- | Creates a scribe as a Custom Forwarder for Datadog that sends log
@@ -6,13 +5,13 @@
6module Katip.Scribes.Datadog.TCP
5module Katip.Scribes.Datadog.TCP
7 ( -- * Types
6 ( -- * Types
8 APIKey(..)
7 APIKey(..)
9− , DatadogScribeSettings(..)
8+ , DataDogScribeSettings(..)
10− , DatadogAuth(..)
9+ , DataDogAuth(..)
11 , directAPIConnectionParams
10 , directAPIConnectionParams
12 , localAgentConnectionParams
11 , localAgentConnectionParams
13 -- * Scribe construction
12 -- * Scribe construction
14− , mkDatadogScribeSettings
13+ , mkDataDogScribeSettings
15− , mkDatadogScribe
14+ , mkDataDogScribe
16 ) where
15 ) where
17
16
18
17
@@ -25,7 +24,6 @@ import qualified Control.Retry as Retry
25import qualified Data.Aeson as A
24import qualified Data.Aeson as A
26import qualified Data.Binary.Builder as BB
25import qualified Data.Binary.Builder as BB
27import qualified Data.ByteString.Lazy as BSL
26import qualified Data.ByteString.Lazy as BSL
28−import Data.Monoid as Monoid
29import qualified Data.Pool as Pool
27import qualified Data.Pool as Pool
30import Data.Text (Text)
28import Data.Text (Text)
31import qualified Data.Text.Encoding as T
29import qualified Data.Text.Encoding as T
@@ -33,8 +31,8 @@ import qualified Data.Text.Lazy.Builder as B
33import Data.Time (NominalDiffTime)
31import Data.Time (NominalDiffTime)
34import Katip
32import Katip
35import Katip.Core (LocJs (..))
33import Katip.Core (LocJs (..))
34+import qualified Network as Net
36import qualified Network.Connection as C
35import qualified Network.Connection as C
37−import qualified Network.Socket as Net
38import qualified System.Posix.Types as POSIX
36import qualified System.Posix.Types as POSIX
39-------------------------------------------------------------------------------
37-------------------------------------------------------------------------------
40
38
@@ -46,7 +44,7 @@ newtype APIKey = APIKey
46 } deriving (Show, Eq)
44 } deriving (Show, Eq)
47
45
48
46
49−data DatadogAuth =
47+data DataDogAuth =
50 NoAuthLocal
48 NoAuthLocal
51 -- ^ Writing to the local agent, no auth required
49 -- ^ Writing to the local agent, no auth required
52 | DirectAuth APIKey
50 | DirectAuth APIKey
@@ -54,20 +52,20 @@ data DatadogAuth =
54 -- required. Using a local agent is recommended.
52 -- required. Using a local agent is recommended.
55
53
56
54
57−data DatadogScribeSettings = DatadogScribeSettings
55+data DataDogScribeSettings = DataDogScribeSettings
58− { datadogScribeSettings_connectionParams :: C.ConnectionParams
56+ { dataDogScribeSettings_connectionParams :: C.ConnectionParams
59 -- ^ Specify where the logs should go. If writing directly to the
57 -- ^ Specify where the logs should go. If writing directly to the
60 -- main intake API, you can use 'directAPIConnectionParams'. If
58 -- main intake API, you can use 'directAPIConnectionParams'. If
61 -- writing to a local agent, we recommend
59 -- writing to a local agent, we recommend
62 -- 'localAgentConnectionParams'
60 -- 'localAgentConnectionParams'
63− , datadogScribeSettings_auth :: DatadogAuth
61+ , dataDogScribeSettings_auth :: DataDogAuth
64− , datadogScribeSettings_poolStripes :: Int
62+ , dataDogScribeSettings_poolStripes :: Int
65 -- ^ How many stripes should be used in the connection pool. 1 is a reasonable default
63 -- ^ How many stripes should be used in the connection pool. 1 is a reasonable default
66− , datadogScribeSettings_connsPerStripe :: Int
64+ , dataDogScribeSettings_connsPerStripe :: Int
67 -- ^ How many TCP connections per stripe should be maintained in the pool.
65 -- ^ How many TCP connections per stripe should be maintained in the pool.
68− , datadogScribeSettings_connectionKeepalive :: NominalDiffTime
66+ , dataDogScribeSettings_connectionKeepalive :: NominalDiffTime
69 -- ^ How long should a TCP connection be kept open. 30 (seconds) is a reasonable default. Anything around 60 will tend to time out a lot.
67 -- ^ How long should a TCP connection be kept open. 30 (seconds) is a reasonable default. Anything around 60 will tend to time out a lot.
70− , datadogScribeSettings_retry :: Retry.RetryPolicyM IO
68+ , dataDogScribeSettings_retry :: Retry.RetryPolicyM IO
71 -- ^ How should exceptions during write be retried?
69 -- ^ How should exceptions during write be retried?
72 }
70 }
73
71
@@ -88,7 +86,7 @@ directAPIConnectionParams = C.ConnectionParams
88 }
86 }
89
87
90
88
91−-- | Configure to talk to a local Datadog agent at 127.0.0.1. TLS is not
89+-- | Configure to talk to a local DataDog agent at 127.0.0.1. TLS is not
92-- utilized. This is the preferred type of connection because
90-- utilized. This is the preferred type of connection because
93-- authentication, buffering, and other more advanced features are
91-- authentication, buffering, and other more advanced features are
94-- handled by the agent configuration.
92-- handled by the agent configuration.
@@ -105,42 +103,42 @@ localAgentConnectionParams port = C.ConnectionParams
105-- connections with a number of connections per stripe equal to
103-- connections with a number of connections per stripe equal to
106-- 'getNumCapabilities'. Retry policy will do an exponential backoff
104-- 'getNumCapabilities'. Retry policy will do an exponential backoff
107-- with a 25ms base delay for up to 5 retries for a total cumulative delay of 775ms
105-- with a 25ms base delay for up to 5 retries for a total cumulative delay of 775ms
108−mkDatadogScribeSettings :: C.ConnectionParams -> DatadogAuth -> IO DatadogScribeSettings
106+mkDataDogScribeSettings :: C.ConnectionParams -> DataDogAuth -> IO DataDogScribeSettings
109−mkDatadogScribeSettings connectionParams auth = do
107+mkDataDogScribeSettings connectionParams auth = do
110 capabilities <- Conc.getNumCapabilities
108 capabilities <- Conc.getNumCapabilities
111− pure $ DatadogScribeSettings
109+ pure $ DataDogScribeSettings
112− { datadogScribeSettings_connectionParams = connectionParams
110+ { dataDogScribeSettings_connectionParams = connectionParams
113− , datadogScribeSettings_auth = auth
111+ , dataDogScribeSettings_auth = auth
114− , datadogScribeSettings_poolStripes = 1
112+ , dataDogScribeSettings_poolStripes = 1
115− , datadogScribeSettings_connsPerStripe = capabilities
113+ , dataDogScribeSettings_connsPerStripe = capabilities
116− , datadogScribeSettings_connectionKeepalive = 30
114+ , dataDogScribeSettings_connectionKeepalive = 30
117− , datadogScribeSettings_retry = Retry.exponentialBackoff 25000 <> Retry.limitRetries 5
115+ , dataDogScribeSettings_retry = Retry.exponentialBackoff 25000 <> Retry.limitRetries 5
118 }
116 }
119
117
120
118
121-------------------------------------------------------------------------------
119-------------------------------------------------------------------------------
122−mkDatadogScribe
120+mkDataDogScribe
123− :: DatadogScribeSettings
121+ :: DataDogScribeSettings
124 -> PermitFunc
122 -> PermitFunc
125 -- ^ Function on whether to permit items, e.g. @permitItem InfoS@
123 -- ^ Function on whether to permit items, e.g. @permitItem InfoS@
126 -> Verbosity
124 -> Verbosity
127 -- ^ Verbosity level to observe
125 -- ^ Verbosity level to observe
128 -> IO Scribe
126 -> IO Scribe
129−mkDatadogScribe settings permit verb = do
127+mkDataDogScribe settings permit verb = do
130 connectionContext <- C.initConnectionContext
128 connectionContext <- C.initConnectionContext
131 pool <- Pool.createPool
129 pool <- Pool.createPool
132− (C.connectTo connectionContext (datadogScribeSettings_connectionParams settings))
130+ (C.connectTo connectionContext (dataDogScribeSettings_connectionParams settings))
133 C.connectionClose
131 C.connectionClose
134− (datadogScribeSettings_poolStripes settings)
132+ (dataDogScribeSettings_poolStripes settings)
135− (datadogScribeSettings_connectionKeepalive settings)
133+ (dataDogScribeSettings_connectionKeepalive settings)
136− (datadogScribeSettings_connsPerStripe settings)
134+ (dataDogScribeSettings_connsPerStripe settings)
137 pure $ Scribe
135 pure $ Scribe
138− { liPush = pushPool (datadogScribeSettings_retry settings) pool keyBuilder verb
136+ { liPush = pushPool (dataDogScribeSettings_retry settings) pool keyBuilder verb
139 , scribeFinalizer = Pool.destroyAllResources pool
137 , scribeFinalizer = Pool.destroyAllResources pool
140 , scribePermitItem = permit
138 , scribePermitItem = permit
141 }
139 }
142 where
140 where
143− !keyBuilder = case datadogScribeSettings_auth settings of
141+ !keyBuilder = case dataDogScribeSettings_auth settings of
144 NoAuthLocal -> Nothing
142 NoAuthLocal -> Nothing
145 DirectAuth (APIKey k) -> Just (BB.fromByteString (T.encodeUtf8 k))
143 DirectAuth (APIKey k) -> Just (BB.fromByteString (T.encodeUtf8 k))
146
144
@@ -162,7 +160,7 @@ pushPool retryPolicy pool token verb item =
162 Left _ -> True
160 Left _ -> True
163 Right _ -> False
161 Right _ -> False
164 where
162 where
165− payloadBuilder = A.fromEncoding (encodeDatadog verb item) Monoid.<> "\n"
163+ payloadBuilder = A.fromEncoding (encodeDatadog verb item) <> "\n"
166 messageBuilder = case token of
164 messageBuilder = case token of
167 --TODO: lots of conversion going on here. could use a bench
165 --TODO: lots of conversion going on here. could use a bench
168 Nothing -> payloadBuilder
166 Nothing -> payloadBuilder
katip-datadog/test/Katip/Tests/Scribes/Datadog/TCP.hs+11−18
@@ -8,7 +8,6 @@ module Katip.Tests.Scribes.Datadog.TCP
8import Control.Concurrent.Async
8import Control.Concurrent.Async
9import qualified Control.Exception.Safe as EX
9import qualified Control.Exception.Safe as EX
10import qualified Data.Aeson as A
10import qualified Data.Aeson as A
11−import qualified Data.Char as Char
12import qualified Data.Conduit as C
11import qualified Data.Conduit as C
13import qualified Data.Conduit.Binary as CB
12import qualified Data.Conduit.Binary as CB
14import qualified Data.Conduit.List as CL
13import qualified Data.Conduit.List as CL
@@ -17,8 +16,7 @@ import Data.Foldable (toList)
17import qualified Data.HashMap.Strict as HM
16import qualified Data.HashMap.Strict as HM
18import Data.IORef
17import Data.IORef
19import Data.Sequence ((|>))
18import Data.Sequence ((|>))
20−import Data.Text (Text)
19+import Data.Text
21−import qualified Data.Text as T
22import Test.Tasty
20import Test.Tasty
23import Test.Tasty.HUnit
21import Test.Tasty.HUnit
24-------------------------------------------------------------------------------
22-------------------------------------------------------------------------------
@@ -33,15 +31,14 @@ tests = testGroup "Katip.Scribes.Datadog.TCP"
33 [ testCase "logs well-formed messages in order" $ do
31 [ testCase "logs well-formed messages in order" $ do
34 logs <- collectLogs $ do
32 logs <- collectLogs $ do
35 logItem (sl "foo" ("bar" :: Text)) "mynamespace" noLoc InfoS "a message"
33 logItem (sl "foo" ("bar" :: Text)) "mynamespace" noLoc InfoS "a message"
36− length logs @?= 1
34+ logs @?=
37− let (CapturedLog ns dta sev msg thread) = head logs
35+ [ CapturedLog
38− ns @?= Namespace ["katip-datadog-tests", "mynamespace"]
36+ { capturedLog_namespace = Namespace ["katip-datadog-tests", "mynamespace"]
39− dta @?= A.Object (HM.singleton "foo" (A.String "bar"))
37+ , capturedLog_data = A.Object (HM.singleton "foo" (A.String "bar"))
40− sev @?= InfoS
38+ , capturedLog_sev = InfoS
41− msg @?= "a message"
39+ , capturedLog_message = "a message"
42− if T.all Char.isNumber thread && T.length thread > 0
40+ }
43− then pure ()
41+ ]
44− else assertFailure ("Expected thread id to just be a number but it was " <> T.unpack thread)
45 ]
42 ]
46 where
43 where
47 noLoc = Nothing
44 noLoc = Nothing
@@ -54,7 +51,6 @@ data CapturedLog = CapturedLog
54 , capturedLog_data :: A.Value
51 , capturedLog_data :: A.Value
55 , capturedLog_sev :: Severity
52 , capturedLog_sev :: Severity
56 , capturedLog_message :: Text
53 , capturedLog_message :: Text
57− , capturedLog_thread :: Text
58 } deriving (Show, Eq)
54 } deriving (Show, Eq)
59
55
60
56
@@ -64,16 +60,13 @@ instance A.FromJSON CapturedLog where
64 sev <- o A..: "severity"
60 sev <- o A..: "severity"
65 dta <- o A..: "data"
61 dta <- o A..: "data"
66 namespace <- o A..: "ns"
62 namespace <- o A..: "ns"
67− thread <- o A..: "thread"
68 pure $ CapturedLog
63 pure $ CapturedLog
69 { capturedLog_namespace = namespace
64 { capturedLog_namespace = namespace
70 , capturedLog_data = dta
65 , capturedLog_data = dta
71 , capturedLog_sev = sev
66 , capturedLog_sev = sev
72 , capturedLog_message = message
67 , capturedLog_message = message
73− , capturedLog_thread = thread
74 }
68 }
75
69
76−
77-------------------------------------------------------------------------------
70-------------------------------------------------------------------------------
78collectLogs :: KatipT IO () -> IO [CapturedLog]
71collectLogs :: KatipT IO () -> IO [CapturedLog]
79collectLogs f = do
72collectLogs f = do
@@ -86,8 +79,8 @@ collectLogs f = do
86 closeLE = closeScribes
79 closeLE = closeScribes
87 port = 1337
80 port = 1337
88 mkLE = do
81 mkLE = do
89− datadogScribeSettings <- mkDatadogScribeSettings (localAgentConnectionParams (fromInteger port)) NoAuthLocal
82+ dataDogScribeSettings <- mkDataDogScribeSettings (localAgentConnectionParams (fromInteger port)) NoAuthLocal
90− scribe <- mkDatadogScribe datadogScribeSettings (permitItem DebugS) V3
83+ scribe <- mkDataDogScribe dataDogScribeSettings DebugS V3
91 registerScribe "datadog" scribe defaultScribeSettings =<< initLogEnv "katip-datadog-tests" "test"
84 registerScribe "datadog" scribe defaultScribeSettings =<< initLogEnv "katip-datadog-tests" "test"
92 startServer capturedRef = CN.runTCPServer (CN.serverSettings (fromInteger port) "*") $ \appData -> C.runConduit $
85 startServer capturedRef = CN.runTCPServer (CN.serverSettings (fromInteger port) "*") $ \appData -> C.runConduit $
93 CN.appSource appData C..|
86 CN.appSource appData C..|
katip-elasticsearch/bench/Main.hs+3−3
@@ -15,11 +15,11 @@ import qualified Data.HashMap.Strict as HM
15import Data.Proxy (Proxy (..))
15import Data.Proxy (Proxy (..))
16import System.Random
16import System.Random
17import qualified Data.Text as T
17import qualified Data.Text as T
18−import Database.Bloodhound.Types
18+import Database.V1.Bloodhound.Types
19import Numeric
19import Numeric
20-------------------------------------------------------------------------------
20-------------------------------------------------------------------------------
21import Katip.Scribes.ElasticSearch
21import Katip.Scribes.ElasticSearch
22−import Katip.Scribes.ElasticSearch.Internal (ESV5)
22+import Katip.Scribes.ElasticSearch.Internal (ESV1)
23-------------------------------------------------------------------------------
23-------------------------------------------------------------------------------
24
24
25main :: IO ()
25main :: IO ()
@@ -34,7 +34,7 @@ main = do
34mkDocIdBenchmark :: Benchmark
34mkDocIdBenchmark :: Benchmark
35mkDocIdBenchmark = bgroup "mkDocId"
35mkDocIdBenchmark = bgroup "mkDocId"
36 [
36 [
37− bench "mkDocId (randomIO)" $ nfIO (mkDocId (Proxy :: Proxy ESV5))
37+ bench "mkDocId (randomIO)" $ nfIO (mkDocId (Proxy :: Proxy ESV1))
38 , bench "mkDocId' (shared )" $ nfIO mkDocId'
38 , bench "mkDocId' (shared )" $ nfIO mkDocId'
39 ]
39 ]
40
40
katip-elasticsearch/changelog.md−21
@@ -1,24 +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−
11−0.6.0.0
12−=======
13−* Update to katip-0.8.0.0 `PermitFunc` convention. To update, for
14− example, if you were passing `InfoS` to `mkEsScribe` to filter
15− severities, instead you should pass `(permitItem InfoS)`.
16−* Update examples to add necessary type signature for the default
17− scribe config.
18−* Bugfix: the default error retry policy was much more rapid than
19− documented.
20−* Internally switch to safe-exceptions from encolosed-exceptions.
21−
220.5.1.1
10.5.1.1
23=======
2=======
24* Loosen katip dep
3* Loosen katip dep
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
6
6
7-------------------------------------------------------------------------------
7-------------------------------------------------------------------------------
8import Control.Exception
8import Control.Exception
9−import Database.Bloodhound
9+import Database.V5.Bloodhound
10import Network.HTTP.Client
10import Network.HTTP.Client
11-------------------------------------------------------------------------------
11-------------------------------------------------------------------------------
12import Katip
12import Katip
katip-elasticsearch/katip-elasticsearch.cabal+19−16
@@ -1,7 +1,7 @@
1name: katip-elasticsearch
1name: katip-elasticsearch
2synopsis: ElasticSearch scribe for the Katip logging framework.
2synopsis: ElasticSearch scribe for the Katip logging framework.
3description: See README.md for more details.
3description: See README.md for more details.
4−version: 0.7.0.0
4+version: 0.6.0.0
5license: BSD3
5license: BSD3
6license-file: LICENSE
6license-file: LICENSE
7author: Ozgun Ataman, Michael Xavier
7author: Ozgun Ataman, Michael Xavier
@@ -35,23 +35,23 @@ library
35 Katip.Scribes.ElasticSearch.Internal
35 Katip.Scribes.ElasticSearch.Internal
36 build-depends:
36 build-depends:
37 base >=4.6 && <5
37 base >=4.6 && <5
38− , katip >= 0.8.0.0
38+ , katip >= 0.2.0.0 && < 0.9
39− , bloodhound >= 0.13.0.0
39+ , bloodhound >= 0.13.0.0 && < 0.17
40− , uuid >= 1.3.12
40+ , uuid >= 1.3.12 && < 1.4
41 , aeson >=0.6
41 , aeson >=0.6
42− , stm >= 2.4.3
42+ , stm >= 2.4.3 && < 2.6
43− , stm-chans >= 3.0.0.2
43+ , stm-chans >= 3.0.0.2 && < 3.1
44− , async >= 2.0.1.0
44+ , async >= 2.0.1.0 && < 3.0
45− , safe-exceptions >= 0.1.0.0
45+ , enclosed-exceptions >= 1.0.0 && < 1.1
46− , http-client >= 0.3
46+ , http-client >= 0.3 && < 0.6
47− , text >= 0.11
47+ , text >= 0.11 && < 1.3
48− , retry >= 0.7
48+ , retry >= 0.7 && < 0.8
49 , exceptions
49 , exceptions
50− , scientific >= 0.3.0.0
50+ , scientific >= 0.3.0.0 && < 0.4
51− , unordered-containers >= 0.1.0.0
51+ , unordered-containers >= 0.1.0.0 && < 0.3
52− , transformers >= 0.2
52+ , transformers >= 0.2 && < 0.6
53− , http-types >= 0.8
53+ , http-types >= 0.8 && < 0.13
54− , time >= 1
54+ , time >= 1 && < 1.9
55 , bytestring
55 , bytestring
56 , semigroups
56 , semigroups
57 hs-source-dirs: src
57 hs-source-dirs: src
@@ -108,4 +108,7 @@ test-suite test
108 , bytestring
108 , bytestring
109 , tagged
109 , tagged
110
110
111+ if flag(lib-Werror)
112+ ghc-options: -Wall -Werror
113+
111 ghc-options: -threaded -rtsopts
114 ghc-options: -threaded -rtsopts
katip-elasticsearch/src/Katip/Scribes/ElasticSearch.hs−5
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2-- | Includes a scribe that can be used to log structured, JSON log
1-- | Includes a scribe that can be used to log structured, JSON log
3-- messages to ElasticSearch. These logs can be explored easily using
2-- messages to ElasticSearch. These logs can be explored easily using
4-- <https://www.elastic.co/products/kibana kibana> or your tool of
3-- <https://www.elastic.co/products/kibana kibana> or your tool of
@@ -68,16 +67,12 @@ module Katip.Scribes.ElasticSearch
68 , essIndexSettings
67 , essIndexSettings
69 , essIndexSharding
68 , essIndexSharding
70 , defaultEsScribeCfg
69 , defaultEsScribeCfg
71−#if !MIN_VERSION_bloodhound(0,17,0)
72 , defaultEsScribeCfgV1
70 , defaultEsScribeCfgV1
73−#endif
74 , defaultEsScribeCfgV5
71 , defaultEsScribeCfgV5
75 -- ** Version-Proxied APIS
72 -- ** Version-Proxied APIS
76 -- $versionproxies
73 -- $versionproxies
77 , defaultEsScribeCfg'
74 , defaultEsScribeCfg'
78−#if !MIN_VERSION_bloodhound(0,17,0)
79 , ESV1
75 , ESV1
80−#endif
81 , ESV5
76 , ESV5
82 -- * Utilities
77 -- * Utilities
83 , mkDocId
78 , mkDocId
katip-elasticsearch/src/Katip/Scribes/ElasticSearch/Internal.hs+17−27
@@ -16,7 +16,8 @@ import Control.Applicative as A
16import Control.Concurrent
16import Control.Concurrent
17import Control.Concurrent.Async
17import Control.Concurrent.Async
18import Control.Concurrent.STM.TBMQueue
18import Control.Concurrent.STM.TBMQueue
19+import Control.Exception.Base
19−import qualified Control.Exception.Safe as EX
20+import Control.Exception.Enclosed
20import Control.Monad
21import Control.Monad
21import Control.Monad.Catch
22import Control.Monad.Catch
22import Control.Monad.IO.Class
23import Control.Monad.IO.Class
@@ -27,8 +28,8 @@ import Control.Retry (RetryPolicy,
27 recovering)
28 recovering)
28import Data.Aeson
29import Data.Aeson
29import Data.ByteString.Lazy (ByteString)
30import Data.ByteString.Lazy (ByteString)
30−import Data.List.NonEmpty (NonEmpty (..))
31+import Data.List.NonEmpty (NonEmpty(..))
31−import Data.Monoid as Monoid
32+import Data.Monoid ((<>))
32import Data.Text (Text)
33import Data.Text (Text)
33import qualified Data.Text as T
34import qualified Data.Text as T
34import qualified Data.Text.Encoding as T
35import qualified Data.Text.Encoding as T
@@ -37,12 +38,8 @@ import Data.Time.Calendar.WeekDate
37import Data.Typeable as Typeable
38import Data.Typeable as Typeable
38import Data.UUID
39import Data.UUID
39import qualified Data.UUID.V4 as UUID4
40import qualified Data.UUID.V4 as UUID4
40−#if MIN_VERSION_bloodhound(0,17,0)
41−import qualified Database.Bloodhound as V5
42−#else
43import qualified Database.V1.Bloodhound as V1
41import qualified Database.V1.Bloodhound as V1
44import qualified Database.V5.Bloodhound as V5
42import qualified Database.V5.Bloodhound as V5
45−#endif
46import Network.HTTP.Client
43import Network.HTTP.Client
47import Network.HTTP.Types.Status
44import Network.HTTP.Types.Status
48import Text.Printf (printf)
45import Text.Printf (printf)
@@ -99,7 +96,7 @@ data EsScribeCfg v = EsScribeCfg {
99-- * DailyIndexSharding
96-- * DailyIndexSharding
100defaultEsScribeCfg' :: ESVersion v => proxy v -> EsScribeCfg v
97defaultEsScribeCfg' :: ESVersion v => proxy v -> EsScribeCfg v
101defaultEsScribeCfg' prx = EsScribeCfg {
98defaultEsScribeCfg' prx = EsScribeCfg {
102− essRetryPolicy = exponentialBackoff 25000 Monoid.<> limitRetries 5
99+ essRetryPolicy = exponentialBackoff 25000 <> limitRetries 5
103 , essQueueSize = EsQueueSize 1000
100 , essQueueSize = EsQueueSize 1000
104 , essPoolSize = EsPoolSize 2
101 , essPoolSize = EsPoolSize 2
105 , essAnnotateTypes = False
102 , essAnnotateTypes = False
@@ -108,22 +105,19 @@ defaultEsScribeCfg' prx = EsScribeCfg {
108 }
105 }
109
106
110
107
111−
112−
113-------------------------------------------------------------------------------
108-------------------------------------------------------------------------------
114−-- | Alias of 'defaultEsScribeCfgV5' to minimize API
109+-- | Alias of 'defaultEsScribeCfgV1' to minimize API
115-- breakage. Previous versions of katip-elasticsearch only supported
110-- breakage. Previous versions of katip-elasticsearch only supported
116−-- ES version 1 and defaulted to it.
111+-- ES version 1.
117−defaultEsScribeCfg :: EsScribeCfg ESV5
112+defaultEsScribeCfg :: EsScribeCfg ESV1
118−defaultEsScribeCfg = defaultEsScribeCfgV5
113+defaultEsScribeCfg = defaultEsScribeCfgV1
119
114
120
115
121-------------------------------------------------------------------------------
116-------------------------------------------------------------------------------
122−#if !MIN_VERSION_bloodhound(0,17,0)
123-- | EsScribeCfg that will use ElasticSearch V1
117-- | EsScribeCfg that will use ElasticSearch V1
124defaultEsScribeCfgV1 :: EsScribeCfg ESV1
118defaultEsScribeCfgV1 :: EsScribeCfg ESV1
125defaultEsScribeCfgV1 = defaultEsScribeCfg' (Typeable.Proxy :: Typeable.Proxy ESV1)
119defaultEsScribeCfgV1 = defaultEsScribeCfg' (Typeable.Proxy :: Typeable.Proxy ESV1)
126−#endif
120+
127
121
128-------------------------------------------------------------------------------
122-------------------------------------------------------------------------------
129-- | EsScribeCfg that will use ElasticSearch V5
123-- | EsScribeCfg that will use ElasticSearch V5
@@ -288,21 +282,21 @@ mkEsScribe cfg@EsScribeCfg {..} env ix mapping permit verb = do
288 -- create or update
282 -- create or update
289 res <- putTemplate prx tpl tplName
283 res <- putTemplate prx tpl tplName
290 unless (statusIsSuccessful (responseStatus res)) $
284 unless (statusIsSuccessful (responseStatus res)) $
291− liftIO $ EX.throwIO (CouldNotPutTemplate res)
285+ liftIO $ throwIO (CouldNotPutTemplate res)
292 else do
286 else do
293 ixExists <- indexExists prx ix
287 ixExists <- indexExists prx ix
294 if ixExists
288 if ixExists
295 then do
289 then do
296 res <- updateIndexSettings prx (toUpdatabaleIndexSettings prx essIndexSettings) ix
290 res <- updateIndexSettings prx (toUpdatabaleIndexSettings prx essIndexSettings) ix
297 unless (statusIsSuccessful (responseStatus res)) $
291 unless (statusIsSuccessful (responseStatus res)) $
298− liftIO $ EX.throwIO (CouldNotUpdateIndexSettings res)
292+ liftIO $ throwIO (CouldNotUpdateIndexSettings res)
299 else do
293 else do
300 r1 <- createIndex prx essIndexSettings ix
294 r1 <- createIndex prx essIndexSettings ix
301 unless (statusIsSuccessful (responseStatus r1)) $
295 unless (statusIsSuccessful (responseStatus r1)) $
302− liftIO $ EX.throwIO (CouldNotCreateIndex r1)
296+ liftIO $ throwIO (CouldNotCreateIndex r1)
303 r2 <- putMapping prx ix mapping base
297 r2 <- putMapping prx ix mapping base
304 unless (statusIsSuccessful (responseStatus r2)) $
298 unless (statusIsSuccessful (responseStatus r2)) $
305− liftIO $ EX.throwIO (CouldNotCreateMapping r2)
299+ liftIO $ throwIO (CouldNotCreateMapping r2)
306
300
307 workers <- replicateM (unEsPoolSize essPoolSize) $ async $
301 workers <- replicateM (unEsPoolSize essPoolSize) $ async $
308 startWorker cfg env mapping q
302 startWorker cfg env mapping q
@@ -427,7 +421,7 @@ startWorker EsScribeCfg {..} env mapping q = go
427 popped <- atomically $ readTBMQueue q
421 popped <- atomically $ readTBMQueue q
428 case popped of
422 case popped of
429 Just (ixn, v) -> do
423 Just (ixn, v) -> do
430− sendLog ixn v `EX.catchAny` eat
424+ sendLog ixn v `catchAny` eat
431 go
425 go
432 Nothing -> return ()
426 Nothing -> return ()
433 prx :: Typeable.Proxy v
427 prx :: Typeable.Proxy v
@@ -440,8 +434,8 @@ startWorker EsScribeCfg {..} env mapping q = go
440 eat _ = return ()
434 eat _ = return ()
441 handler _ = Handler $ \e ->
435 handler _ = Handler $ \e ->
442 case fromException e of
436 case fromException e of
443− Just (_ :: EX.SomeAsyncException) -> return False
437+ Just (_ :: AsyncException) -> return False
444− _ -> return True
438+ _ -> return True
445
439
446
440
447-------------------------------------------------------------------------------
441-------------------------------------------------------------------------------
@@ -491,10 +485,7 @@ class ESVersion v where
491
485
492
486
493
487
494−#if !MIN_VERSION_bloodhound(0,17,0)
495data ESV1 = ESV1
488data ESV1 = ESV1
496−{-# DEPRECATED ESV1 "ESV1 is deprecated and removed in bloodhound >= 0.17.0" #-}
497−
498
489
499instance ESVersion ESV1 where
490instance ESVersion ESV1 where
500 type BHEnv ESV1 = V1.BHEnv
491 type BHEnv ESV1 = V1.BHEnv
@@ -534,7 +525,6 @@ instance ESVersion ESV1 where
534 [ "type" .= String "string"
525 [ "type" .= String "string"
535 , "index" .= String "analyzed"
526 , "index" .= String "analyzed"
536 ]
527 ]
537−#endif
538
528
539
529
540data ESV5 = ESV5
530data ESV5 = ESV5
katip-elasticsearch/test/Main.hs+7−23
@@ -34,12 +34,8 @@ import Data.Time
34import Data.Time.Calendar.WeekDate
34import Data.Time.Calendar.WeekDate
35import Data.Typeable as Typeable
35import Data.Typeable as Typeable
36import qualified Data.Vector as V
36import qualified Data.Vector as V
37−#if MIN_VERSION_bloodhound(0,17,0)
38−import qualified Database.Bloodhound as V5
39−#else
40import qualified Database.V1.Bloodhound as V1
37import qualified Database.V1.Bloodhound as V1
41import qualified Database.V5.Bloodhound as V5
38import qualified Database.V5.Bloodhound as V5
42−#endif
43import Network.HTTP.Client
39import Network.HTTP.Client
44import Network.HTTP.Types.Status
40import Network.HTTP.Types.Status
45import Test.QuickCheck.Instances ()
41import Test.QuickCheck.Instances ()
@@ -58,10 +54,8 @@ main :: IO ()
58main = defaultMainWithIngredients ings $ askOption $ \vers -> testGroup "katip-elasticsearch"
54main = defaultMainWithIngredients ings $ askOption $ \vers -> testGroup "katip-elasticsearch"
59 [
55 [
60 case vers of
56 case vers of
61−#if !MIN_VERSION_bloodhound(0,17,0)
62− TestV1 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV1)
57+ TestV1 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV1)
63−#endif
64− TestV5 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV5)
58+ TestV5 -> esTests (Typeable.Proxy :: Typeable.Proxy ESV5)
65 , typeAnnotatedTests
59 , typeAnnotatedTests
66 , roundToSundayTests
60 , roundToSundayTests
67 ]
61 ]
@@ -70,26 +64,18 @@ main = defaultMainWithIngredients ings $ askOption $ \vers -> testGroup "katip-e
70
64
71
65
72-------------------------------------------------------------------------------
66-------------------------------------------------------------------------------
73−data TestWithESVersion = TestV5
67+data TestWithESVersion = TestV1
74−#if !MIN_VERSION_bloodhound(0,17,0)
68+ | TestV5
75− | TestV1
76−#endif
77− deriving (Typeable)
69+ deriving (Typeable)
78
70
79
71
80instance IsOption TestWithESVersion where
72instance IsOption TestWithESVersion where
81− defaultValue = TestV5
73+ defaultValue = TestV1
82−#if !MIN_VERSION_bloodhound(0,17,0)
83 parseValue "1" = Just TestV1
74 parseValue "1" = Just TestV1
84−#endif
85 parseValue "5" = Just TestV5
75 parseValue "5" = Just TestV5
86 parseValue _ = Nothing
76 parseValue _ = Nothing
87 optionName = Tagged "es-version"
77 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."
78+ optionHelp = Tagged "Version of ES to test against, either 1 or 5, defaulting to 1."
90−#else
91− optionHelp = Tagged "Version of ES to test against, only v5 is supported, but should work for higher versions."
92−#endif
93
79
94
80
95class ESVersion v => TestESVersion v where
81class ESVersion v => TestESVersion v where
@@ -115,7 +101,6 @@ class ESVersion v => TestESVersion v where
115 searchByIndex :: proxy v -> IndexName v -> Search v -> BH v IO (Response ByteString)
101 searchByIndex :: proxy v -> IndexName v -> Search v -> BH v IO (Response ByteString)
116
102
117
103
118−#if !MIN_VERSION_bloodhound(0,17,0)
119instance TestESVersion ESV1 where
104instance TestESVersion ESV1 where
120 type Server ESV1 = V1.Server
105 type Server ESV1 = V1.Server
121 toServer _ = V1.Server
106 toServer _ = V1.Server
@@ -137,7 +122,6 @@ instance TestESVersion ESV1 where
137 refreshIndex _ = V1.refreshIndex
122 refreshIndex _ = V1.refreshIndex
138 withBH _ = V1.withBH
123 withBH _ = V1.withBH
139 searchByIndex _ = V1.searchByIndex
124 searchByIndex _ = V1.searchByIndex
140−#endif
141
125
142
126
143instance TestESVersion ESV5 where
127instance TestESVersion ESV5 where
katip-logzio/LICENSEdeleted−30
@@ -1,30 +0,0 @@
1−Copyright (c) 2018, Soostone Inc
2−
3−All rights reserved.
4−
5−Redistribution and use in source and binary forms, with or without
6−modification, are permitted provided that the following conditions are met:
7−
8− * Redistributions of source code must retain the above copyright
9− notice, this list of conditions and the following disclaimer.
10−
11− * Redistributions in binary form must reproduce the above
12− copyright notice, this list of conditions and the following
13− disclaimer in the documentation and/or other materials provided
14− with the distribution.
15−
16− * Neither the name of Ozgun Ataman nor the names of other
17− contributors may be used to endorse or promote products derived
18− from this software without specific prior written permission.
19−
20−THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21−"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22−LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23−A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24−OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25−SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26−LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27−DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28−THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29−(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30−OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
katip-logzio/README.mddeleted−4
@@ -1,4 +0,0 @@
1−# Katip Logz.IO [![Build Status](https://travis-ci.org/Soostone/katip.svg?branch=master)](https://travis-ci.org/Soostone/katip)
2−
3−katip-logzio is a scribe for the Katip logging framework that sends
4−structured logs to the logz.io centralized logging platform.
katip-logzio/changelog.mddeleted−9
@@ -1,9 +0,0 @@
1−0.1.1.0
2−=======
3−* Fixed bug where on scribe close, any items left in the buffer that
4− hadn't timed out or been flushed already would be dropped rather
5− than flushed. All users are encouraged to upgrade.
6−
7−0.1.0.0
8−==========
9−* Initial release
katip-logzio/katip-logzio.cabaldeleted−94
@@ -1,94 +0,0 @@
1−name: katip-logzio
2−version: 0.1.1.0
3−synopsis: Logz.IO scribe for the Katip logging framework
4−description: See README.md for more details.
5−license: BSD3
6−license-file: LICENSE
7−author: Michael Xavier
8−maintainer: michael.xavier@soostone.com
9−copyright: Soostone Inc, 2018
10−category: Data, Text, Logging
11−homepage: https://github.com/Soostone/katip
12−bug-reports: https://github.com/Soostone/katip/issues
13−build-type: Simple
14−cabal-version: >=1.10
15−extra-source-files:
16− README.md
17− changelog.md
18−
19−tested-with: GHC == 8.4.3
20−
21−source-repository head
22− type: git
23− location: https://github.com/Soostone/katip.git
24−
25−flag lib-Werror
26− default: False
27− manual: True
28−
29−
30−library
31− exposed-modules:
32− Katip.Scribes.LogzIO.HTTPS
33− build-depends:
34− base >=4.8.0.0 && <5
35− , katip >= 0.8.0.0
36− , stm >= 2.1.1
37− , async
38− , time >= 1.8.0.2
39− , aeson >= 0.10.0.0
40− , unix
41− , unordered-containers
42− , http-client >= 0.1.0
43− , http-client-tls >= 0.3.4
44− , uri-bytestring
45− , errors >= 2.0.0
46− , safe-exceptions
47− , retry
48− , http-types
49− , text
50− , bytestring
51− , scientific
52− , stm-chans >= 2.0.0
53− hs-source-dirs: src
54− default-language: Haskell2010
55− hs-source-dirs: src
56− ghc-options: -Wall -Wall -Wnoncanonical-monad-instances
57− if flag(lib-Werror)
58− ghc-options: -Werror
59−
60−
61−test-suite test
62− type: exitcode-stdio-1.0
63− main-is: Main.hs
64− hs-source-dirs: test
65− default-language: Haskell2010
66− other-modules:
67− Katip.Tests.Scribes.LogzIO.HTTPS
68− build-depends: base
69− , katip
70− , katip-logzio
71− , tasty
72− , tasty-hedgehog
73− , tasty-hunit
74− , hedgehog
75− , vector
76− , unordered-containers
77− , text
78− , bytestring
79− , time
80− , template-haskell
81− , aeson
82− , scientific
83− , unix
84− , hostname
85− , scotty
86− , stm
87− , async
88− , safe-exceptions
89− , uri-bytestring
90− , warp
91− , http-types
92− ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -Wnoncanonical-monad-instances
93− if flag(lib-Werror)
94− ghc-options: -Werror
katip-logzio/src/Katip/Scribes/LogzIO/HTTPS.hsdeleted−639
@@ -1,639 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE OverloadedStrings #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE TypeApplications #-}
7−-- | This is a log scribe that writes logs to logz.io's bulk
8−-- <https://app.logz.io/#/dashboard/data-sources/Bulk-HTTPS HTTPS
9−-- API>.
10−module Katip.Scribes.LogzIO.HTTPS
11− ( -- * Scribe construction
12− mkLogzIOScribe
13− -- * Types
14− , BulkAPIError(..)
15− , LogzIOScribeConfiguration(..)
16− , Scheme(..)
17− , APIToken(..)
18− , LoggingError(..)
19− -- ** Presets for configuration
20− , usRegionHost
21− , euRegionHost
22− , httpsPort
23− , httpPort
24− , defaultRetryPolicy
25− , defaultLogzIOScribeConfiguration
26− -- * Internal API exported for testing
27− , renderLineTruncated
28− , renderLineTruncated'
29− , maxPayloadBytes
30− , maxLogLineLength
31− , BulkBuffer (..)
32− , LogAction (..)
33− , bufferItem
34− , bufferItem'
35− , forceFlush
36− , Bytes (..)
37− ) where
38−
39−
40−-------------------------------------------------------------------------------
41−import Control.Applicative
42−import qualified Control.Concurrent.Async as Async
43−import qualified Control.Concurrent.STM as STM
44−import qualified Control.Concurrent.STM.TBMQueue as STM
45−import qualified Control.Error as E
46−import qualified Control.Exception.Safe as EX
47−import Control.Monad
48−import qualified Control.Retry as Retry
49−import qualified Data.Aeson as A
50−import qualified Data.ByteString.Builder as BB
51−import qualified Data.ByteString.Lazy as LBS
52−import qualified Data.ByteString.Lazy.Char8 as LBS8
53−import qualified Data.HashMap.Strict as HM
54−import Data.Int
55−import qualified Data.Scientific as Scientific
56−import Data.Semigroup as Semigroup
57−import Data.String (IsString)
58−import qualified Data.Text as T
59−import qualified Data.Text.Encoding as TE
60−import qualified Data.Text.Lazy as TL
61−import qualified Data.Text.Lazy.Builder as TB
62−import qualified Data.Time as Time
63−import qualified Katip as K
64−import Katip.Core (LocJs (..))
65−import qualified Network.HTTP.Client as HTTP
66−import qualified Network.HTTP.Client.TLS as HTTPS
67−import qualified Network.HTTP.Types as HTypes
68−import qualified System.Posix.Types as POSIX
69−import qualified URI.ByteString as URIBS
70−-------------------------------------------------------------------------------
71−
72−
73−
74−-- | This is returned when the bulk import was a partial success
75−data BulkAPIError = BulkAPIError
76− { bulkAPIError_malformedLines :: Int
77− -- ^ The number of log lines which are not well-formed JSON. This
78− -- indicates a __library bug__. Please file an issue on GitHub.
79− , bulkAPIError_successfulLines :: Int
80− -- ^ The number of log lines received successfully.
81− , bulkAPIError_oversizedLines :: Int
82− -- ^ The number of log lines which exceed the line length
83− -- limit. katip-logzio makes a best effort to truncate logs that
84− -- exceed the size limit. This probably indicates a __library bug__
85− -- and should be reported as an issue on GitHub.
86− , bulkAPIError_emptyLogLines :: Int
87− -- ^ The number of log lines which were empty. There isnt' really a
88− -- concept of a truly empty log line in katip, so this most likely
89− -- indicates a __library bug__ and should be reported as an issue on
90− -- GitHub.
91− } deriving (Show, Eq)
92−
93−
94−instance A.FromJSON BulkAPIError where
95− parseJSON = A.withObject "BulkAPIError" $ \o -> do
96− malformedLines <- o A..: "malformedLines"
97− successfulLines <- o A..: "successfulLines"
98− oversizedLines <- o A..: "oversizedLines"
99− emptyLogLines <- o A..: "emptyLogLines"
100− pure $ BulkAPIError
101− { bulkAPIError_malformedLines = malformedLines
102− , bulkAPIError_successfulLines = successfulLines
103− , bulkAPIError_oversizedLines = oversizedLines
104− , bulkAPIError_emptyLogLines = emptyLogLines
105− }
106−
107−
108−-------------------------------------------------------------------------------
109−data LogzIOScribeConfiguration = LogzIOScribeConfiguration
110− { logzIOScribeConfiguration_bufferItems :: Int
111− -- ^ Will flush the log buffer if this many items is in the buffer
112− -- __or__ 'logzIOScribeConfiguration_bufferTimeout' is reached,
113− -- whichever is first
114− , logzIOScribeConfiguration_bufferTimeout :: Time.NominalDiffTime
115− -- ^ Will flush the buffer if it has been this long since the last
116− -- flush __or__ 'logzIOScribeConfiguration_bufferItems' items are
117− -- accumulated, whichever is first. NominalDiffTime has a Num
118− -- instance, so you can use a literal to specify seconds, e.g. 30 =
119− -- 30s.
120− , logzIOScribeConfiguration_scheme :: Scheme
121− , logzIOScribeConfiguration_host :: URIBS.Host
122− , logzIOScribeConfiguration_port :: URIBS.Port
123− , logzIOScribeConfiguration_token :: APIToken
124− , logzIOScribeConfiguration_retry :: Retry.RetryPolicyM IO
125− -- ^ How should exceptions during writes be retried?
126− , logzIOScribeConfiguration_onError :: LoggingError -> IO ()
127− }
128−
129−
130−-- | A default configuration:
131−--
132−-- * 100 item buffering
133−--
134−-- * 30 second buffer timeout
135−--
136−-- * US, HTTPS logging endpoint (listener.logz.io:8071)
137−--
138−-- * 'defaultRetryPolicy' of 25ms exponential backoff, 5 retries
139−--
140−-- * Ignore logging errors
141−defaultLogzIOScribeConfiguration :: APIToken -> LogzIOScribeConfiguration
142−defaultLogzIOScribeConfiguration token = LogzIOScribeConfiguration
143− { logzIOScribeConfiguration_bufferItems = 100
144− , logzIOScribeConfiguration_bufferTimeout = 30
145− , logzIOScribeConfiguration_scheme = HTTPS
146− , logzIOScribeConfiguration_host = usRegionHost
147− , logzIOScribeConfiguration_port = httpsPort
148− , logzIOScribeConfiguration_token = token
149− , logzIOScribeConfiguration_retry = defaultRetryPolicy
150− , logzIOScribeConfiguration_onError = const (pure ())
151− }
152−
153−
154−-------------------------------------------------------------------------------
155−-- | You can retrieve your account or sub-account's API token on the
156−-- <https://app.logz.io/#/dashboard/settings/manage-accounts manage
157−-- accounts page>. Note that APIToken has an IsString instance,
158−-- meaning that you can use a string literal with OverloadedStrings
159−-- enabled.
160−newtype APIToken = APIToken
161− { apiToken :: T.Text
162− } deriving (Show, Eq, IsString)
163−
164−
165−-- | This particular bulk API only supports HTTP or HTTPS. HTTPS is
166−-- strongly recommended for security reasons.
167−data Scheme
168− = HTTPS
169− -- ^ HTTPs should always be used except for local testing.
170− | HTTP
171− deriving (Show, Eq)
172−
173−
174−-- | See
175−-- <https://support.logz.io/hc/en-us/articles/210206365-What-IP-addresses-should-I-open-in-my-firewall-to-ship-logs-to-Logz-io-
176−-- this> for a list of listeners. This is the US region host,
177−-- listener.logz.io
178−usRegionHost :: URIBS.Host
179−usRegionHost = URIBS.Host "listener.logz.io"
180−
181−
182−-- | See
183−-- <https://support.logz.io/hc/en-us/articles/210206365-What-IP-addresses-should-I-open-in-my-firewall-to-ship-logs-to-Logz-io-
184−-- this> for a list of listeners. This is the EU region host,
185−-- listener.logz.io
186−euRegionHost :: URIBS.Host
187−euRegionHost = URIBS.Host "listener-eu.logz.io"
188−
189−
190−-- | Logz.io uses port 8071 for HTTPS
191−httpsPort :: URIBS.Port
192−httpsPort = URIBS.Port 8071
193−
194−
195−-- | Logz.io uses port 8070 for HTTP
196−httpPort :: URIBS.Port
197−httpPort = URIBS.Port 8070
198−
199−
200−-- | A reasonable retry policy: exponential backoff with 25ms base
201−-- delay up to 5 retries, for a total cumulative delay of 775ms.
202−defaultRetryPolicy :: (Monad m) => Retry.RetryPolicyM m
203−defaultRetryPolicy = Retry.exponentialBackoff 25000 `mappend` Retry.limitRetries 5
204−
205−-------------------------------------------------------------------------------
206−mkLogzIOScribe
207− :: LogzIOScribeConfiguration
208− -> K.PermitFunc
209− -> K.Verbosity
210− -> IO K.Scribe
211−mkLogzIOScribe config permitItem verbosity = do
212− -- This size is actually somewhat arbitrary. We're just making sure
213− -- it isn't infinite so we don't consume the whole backlog right
214− -- away and take up lots of memory.. Katip above us does bounded
215− -- buffering and load-shedding when there's too much of a
216− -- backlog. Our writes are blocking to apply backpressure up to
217− -- katip. This value just holds onto values temporarily while we
218− -- concatenate them into a buffer.
219− ingestionQueue <- STM.newTBMQueueIO (itemBufferSize * 10)
220−
221− let newTimer = STM.registerDelay itemBufferTimeoutMicros
222− timerRef <- STM.newTVarIO =<< newTimer
223−
224− -- Set up a connection manager for requests
225− mgr <- case scheme of
226− HTTPS -> HTTPS.newTlsManager
227− HTTP -> HTTP.newManager HTTP.defaultManagerSettings
228−
229− -- An STM transaction that will return true when writes are stopped
230− -- and backlog is emptied.
231− let workExhausted :: STM.STM Bool
232− workExhausted = (&&)
233− <$> STM.isClosedTBMQueue ingestionQueue
234− <*> STM.isEmptyTBMQueue ingestionQueue
235− -- Block until there's no more work
236− let waitWorkExhausted :: STM.STM ()
237− waitWorkExhausted = STM.check =<< workExhausted
238− let pop :: STM.STM (Tick AnyLogItem)
239− pop = maybe WorkExhausted NewItem <$> STM.readTBMQueue ingestionQueue
240−
241− let timeExpired :: STM.STM (Tick a)
242− timeExpired = do
243− isExpired <- STM.readTVar =<< STM.readTVar timerRef
244− STM.check isExpired
245− pure TimeExpired
246−
247− -- Circular transaction that checks for completion of work, time
248− -- expiration, or new events.
249− let nextTick :: STM.STM (Tick AnyLogItem)
250− nextTick =
251− timeExpired <|>
252− pop <|>
253− (WorkExhausted <$ waitWorkExhausted)
254−
255− -- Blocking push. This applies backpressure upstream to katip, which does its own buffering
256− let push :: AnyLogItem -> STM.STM ()
257− push = void . STM.writeTBMQueue ingestionQueue
258−
259− let sealQueue = STM.atomically (STM.closeTBMQueue ingestionQueue)
260−
261− -- Replace the timer with a new one
262− let resetTimer = STM.atomically . STM.writeTVar timerRef =<< newTimer
263−
264− -- Send the buffer and then reset the timer
265− let flush curBuffer = do
266− res <- flushBuffer config mgr curBuffer
267− case res of
268− Left e -> onErrorSafe e
269− Right () -> pure ()
270− resetTimer
271−
272− let flushLoop :: BulkBuffer -> IO ()
273− flushLoop curBuffer = do
274− tick <- STM.atomically nextTick
275− case tick of
276− WorkExhausted -> do
277− flush curBuffer -- flush what you've got
278− pure () -- stop looping
279− TimeExpired -> do
280− flush curBuffer
281− flushLoop mempty
282− NewItem (AnyLogItem item) -> do
283− case bufferItem (logzIOScribeConfiguration_bufferItems config) verbosity item curBuffer of
284− Buffered newBuffer -> flushLoop newBuffer
285− FlushNow flushThis newBuffer -> do
286− flush flushThis
287− flushLoop newBuffer
288−
289− flushThread <- Async.async (flushLoop mempty)
290−
291− let close = do
292− sealQueue
293− _ <- Async.waitCatch flushThread
294− pure ()
295−
296− pure $ K.Scribe
297− { K.liPush = STM.atomically . push . AnyLogItem
298− , K.scribeFinalizer = close
299− , K.scribePermitItem = permitItem
300− }
301− where
302− itemBufferSize = logzIOScribeConfiguration_bufferItems config
303− itemBufferTimeoutMicros = ndtToMicros (logzIOScribeConfiguration_bufferTimeout config)
304− scheme = logzIOScribeConfiguration_scheme config
305− onErrorSafe ex = do
306− _ <- EX.tryAny (logzIOScribeConfiguration_onError config ex)
307− pure ()
308−
309−
310−data AnyLogItem where
311− AnyLogItem :: K.LogItem a => K.Item a -> AnyLogItem
312−
313−
314−-------------------------------------------------------------------------------
315−data Tick a =
316− TimeExpired
317− | NewItem !a
318− | WorkExhausted
319−
320−
321−-------------------------------------------------------------------------------
322−-- Match the native resolution of NominalDiffTime, which is excessive
323−-- for our needs
324−ndtPicos :: Time.NominalDiffTime -> Int64
325−ndtPicos = round . (* picos)
326− where
327− picos :: Time.NominalDiffTime
328− picos = 10 ^ (9 :: Int)
329−
330−
331−-------------------------------------------------------------------------------
332−ndtToMicros :: Time.NominalDiffTime -> Int
333−ndtToMicros t = round (((fromIntegral (ndtPicos t)) :: Double) / picosInMicro)
334− where
335− picosInMicro = 10 ^ (3 :: Int)
336−
337−
338−-------------------------------------------------------------------------------
339−data LoggingError =
340− URIError HTTP.HttpException
341− -- ^ The URI generated was invalid. Check your configuration
342− | RequestError HTTP.HttpException
343− -- ^ We encountered an exception while sending the batch request
344− | PartialFailure BulkAPIError
345− -- ^ Some or all of the request was rejected. Check the logz.io UI
346− -- for indexing errors.
347− | BadToken
348− -- ^ Your API token was rejected.
349− | UnknownFailureResponse HTypes.Status LBS.ByteString
350− -- ^ An error returned, but it could not be decoded into a
351− -- 'BulkAPIError'. This may indicate a __library bug__, which should
352− -- be reported to the issue tracker.
353− deriving (Show)
354−
355−-------------------------------------------------------------------------------
356−flushBuffer
357− :: LogzIOScribeConfiguration
358− -> HTTP.Manager
359− -> BulkBuffer
360− -> IO (Either LoggingError ())
361−flushBuffer config mgr bulkBuffer
362− | bulkBuffer_itemCount bulkBuffer <= 0 = pure (Right ())
363− | otherwise = do
364− E.runExceptT $ do
365− req <- E.fmapLT URIError (E.ExceptT (EX.try (configureRequest <$> HTTP.parseRequest uriStr)))
366− resp <- E.fmapLT RequestError $ E.ExceptT $ EX.try $
367− Retry.recovering retryPolicy [\_stat -> EX.Handler handleHttpException] $ \_stat ->
368− HTTP.httpLbs req mgr
369− let respLBS = HTTP.responseBody resp
370− let respStatus = HTTP.responseStatus resp
371− if HTypes.statusIsSuccessful respStatus
372− then pure ()
373− else case A.decode @BulkAPIError respLBS of
374− Nothing
375− | HTypes.statusCode respStatus == 401 -> E.throwE BadToken
376− | otherwise -> E.throwE (UnknownFailureResponse respStatus respLBS)
377− Just bulkError -> E.throwE (PartialFailure bulkError)
378− where
379− configureRequest req = req
380− { HTTP.method = HTypes.methodPost
381− , HTTP.requestBody = HTTP.RequestBodyLBS (BB.toLazyByteString (bulkBuffer_payload bulkBuffer))
382− }
383−
384− retryPolicy = logzIOScribeConfiguration_retry config
385− apiTokenBS = TE.encodeUtf8 (apiToken (logzIOScribeConfiguration_token config))
386−
387− handleHttpException :: (Applicative m) => HTTP.HttpException -> m Bool
388− handleHttpException _ = pure True
389−
390− uriStr = LBS8.unpack (BB.toLazyByteString (URIBS.serializeURIRef uri))
391−
392− authority = URIBS.Authority
393− { URIBS.authorityUserInfo = Nothing
394− , URIBS.authorityHost = logzIOScribeConfiguration_host config
395− , URIBS.authorityPort = Just (logzIOScribeConfiguration_port config)
396− }
397−
398− uri = URIBS.URI
399− { URIBS.uriScheme = case logzIOScribeConfiguration_scheme config of
400− HTTPS -> URIBS.Scheme "https"
401− HTTP -> URIBS.Scheme "http"
402− , URIBS.uriAuthority = Just authority
403− , URIBS.uriPath = "/"
404− , URIBS.uriQuery = URIBS.Query
405− [ ("token", apiTokenBS)
406− ]
407− , URIBS.uriFragment = Nothing
408− }
409−
410−
411−-------------------------------------------------------------------------------
412−newtype Bytes = Bytes
413− { bytes :: Int64
414− } deriving (Show, Eq, Num, Ord, Bounded)
415−
416−
417−-- | How big of a body can we send? The limit is defined
418−-- <https://app.logz.io/#/dashboard/data-sources/Bulk-HTTPS here>.
419−maxPayloadBytes :: Bytes
420−maxPayloadBytes = 10485760
421−
422−
423−-- | How long can each serialized payload be (let's assume including
424−-- the trailing newline). The limit is defined
425−-- <https://app.logz.io/#/dashboard/data-sources/Bulk-HTTPS here>.
426−maxLogLineLength :: Bytes
427−maxLogLineLength = 500000
428−
429−
430−measureJSONLine :: A.ToJSON a => a -> (BB.Builder, Bytes)
431−measureJSONLine a = (BB.lazyByteString lbs, Bytes (LBS.length lbs))
432− where
433− lbs = A.encode a <> "\n"
434−
435−
436−-- | Fully-rendered JSON object for an item
437−fullItemObject :: K.LogItem a => K.Verbosity -> K.Item a -> A.Object
438−fullItemObject verbosity item = HM.fromList
439− [ "app" A..= K._itemApp item
440− , "env" A..= K._itemEnv item
441− , "sev" A..= K._itemSeverity item
442− , "thread" A..= K.getThreadIdText (K._itemThread item)
443− , "host" A..= K._itemHost item
444− , "pid" A..= pidInt
445− , "data" A..= annotateKeys (K.payloadObject verbosity (K._itemPayload item))
446− -- Slight deviation, logz.io uses "message" instead of "msg"
447− , "message" A..= TB.toLazyText (K.unLogStr (K._itemMessage item))
448− -- Another slight deviation, logz.io uses "@timestamp" instead of
449− -- "at". Note, your logs should be sent roughly close to when they
450− -- are created. They are assigned to indexes based on index date, so
451− -- time searches act strange if you backfill too far from the
452− -- past. They seem to support 3 decimal places of
453− -- precision. Formatting like this requires time 1.8.0.2, which is
454− -- reflected in the cabal file.
455− , "@timestamp" A..= A.String (T.pack (Time.formatTime Time.defaultTimeLocale "%Y-%m-%dT%H:%M:%S%03QZ" (K._itemTime item)))
456− , "ns" A..= K._itemNamespace item
457− , "loc" A..= (LocJs <$> K._itemLoc item)
458− ]
459− where
460− POSIX.CPid pidInt = K._itemProcess item
461−
462−
463−-- | A version of 'renderLine' which renders a line and stays under
464−-- the maximum line size of 500,000 bytes. If the default rendering is
465−-- too large, the log will be reduced to a timestamp and a potentially
466−-- truncated message.
467−renderLineTruncated :: K.LogItem a => K.Verbosity -> K.Item a -> (BB.Builder, Bytes)
468−renderLineTruncated = renderLineTruncated' maxLogLineLength
469−
470−
471−-- | A generalized renderLineTruncated that takes a custom line length
472−-- limit. This is exclusively for testing.
473−renderLineTruncated'
474− :: K.LogItem a
475− => Bytes
476− -- ^ Custom max log line length. Be careful, too low and no amount
477− -- of shrinking can get under the size, breaking the invariant. For
478− -- the production limit, we are guraanteed always able to come in
479− -- under the limit.
480− -> K.Verbosity
481− -> K.Item a
482− -> (BB.Builder, Bytes)
483−renderLineTruncated' customMaxLogLineLength verbosity item =
484− if fullSize <= customMaxLogLineLength
485− then (fullLine, fullSize)
486− else (fallbackLine, fallbackSize)
487− where
488− fullObject = fullItemObject verbosity item
489− (fullLine, fullSize) = measureJSONLine fullObject
490− -- only the absolutely necessary keys, with message stripped out
491− -- to help us calculate how much of the message we can keep. these
492− -- are lazily evaluated and as such won't be computed unless the
493− -- item is too big
494− blankObject :: A.Object
495− blankObject = HM.fromList
496− [ "message" A..= A.String "" -- we'll start with a blank message
497− , "@timestamp" A..= K._itemTime item
498− ]
499− (_, blankObjectSize) = measureJSONLine blankObject
500− messageBytesAllowed = maxLogLineLength - blankObjectSize
501− (fallbackLine, fallbackSize) = measureJSONLine fallbackObject
502− fallbackObject :: A.Object
503− fallbackObject = HM.fromList
504− [ "message" A..= A.toJSON (TL.take (bytes messageBytesAllowed) (TB.toLazyText (K.unLogStr (K._itemMessage item))))
505− , "@timestamp" A..= A.toJSON (K._itemTime item)
506− ]
507−
508−
509−data BulkBuffer = BulkBuffer
510− { bulkBuffer_bytesUsed :: !Bytes
511− , bulkBuffer_payload :: !BB.Builder
512− , bulkBuffer_itemCount :: !Int
513− }
514−
515−
516−instance Semigroup.Semigroup BulkBuffer where
517− (BulkBuffer bytesUsedA bufferA itemCountA) <>
518− (BulkBuffer bytesUsedB bufferB itemCountB) = BulkBuffer
519− (bytesUsedA + bytesUsedB)
520− (bufferA <> bufferB)
521− (itemCountA + itemCountB)
522−
523−
524−instance Monoid BulkBuffer where
525− mempty = BulkBuffer 0 mempty 0
526− mappend = (<>)
527−
528−
529−data LogAction buf =
530− Buffered
531− buf
532− -- ^ New buffer
533− | FlushNow
534− buf
535− -- ^ Buffer to flush
536− buf
537− -- ^ New buffer
538−
539−bufferItem
540− :: K.LogItem a
541− => Int
542− -- ^ Maximum items before flushing
543− -> K.Verbosity
544− -> K.Item a
545− -> BulkBuffer
546− -> LogAction BulkBuffer
547−bufferItem = bufferItem' maxPayloadBytes maxLogLineLength
548−
549−
550−-- | An internal version with a configurable max item size and max
551−-- message size for testing. Be careful with this: if you set the
552−-- values too low, some of the guarantees about reducability of
553−-- messages break down.
554−bufferItem'
555− :: (K.LogItem a)
556− => Bytes
557− -- ^ Max payload size
558− -> Bytes
559− -- ^ Max item size
560− -> Int
561− -- ^ Maximum items before flushing
562− -> K.Verbosity
563− -> K.Item a
564− -> BulkBuffer
565− -> LogAction BulkBuffer
566−bufferItem' customMaxPayload customMaxItem maxItems verb item bulkBuffer =
567− let (encodedLine, spaceNeeded) = renderLineTruncated' customMaxItem verb item
568− newBytesUsed = bulkBuffer_bytesUsed bulkBuffer + spaceNeeded
569− newItemCount = bulkBuffer_itemCount bulkBuffer + 1
570− in if newItemCount >= maxItems || newBytesUsed >= customMaxPayload
571− then FlushNow
572− bulkBuffer
573− BulkBuffer
574− { bulkBuffer_bytesUsed = spaceNeeded
575− , bulkBuffer_payload = encodedLine
576− , bulkBuffer_itemCount = 1
577− }
578− else Buffered $
579− BulkBuffer
580− { bulkBuffer_bytesUsed = newBytesUsed
581− , bulkBuffer_payload = bulkBuffer_payload bulkBuffer <> encodedLine
582− , bulkBuffer_itemCount = newItemCount
583− }
584−
585−
586−-- | When time has run out on a flush, splits the buffer. n.b. this
587−-- will always return 'FlushNow' with an empty new buffer.
588−forceFlush :: (Monoid buf) => buf -> LogAction buf
589−forceFlush buf = FlushNow buf mempty
590−
591−
592−-------------------------------------------------------------------------------
593−-- Annotation borrowed from katip-elasticsearch. There are fixed
594−-- fields in katip logs which should stay the same type forever and
595−-- thus won't need annotation. However, any code in userland may
596−-- choose to add akey to their log's metadata with a type that's
597−-- incompatible with the mapping. If the first value that's picked up
598−-- restrictive (e.g. a long), subsequent values will be rejected. By
599−-- differentiating types by their name, this is guaranteed not to
600−-- happen.
601−
602−annotateValue :: A.Value -> A.Value
603−annotateValue (A.Object o) = A.Object (annotateKeys o)
604−annotateValue (A.Array a) = A.Array (annotateValue <$> a)
605−annotateValue x = x
606−
607−
608−annotateKeys :: A.Object -> A.Object
609−annotateKeys = HM.fromList . map go . HM.toList
610− where
611− go (k, A.Object o) = (k, A.Object (annotateKeys o))
612− go (k, A.Array a) = (k, A.Array (annotateValue <$> a))
613− go (k, s@(A.String _)) = (k <> stringAnn, s)
614− go (k, n@(A.Number sci)) = if Scientific.isFloating sci
615− then (k <> doubleAnn, n)
616− else (k <> longAnn, n)
617− go (k, b@(A.Bool _)) = (k <> booleanAnn, b)
618− go (k, A.Null) = (k <> nullAnn, A.Null)
619−
620−
621−-------------------------------------------------------------------------------
622−-- Annotation Constants
623−-------------------------------------------------------------------------------
624−
625−
626−stringAnn :: T.Text
627−stringAnn = "::s"
628−
629−doubleAnn :: T.Text
630−doubleAnn = "::d"
631−
632−longAnn :: T.Text
633−longAnn = "::l"
634−
635−booleanAnn :: T.Text
636−booleanAnn = "::b"
637−
638−nullAnn :: T.Text
639−nullAnn = "::n"
katip-logzio/test/Katip/Tests/Scribes/LogzIO/HTTPS.hsdeleted−466
@@ -1,466 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−module Katip.Tests.Scribes.LogzIO.HTTPS
3− ( tests
4− ) where
5−
6−
7−-------------------------------------------------------------------------------
8−import Control.Concurrent.Async
9−import Control.Concurrent.STM
10−import Control.Concurrent.STM.TSem
11−import Control.Exception.Safe
12−import Control.Monad
13−import Control.Monad.IO.Class
14−import Data.Aeson as A
15−import qualified Data.ByteString.Builder as BB
16−import qualified Data.ByteString.Lazy as LBS
17−import qualified Data.ByteString.Lazy.Char8 as LBS8
18−import qualified Data.HashMap.Strict as HM
19−import Data.Int
20−import Data.Scientific
21−import Data.Text (Text)
22−import Data.Time
23−import Data.Time.Clock.POSIX
24−import qualified Data.Vector as V
25−import Hedgehog as HH
26−import qualified Hedgehog.Gen as Gen
27−import qualified Hedgehog.Range as Range
28−import Katip
29−import Katip.Core
30−import Language.Haskell.TH (Loc (..))
31−import Network.HostName
32−import qualified Network.HTTP.Types.Status as HTypes
33−import qualified Network.Wai.Handler.Warp as Warp
34−import System.Posix
35−import System.Timeout
36−import Test.Tasty
37−import Test.Tasty.Hedgehog
38−import Test.Tasty.HUnit
39−import URI.ByteString
40−import qualified Web.Scotty as Scotty
41−-------------------------------------------------------------------------------
42−import Katip.Scribes.LogzIO.HTTPS
43−-------------------------------------------------------------------------------
44−
45−
46−tests :: TestTree
47−tests = testGroup "Katip.Scribes.LogzIO.HTTP"
48− [ bufferItemTests
49− , renderLineTruncatedTests
50− , scribeTests
51− ]
52−
53−
54−-------------------------------------------------------------------------------
55−-- We bind to a system port so we use a lock on these tests to avoid interleaved, concurrent requests
56−scribeTests :: TestTree
57−scribeTests = withLock $ \mkSem -> testGroup "scribe"
58− [ testCase "writes well-formed log items as lines to the given API" $ do
59− sem <- mkSem
60− withServer sem $ \receivedMessages -> do
61− withScribe (\cfg -> cfg { logzIOScribeConfiguration_bufferTimeout = 1}) $ \scribe -> do
62− (liPush scribe) (genericLog "a message")
63−
64− requestLog <- timeoutFailure (20 * second) (atomically (readTQueue receivedMessages))
65− requestLog_token requestLog @?= testAPIToken
66− let logs = requestLog_logs requestLog
67− length logs @?= 1
68− let firstLog = head logs
69− receivedLog_message firstLog @?= "a message"
70− , testCase "respects the items limit" $ do
71− sem <- mkSem
72− withServer sem $ \receivedMessages -> do
73− withScribe (\cfg -> cfg { logzIOScribeConfiguration_bufferItems = 1, logzIOScribeConfiguration_bufferTimeout = 1}) $ \scribe -> do
74− (liPush scribe) (genericLog "message 1")
75− (liPush scribe) (genericLog "message 2")
76−
77− log1 <- timeoutFailure (20 * second) (atomically (readTQueue receivedMessages))
78− requestLog_token log1 @?= testAPIToken
79− let logs1 = requestLog_logs log1
80− length logs1 @?= 1
81− let firstLog1 = head logs1
82− receivedLog_message firstLog1 @?= "message 1"
83−
84− log2 <- timeoutFailure (20 * second) (atomically (readTQueue receivedMessages))
85− let logs2 = requestLog_logs log2
86− length logs2 @?= 1
87− let firstLog2 = head logs2
88− receivedLog_message firstLog2 @?= "message 2"
89− , testCase "respects the time limit" $ do
90− sem <- mkSem
91− withServer sem $ \receivedMessages -> do
92− withScribe (\cfg -> cfg { logzIOScribeConfiguration_bufferItems = 2, logzIOScribeConfiguration_bufferTimeout = 1}) $ \scribe -> do
93− (liPush scribe) (genericLog "a message")
94− log1 <- timeoutFailure (20 * second) (atomically (readTQueue receivedMessages))
95− requestLog_token log1 @?= testAPIToken
96− let logs1 = requestLog_logs log1
97− length logs1 @?= 1
98− let firstLog1 = head logs1
99− receivedLog_message firstLog1 @?= "a message"
100− , testCase "reports errors" $ do
101− sem <- mkSem
102− let badToken = APIToken "badtoken"
103− errorSignal <- newEmptyTMVarIO
104− withServer sem $ \_receivedMessages -> do
105− withScribe (\cfg -> cfg { logzIOScribeConfiguration_bufferItems = 1, logzIOScribeConfiguration_bufferTimeout = 1, logzIOScribeConfiguration_token = badToken, logzIOScribeConfiguration_onError = void . atomically . tryPutTMVar errorSignal}) $ \scribe -> do
106− (liPush scribe) (genericLog "a message")
107− loggingError <- timeoutFailure (20 * second) (atomically (takeTMVar errorSignal))
108− case loggingError of
109− BadToken -> pure ()
110− _ -> assertFailure ("Expected BadToken error but got " <> show loggingError)
111− , testCase "flushes all queued logs on close" $ do
112− sem <- mkSem
113− withServer sem $ \receivedMessages -> do
114− withScribe (\cfg -> cfg { logzIOScribeConfiguration_bufferItems = 100, logzIOScribeConfiguration_bufferTimeout = 100}) $ \scribe -> do
115− (liPush scribe) (genericLog "message 1")
116− (liPush scribe) (genericLog "message 2")
117− messages <- atomically (flushTQueue receivedMessages)
118− let allLogs = receivedLog_message <$> mconcat (requestLog_logs <$> messages)
119− allLogs @?= ["message 1", "message 2"]
120− ]
121− where
122− second = 1000000
123− timeoutFailure waitTime f = do
124− res <- timeout waitTime f
125− case res of
126− Nothing -> assertFailure ("Operation timed out after " <> show waitTime <> "us")
127− Just x -> pure x
128− withLock = withResource (atomically (newTSem 1)) (const (return ()))
129− withServer sem f = bracket_ (atomically (waitTSem sem)) (atomically (signalTSem sem)) $ do
130− receivedMessages <- atomically newTQueue
131− withAsync (startServer receivedMessages) $ \_async -> f receivedMessages
132− testAPIToken = "bigSECRET"
133− genericLog message = Item
134− { _itemApp = "katip-logzio"
135− , _itemEnv = "test"
136− , _itemSeverity = InfoS
137− , _itemThread = ThreadIdText "42"
138− , _itemHost = "localhost"
139− , _itemProcess = 111
140− , _itemPayload = ()
141− , _itemMessage = message
142− , _itemTime = mkUTCTime 2019 1 2 3 4 5
143− , _itemNamespace = "test"
144− , _itemLoc = Nothing
145− }
146− startServer receivedMessages = Scotty.scottyOpts serverOpts $ do
147− Scotty.post "/" $ do
148− token <- APIToken <$> Scotty.param "token"
149− if token == testAPIToken
150− then do
151− bodyLines <- LBS8.lines <$> Scotty.body
152− receivedLogs <- mapM (either error pure . A.eitherDecode) bodyLines
153− let requestLog = RequestLog
154− { requestLog_token = token
155− , requestLog_logs = receivedLogs
156− }
157− liftIO (atomically (writeTQueue receivedMessages requestLog))
158− else Scotty.status HTypes.unauthorized401
159− serverOpts = Scotty.Options
160− { Scotty.verbose = 0
161− , Scotty.settings = Warp.setPort 1337 Warp.defaultSettings
162− }
163− withScribe modConfig =
164− bracket (mkLogzIOScribe (modConfig scribeConfig) (const (pure True)) V3) scribeFinalizer
165− scribeConfig = LogzIOScribeConfiguration
166− { logzIOScribeConfiguration_bufferItems = 5
167− , logzIOScribeConfiguration_bufferTimeout = 10
168− , logzIOScribeConfiguration_scheme = HTTP
169− , logzIOScribeConfiguration_host = Host "127.0.0.1"
170− , logzIOScribeConfiguration_port = Port 1337
171− , logzIOScribeConfiguration_token = testAPIToken
172− , logzIOScribeConfiguration_retry = defaultRetryPolicy
173− , logzIOScribeConfiguration_onError = const (pure ())
174− }
175−
176−
177−
178−data RequestLog = RequestLog
179− { requestLog_token :: APIToken
180− , requestLog_logs :: [ReceivedLog]
181− }
182−
183−
184−-- | Simplified log that only parses the fields we want to assert on
185−data ReceivedLog = ReceivedLog
186− { receivedLog_message :: Text
187− }
188−
189−
190−instance FromJSON ReceivedLog where
191− parseJSON = withObject "ReceivedLog" $ \o -> do
192− message <- o .: "message"
193− pure $ ReceivedLog
194− { receivedLog_message = message
195− }
196−
197−
198−-------------------------------------------------------------------------------
199−bufferItemTests :: TestTree
200−bufferItemTests = testGroup "bufferItem"
201− [ testProperty "0 or negative maximum items always flushes immediately" $ property $ do
202− item <- forAllWith showSimplePayloadItem genSimplePayloadItem
203− verbosity <- forAll genVerbosity
204− maxItems <- forAll (Gen.int (Range.linear minBound 0))
205− bulkBuffer <- forAllWith inspectBulkBuffer genBulkBuffer
206− let logAction = bufferItem' reducedMaxPayloadBytes reducedMaxLogLineLength maxItems verbosity item bulkBuffer
207− assertFlushNow logAction
208− , testProperty "never buffers too many items" $ property $ do
209− item <- forAllWith showSimplePayloadItem genSimplePayloadItem
210− verbosity <- forAll genVerbosity
211− maxItems <- forAll (Gen.int (Range.linear 0 100))
212− bulkBuffer <- forAllWith inspectBulkBuffer genBulkBuffer
213− let logAction = bufferItem' reducedMaxPayloadBytes reducedMaxLogLineLength maxItems verbosity item bulkBuffer
214− case logAction of
215− Buffered newBuffer -> HH.assert (bulkBuffer_itemCount newBuffer <= maxItems)
216− _ -> pure ()
217− , testProperty "never produces too many bytes" $ property $ do
218− item <- forAllWith showSimplePayloadItem genSimplePayloadItem
219− verbosity <- forAll genVerbosity
220− maxItems <- forAll (Gen.int (Range.linear 0 100))
221− bulkBuffer <- forAllWith inspectBulkBuffer genBulkBuffer
222− let logAction = bufferItem' reducedMaxPayloadBytes reducedMaxLogLineLength maxItems verbosity item bulkBuffer
223− case logAction of
224− Buffered newBuffer -> HH.assert (bulkBuffer_bytesUsed newBuffer <= reducedMaxPayloadBytes)
225− _ -> pure ()
226− , testProperty "never flushes more than 1 item" $ property $ do
227− item <- forAllWith showSimplePayloadItem genSimplePayloadItem
228− verbosity <- forAll genVerbosity
229− maxItems <- forAll (Gen.int (Range.linear minBound 0))
230− bulkBuffer <- forAllWith inspectBulkBuffer genBulkBuffer
231− let logAction = bufferItem' reducedMaxPayloadBytes reducedMaxLogLineLength maxItems verbosity item bulkBuffer
232− case logAction of
233− FlushNow _ newBuffer -> bulkBuffer_itemCount newBuffer === 1
234− _ -> pure ()
235− , testProperty "flushes when the buffer is exactly at the limit" $ property $ do
236− maxItems <- forAll (Gen.int (Range.linear 1 20))
237− item <- forAllWith showSimplePayloadItem genSimplePayloadItem
238− bulkBuffer <- forAllWith inspectBulkBuffer ((\b -> b { bulkBuffer_itemCount = maxItems - 1 }) <$> genBulkBuffer)
239− verbosity <- forAll genVerbosity
240− let logAction = bufferItem' maxBound maxBound maxItems verbosity item bulkBuffer
241− case logAction of
242− FlushNow _ _ -> pure ()
243− Buffered _ -> fail "Expected a FlushNow but got a Buffered"
244− ]
245− where
246− assertFlushNow (FlushNow _ _) = pure ()
247− assertFlushNow (Buffered _) = fail ("Expceted FlushNow but got a Buffered")
248− inspectBulkBuffer (BulkBuffer used _ itemCount) =
249− "BulkBuffer (" <> show used <> ") ... " <> show itemCount
250−
251−
252−-------------------------------------------------------------------------------
253−-- | Reduced max log line length for more space and time efficient tests
254−reducedMaxLogLineLength :: Bytes
255−reducedMaxLogLineLength = Bytes (bytes maxLogLineLength `div` limitScalingFactor)
256−
257−
258−reducedMaxPayloadBytes :: Bytes
259−reducedMaxPayloadBytes = Bytes (bytes maxPayloadBytes `div` limitScalingFactor)
260−
261−
262−limitScalingFactor :: Int64
263−limitScalingFactor = 10
264−
265−-------------------------------------------------------------------------------
266−showSimplePayloadItem :: Item SimpleLogPayload -> String
267−showSimplePayloadItem item =
268− show (item { _itemPayload = [(k, toJSON v) | (k, AnyLogPayload v) <- unSimpleLogPayload (_itemPayload item)]})
269−
270−
271−-------------------------------------------------------------------------------
272−renderLineTruncatedTests :: TestTree
273−renderLineTruncatedTests = testGroup "renderLineTruncated"
274− [ testProperty "always produces an accurate byte count" $ property $ do
275− item <- forAllWith showSimplePayloadItem genSimplePayloadItem
276− verbosity <- forAll genVerbosity
277− let (builder, Bytes byteCount) = renderLineTruncated' reducedMaxLogLineLength verbosity item
278− builderLength builder === byteCount
279− , testProperty "always produces a payload smaller than the line limit" $ property $ do
280− let tinyMaxLineLength = 1024 -- set a really low one for speed
281− let lowerBound = fromIntegral (bytes tinyMaxLineLength - 100)
282− let upperBound = fromIntegral (bytes tinyMaxLineLength + 100)
283− bigMessage <- forAll (genLogStr' (Range.linear lowerBound upperBound))
284− item <- (\item -> item { _itemMessage = bigMessage}) <$> forAllWith showSimplePayloadItem genSimplePayloadItem
285− verbosity <- forAll genVerbosity
286− let (_, byteCount) = renderLineTruncated' tinyMaxLineLength verbosity item
287− HH.assert (byteCount <= reducedMaxLogLineLength)
288− ]
289−
290−
291−-------------------------------------------------------------------------------
292−builderLength :: BB.Builder -> Int64
293−builderLength = LBS.length . BB.toLazyByteString
294−
295−-------------------------------------------------------------------------------
296−genVerbosity :: Gen Verbosity
297−genVerbosity = Gen.enumBounded
298−
299−
300−-------------------------------------------------------------------------------
301−genBulkBuffer :: Gen BulkBuffer
302−genBulkBuffer = do
303− bytesUsed <- genBytes
304− -- generating a large bytestring string with hedghog seems quite slow, instead we'll generate a smaller string and replicate and merge it
305− payloadMultiplier <- Gen.int (Range.linear 1 100)
306− payloadSegment <- genBuilder (Range.linear 0 (round (toRational (bytes reducedMaxPayloadBytes) * 0.05)))
307− let payload = mconcat (replicate payloadMultiplier payloadSegment)
308− itemCount <- Gen.int (Range.linear 0 maxBound)
309− pure $ BulkBuffer
310− { bulkBuffer_bytesUsed = bytesUsed
311− , bulkBuffer_payload = payload
312− , bulkBuffer_itemCount = itemCount
313− }
314−
315−
316−-------------------------------------------------------------------------------
317−genBytes :: Gen Bytes
318−genBytes = Bytes <$> Gen.int64 (Range.linear 0 maxBound)
319−
320−
321−-------------------------------------------------------------------------------
322−genBuilder :: Range Int -> Gen BB.Builder
323−genBuilder size = BB.byteString <$> Gen.bytes size
324−
325−
326−-------------------------------------------------------------------------------
327−genSimplePayloadItem :: Gen (Item SimpleLogPayload)
328−genSimplePayloadItem = do
329− app <- genNamespace
330− env <- genEnvironment
331− severity <- genSeverity
332− thread <- genThreadIdText
333− host <- genHostName
334− process <- genProcessID
335− payload <- genSimpleLogPayload
336− message <- genLogStr
337− time <- genUTCTime
338− namespace <- genNamespace
339− loc <- Gen.maybe genLoc
340− pure $ Item
341− { _itemApp = app
342− , _itemEnv = env
343− , _itemSeverity = severity
344− , _itemThread = thread
345− , _itemHost = host
346− , _itemProcess = process
347− , _itemPayload = payload
348− , _itemMessage = message
349− , _itemTime = time
350− , _itemNamespace = namespace
351− , _itemLoc = loc
352− }
353−
354−
355−genNamespace :: Gen Namespace
356−genNamespace = Namespace <$> Gen.list (Range.linear 0 20) genSmallText
357−
358−
359−genEnvironment :: Gen Environment
360−genEnvironment = Environment <$> genSmallText
361−
362−
363−genSeverity :: Gen Severity
364−genSeverity = Gen.enumBounded
365−
366−
367−genThreadIdText :: Gen ThreadIdText
368−genThreadIdText = ThreadIdText <$> genSmallText
369−
370−
371−genHostName :: Gen HostName
372−genHostName = genSmallString
373−
374−
375−genProcessID :: Gen ProcessID
376−genProcessID = Gen.enumBounded
377−
378−
379−genLogStr :: Gen LogStr
380−genLogStr = genLogStr' (Range.linear 0 1024)
381−
382−
383−genLogStr' :: Range Int -> Gen LogStr
384−genLogStr' sizeRange = ls <$> Gen.text sizeRange Gen.unicode
385−
386−
387−genUTCTime :: Gen UTCTime
388−genUTCTime = posixSecondsToUTCTime . fromIntegral
389− <$> Gen.int (Range.linear 0 maxBound)
390−
391−
392−genLoc :: Gen Loc
393−genLoc = Loc
394− <$> genSmallString
395− <*> genSmallString
396− <*> genSmallString
397− <*> genCharPos
398− <*> genCharPos
399− where
400− genCharPos = (,)
401− <$> Gen.int (Range.linear 0 maxBound)
402− <*> Gen.int (Range.linear 0 maxBound)
403−
404−
405−genSimpleLogPayload :: Gen SimpleLogPayload
406−genSimpleLogPayload = SimpleLogPayload
407− <$> Gen.list (Range.linear 0 20) genPair
408− where
409− genPair = (,)
410− <$> genSmallText
411− <*> genAnyLogPayload
412−
413−
414−genAnyLogPayload :: Gen AnyLogPayload
415−genAnyLogPayload = AnyLogPayload
416− <$> genValue
417−
418−
419−genValue :: Gen Value
420−genValue = Gen.recursive Gen.choice nonRecursiveGens recursiveGens
421− where
422− nonRecursiveGens =
423− [ pure A.Null
424− , A.String <$> genSmallText
425− , A.Number <$> genScientific
426− , A.Bool <$> Gen.bool
427− ]
428− recursiveGens =
429− [ A.Object <$> genObject
430− , A.Array <$> genArray
431− ]
432− genObject = HM.fromList <$> Gen.list (Range.linear 0 20) genPair
433− where
434− genPair = (,) <$> genSmallText <*> genValue
435− genArray = V.fromList <$>
436− Gen.list (Range.linear 0 20) genValue
437−
438−
439−
440−genScientific :: Gen Scientific
441−genScientific = scientific
442− <$> Gen.integral (Range.linear (-999999) 999999)
443− <*> Gen.int (Range.linear (-999) (999))
444−
445−
446−genSmallText :: Gen Text
447−genSmallText = Gen.text (Range.linear 0 128) Gen.unicode
448−
449−
450−genSmallString :: Gen String
451−genSmallString = Gen.string (Range.linear 0 128) Gen.unicode
452−
453−
454−-------------------------------------------------------------------------------
455−mkUTCTime :: Integer -> Int -> Int -> DiffTime -> DiffTime -> DiffTime -> UTCTime
456−mkUTCTime y m d hr mn s = UTCTime day dt
457− where
458− day = mkDay y m d
459− dt = s + 60 * mn + 60 * 60 * hr
460−
461−
462−-------------------------------------------------------------------------------
463−mkDay :: Integer -> Int -> Int -> Day
464−mkDay y m d = day
465− where
466− Just day = fromGregorianValid y m d
katip-logzio/test/Main.hsdeleted−22
@@ -1,22 +0,0 @@
1−module Main
2− ( main
3− ) where
4−
5−
6−-------------------------------------------------------------------------------
7−import Test.Tasty
8−-------------------------------------------------------------------------------
9−import qualified Katip.Tests.Scribes.LogzIO.HTTPS
10−-------------------------------------------------------------------------------
11−
12−
13−
14−main :: IO ()
15−main = defaultMain tests
16−
17−
18−-------------------------------------------------------------------------------
19−tests :: TestTree
20−tests = testGroup "katip-logzio"
21− [ Katip.Tests.Scribes.LogzIO.HTTPS.tests
22− ]
katip/README.md+1−1
@@ -16,7 +16,7 @@ Kâtip (pronounced kah-tip) is the Turkish word for scribe.
16 add context that will be automatically merged in with existing log
16 add context that will be automatically merged in with existing log
17 context.
17 context.
18
18
19−* *Easy Integration:* Katip was designed to be easily integrated
19+* *Easy to Integration:* Katip was designed to be easily integrated
20 into existing monads. By using typeclasses for logging facilities,
20 into existing monads. By using typeclasses for logging facilities,
21 individual subsystems and even libraries can easily add their own
21 individual subsystems and even libraries can easily add their own
22 namespacing and context without having any knowledge of their
22 namespacing and context without having any knowledge of their
katip/changelog.md−37
@@ -1,40 +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−
14−0.8.2.0
15−=======
16−* Add `MonadFail` instances for `base` >= 4.9.0.0 [Koray Al](https://github.com/korayal)
17−
18−0.8.1.0
19−=======
20−* Export `logLoc`. Credit to [Brian McKenna](https://github.com/puffnfresh)
21−
22−0.8.0.0
23−=======
24−* **Breaking change**: switch from severity based log level filtering to
25− user-definable `PermitFunc`. To upgrade, any time you initialize one
26− of the standard scribes, rather than providing a severity like
27− `InfoS`, instead pass in `(permitItem InfoS)`. Credit to
28− [vlatkoB](https://github.com/vlatkoB).
29−* Update some misformatted and outdated docs.
30−* Set lower bound of base to 4.9.0.0, the version shipped with GHC
31− 8.0.2. Support for GHC versions older than that is removed.
32−* Update default formatter of Handle scribe to format pid as `[PID
33− 1234]` instead of `[1234]` and `[ThreadId 1234]` instead of
34− `[1234]`.
35−* Add missing `Bounded` instance for `Verbosity`. This allows you to
36− easily enumerate all verbosities via `[minBound..maxBound]`.
37−
380.7.0.0
10.7.0.0
39=======
2=======
40* Exclusively use HasCallstack for location reporting in errors. Credit to [Domen Kožar](https://github.com/domenkozar)
3* Exclusively use HasCallstack for location reporting in errors. Credit to [Domen Kožar](https://github.com/domenkozar)
katip/katip.cabal+22−22
@@ -1,5 +1,5 @@
1name: katip
1name: katip
2−version: 0.8.5.0
2+version: 0.8.0.0
3synopsis: A structured logging framework.
3synopsis: A structured logging framework.
4description:
4description:
5 Katip is a structured logging framework. See README.md for more details.
5 Katip is a structured logging framework. See README.md for more details.
@@ -56,29 +56,29 @@ library
56 build-depends: base >=4.9.0.0 && <5
56 build-depends: base >=4.9.0.0 && <5
57 , aeson >=0.6
57 , aeson >=0.6
58 , async < 3.0.0.0
58 , async < 3.0.0.0
59− , auto-update >= 0.1
59+ , auto-update >= 0.1 && < 0.2
60− , bytestring >= 0.9
60+ , bytestring >= 0.9 && < 0.11
61− , containers >=0.4
61+ , containers >=0.4 && <0.7
62− , either >= 4
62+ , either >= 4 && < 5.1
63 , safe-exceptions >= 0.1.0.0
63 , safe-exceptions >= 0.1.0.0
64− , hostname >=1.0
64+ , hostname >=1.0 && <1.1
65− , old-locale >= 1.0
65+ , old-locale >= 1.0 && < 1.1
66− , string-conv >= 0.1
66+ , string-conv >= 0.1 && < 0.2
67− , template-haskell >= 2.8
67+ , template-haskell >= 2.8 && < 2.15
68− , text >= 0.11
68+ , text >= 0.11 && <1.3
69− , time >= 1
69+ , time >= 1 && < 1.9
70− , transformers >= 0.3
70+ , transformers >= 0.3 && < 0.6
71 , transformers-compat
71 , transformers-compat
72− , unordered-containers >= 0.2
72+ , unordered-containers >= 0.2 && < 0.3
73− , monad-control >= 1.0
73+ , monad-control >= 1.0 && < 1.1
74− , mtl >= 2.0
74+ , mtl >= 2.0 && < 2.3
75− , transformers-base >= 0.3
75+ , transformers-base >= 0.3 && < 0.6
76− , resourcet >= 1.1
76+ , resourcet >= 1.1 && < 1.3
77 , scientific >= 0.3.3.0
77 , scientific >= 0.3.3.0
78− , microlens >= 0.2.0.0
78+ , microlens >= 0.2.0.0 && < 0.5
79− , microlens-th >= 0.1.0.0
79+ , microlens-th >= 0.1.0.0 && < 0.5
80 , semigroups
80 , semigroups
81− , unliftio-core >= 0.1
81+ , unliftio-core >= 0.1 && < 0.2
82 , stm >= 2.4
82 , stm >= 2.4
83
83
84 hs-source-dirs: src
84 hs-source-dirs: src
@@ -87,7 +87,7 @@ library
87 if flag(lib-Werror)
87 if flag(lib-Werror)
88 ghc-options: -Werror
88 ghc-options: -Werror
89 if os(windows)
89 if os(windows)
90− build-depends: Win32 >=2.3 && <2.9
90+ build-depends: Win32 >=2.3 && <2.6
91 exposed-modules: Katip.Compat
91 exposed-modules: Katip.Compat
92 else
92 else
93 build-depends: unix >= 2.5 && <2.8
93 build-depends: unix >= 2.5 && <2.8
@@ -114,7 +114,7 @@ test-suite test
114 , template-haskell
114 , template-haskell
115 , text
115 , text
116 , time
116 , time
117− , time-locale-compat >= 0.1.0.1
117+ , time-locale-compat >= 0.1.0.1 && < 0.2
118 , directory
118 , directory
119 , regex-tdfa
119 , regex-tdfa
120 , unordered-containers
120 , unordered-containers
katip/src/Katip.hs+1−3
@@ -44,7 +44,7 @@
44--
44--
45-- newtype App m a = App {
45-- newtype App m a = App {
46-- unApp :: ReaderT Config m a
46-- unApp :: ReaderT Config m a
47−-- } deriving (Functor, Applicative, Monad, MonadIO, MonadReader Config) -- these are necessary
47+-- } deriving (Functor, Applicative, Monad) -- more instances as needed
48--
48--
49--
49--
50-- -- These instances get even easier with lenses!
50-- -- These instances get even easier with lenses!
@@ -159,9 +159,7 @@ module Katip
159 , logF
159 , logF
160 , logMsg
160 , logMsg
161 , logT
161 , logT
162− , logLoc
163 , logItem
162 , logItem
164− , logKatipItem
165 , logException
163 , logException
166 -- ** 'KatipContext': Logging With Context
164 -- ** 'KatipContext': Logging With Context
167 -- $katipcontextlogging
165 -- $katipcontextlogging
katip/src/Katip/Core.hs+46−76
@@ -22,18 +22,15 @@
22module Katip.Core where
22module Katip.Core where
23
23
24-------------------------------------------------------------------------------
24-------------------------------------------------------------------------------
25−import Control.Applicative as A
25+import Control.Applicative as A
26import Control.AutoUpdate
26import Control.AutoUpdate
27import Control.Concurrent
27import Control.Concurrent
28−import qualified Control.Concurrent.Async as Async
28+import qualified Control.Concurrent.Async as Async
29import Control.Concurrent.STM
29import Control.Concurrent.STM
30−import qualified Control.Concurrent.STM.TBQueue as BQ
30+import qualified Control.Concurrent.STM.TBQueue as BQ
31import Control.Exception.Safe
31import Control.Exception.Safe
32−import Control.Monad (unless, void, when)
32+import Control.Monad (unless, void, when)
33import Control.Monad.Base
33import Control.Monad.Base
34−#if MIN_VERSION_base(4, 9, 0)
35−import qualified Control.Monad.Fail as MF
36−#endif
37import Control.Monad.IO.Class
34import Control.Monad.IO.Class
38import Control.Monad.IO.Unlift
35import Control.Monad.IO.Unlift
39import Control.Monad.Trans.Class
36import Control.Monad.Trans.Class
@@ -44,31 +41,28 @@ import Control.Monad.Trans.Either
44import Control.Monad.Trans.Except
41import Control.Monad.Trans.Except
45import Control.Monad.Trans.Maybe
42import Control.Monad.Trans.Maybe
46import Control.Monad.Trans.Reader
43import Control.Monad.Trans.Reader
47−import Control.Monad.Trans.Resource (MonadResource, ResourceT,
44+import Control.Monad.Trans.Resource (ResourceT, transResourceT)
48− transResourceT)
49−import Control.Monad.Trans.RWS.Lazy (RWST, mapRWST)
45+import Control.Monad.Trans.State.Lazy (StateT, mapStateT)
50−import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST, mapRWST)
46+import qualified Control.Monad.Trans.State.Strict as Strict (StateT, mapStateT)
51−import Control.Monad.Trans.State.Lazy (StateT, mapStateT)
47+import Control.Monad.Trans.Writer.Lazy (WriterT, mapWriterT)
52−import qualified Control.Monad.Trans.State.Strict as Strict (StateT, mapStateT)
48+import qualified Control.Monad.Trans.Writer.Strict as Strict (WriterT, mapWriterT)
53−import Control.Monad.Trans.Writer.Lazy (WriterT, mapWriterT)
49+import Control.Monad.Trans.RWS.Lazy (RWST, mapRWST)
54−import qualified Control.Monad.Trans.Writer.Strict as Strict (WriterT,
50+import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST, mapRWST)
55− mapWriterT)
56−import Data.Aeson (FromJSON (..), ToJSON (..),
51+import Data.Aeson (FromJSON (..), ToJSON (..),
57− object)
52+ object)
58−import qualified Data.Aeson as A
53+import qualified Data.Aeson as A
59−import Data.Foldable as FT
54+import Data.Foldable as FT
60−import qualified Data.HashMap.Strict as HM
55+import qualified Data.HashMap.Strict as HM
61import Data.List
56import Data.List
62−import qualified Data.Map.Strict as M
57+import qualified Data.Map.Strict as M
63−import Data.Maybe (fromMaybe)
64−import Data.Semigroup as SG
58+import Data.Semigroup as SG
65import Data.String
59import Data.String
66import Data.String.Conv
60import Data.String.Conv
67−import Data.Text (Text)
61+import Data.Text (Text)
68−import qualified Data.Text as T
62+import qualified Data.Text as T
69−import qualified Data.Text.Lazy.Builder as B
63+import qualified Data.Text.Lazy.Builder as B
70import Data.Time
64import Data.Time
71−import GHC.Generics hiding (to)
65+import GHC.Generics hiding (to)
72#if MIN_VERSION_base(4, 8, 0)
66#if MIN_VERSION_base(4, 8, 0)
73#if !MIN_VERSION_base(4, 9, 0)
67#if !MIN_VERSION_base(4, 9, 0)
74import GHC.SrcLoc
68import GHC.SrcLoc
@@ -76,7 +70,7 @@ import GHC.SrcLoc
76import GHC.Stack
70import GHC.Stack
77#endif
71#endif
78import Language.Haskell.TH
72import Language.Haskell.TH
79−import qualified Language.Haskell.TH.Syntax as TH
73+import qualified Language.Haskell.TH.Syntax as TH
80import Lens.Micro
74import Lens.Micro
81import Lens.Micro.TH
75import Lens.Micro.TH
82import Network.HostName
76import Network.HostName
@@ -143,7 +137,7 @@ data Severity
143-- - 'V3' implies the maximum amount of payload information.
137-- - 'V3' implies the maximum amount of payload information.
144-- - Anything in between is left to the discretion of the developer.
138-- - Anything in between is left to the discretion of the developer.
145data Verbosity = V0 | V1 | V2 | V3
139data Verbosity = V0 | V1 | V2 | V3
146− deriving (Eq, Ord, Show, Read, Generic, Enum, Bounded)
140+ deriving (Eq, Ord, Show, Read, Generic, Enum)
147
141
148
142
149-------------------------------------------------------------------------------
143-------------------------------------------------------------------------------
@@ -250,9 +244,7 @@ newtype ThreadIdText = ThreadIdText {
250
244
251
245
252mkThreadIdText :: ThreadId -> ThreadIdText
246mkThreadIdText :: ThreadId -> ThreadIdText
253−mkThreadIdText = ThreadIdText . stripPrefix' "ThreadId " . T.pack . show
247+mkThreadIdText = ThreadIdText . T.pack . show
254− where
255− stripPrefix' pfx t = fromMaybe t (T.stripPrefix pfx t)
256
248
257
249
258-------------------------------------------------------------------------------
250-------------------------------------------------------------------------------
@@ -586,9 +578,9 @@ permitOR f1 f2 = \a -> liftA2 (||) (f1 a) (f2 a)
586
578
587
579
588data Scribe = Scribe {
580data Scribe = Scribe {
589− liPush :: forall a. LogItem a => Item a -> IO ()
581+ liPush :: forall a. LogItem a => Item a -> IO ()
590 -- ^ How do we write an item to the scribe's output?
582 -- ^ How do we write an item to the scribe's output?
591− , scribeFinalizer :: IO ()
583+ , scribeFinalizer :: IO ()
592 -- ^ Provide a __blocking__ finalizer to call when your scribe is
584 -- ^ Provide a __blocking__ finalizer to call when your scribe is
593 -- removed. All pending writes should be flushed synchronously. If
585 -- removed. All pending writes should be flushed synchronously. If
594 -- this is not relevant to your scribe, return () is fine.
586 -- this is not relevant to your scribe, return () is fine.
@@ -626,7 +618,7 @@ instance Monoid Scribe where
626-------------------------------------------------------------------------------
618-------------------------------------------------------------------------------
627data ScribeHandle = ScribeHandle {
619data ScribeHandle = ScribeHandle {
628 shScribe :: Scribe
620 shScribe :: Scribe
629− , shChan :: BQ.TBQueue WorkerMessage
621+ , shChan :: BQ.TBQueue WorkerMessage
630 }
622 }
631
623
632
624
@@ -665,7 +657,7 @@ makeLenses ''LogEnv
665
657
666
658
667-------------------------------------------------------------------------------
659-------------------------------------------------------------------------------
668−-- | Create a reasonable default InitLogEnv. Uses an 'AutoUpdate' which
660+-- | Create a reasonable default InitLogEnv. Uses an 'AutoUdate' which
669-- updates the timer every 1ms. If you need even more timestamp
661-- updates the timer every 1ms. If you need even more timestamp
670-- precision at the cost of performance, consider setting
662-- precision at the cost of performance, consider setting
671-- '_logEnvTimer' with 'getCurrentTime'.
663-- '_logEnvTimer' with 'getCurrentTime'.
@@ -880,8 +872,7 @@ instance (Katip m) => Katip (ResourceT m) where
880-- opposed to implementing 'Katip' for your monad.
872-- opposed to implementing 'Katip' for your monad.
881newtype KatipT m a = KatipT { unKatipT :: ReaderT LogEnv m a }
873newtype KatipT m a = KatipT { unKatipT :: ReaderT LogEnv m a }
882 deriving ( Functor, Applicative, Monad, MonadIO
874 deriving ( Functor, Applicative, Monad, MonadIO
883− , MonadMask, MonadCatch, MonadThrow, MonadResource, MonadTrans
875+ , MonadMask, MonadCatch, MonadThrow, MonadTrans, MonadBase b)
884− , MonadBase b)
885
876
886
877
887instance MonadIO m => Katip (KatipT m) where
878instance MonadIO m => Katip (KatipT m) where
@@ -903,20 +894,9 @@ instance (MonadBaseControl b m) => MonadBaseControl b (KatipT m) where
903 restoreM = defaultRestoreM
894 restoreM = defaultRestoreM
904
895
905instance MonadUnliftIO m => MonadUnliftIO (KatipT m) where
896instance 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
910 askUnliftIO = KatipT $
897 askUnliftIO = KatipT $
911− withUnliftIO $ \u ->
898+ withUnliftIO $ \u ->
912− pure (UnliftIO (unliftIO u . unKatipT))
899+ pure (UnliftIO (unliftIO u . unKatipT))
913−#endif
914−
915−#if MIN_VERSION_base(4, 9, 0)
916−instance MF.MonadFail m => MF.MonadFail (KatipT m) where
917− fail msg = lift (MF.fail msg)
918− {-# INLINE fail #-}
919−#endif
920
900
921-------------------------------------------------------------------------------
901-------------------------------------------------------------------------------
922-- | Execute 'KatipT' on a log env.
902-- | Execute 'KatipT' on a log env.
@@ -948,29 +928,19 @@ logItem
948 -> m ()
928 -> m ()
949logItem a ns loc sev msg = do
929logItem a ns loc sev msg = do
950 LogEnv{..} <- getLogEnv
930 LogEnv{..} <- getLogEnv
951− logKatipItem =<< liftIO
931+ liftIO $ do
932+ item <- Item
952− (Item <$> pure _logEnvApp
933+ <$> pure _logEnvApp
953− <*> pure _logEnvEnv
934+ <*> pure _logEnvEnv
954− <*> pure sev
935+ <*> pure sev
955− <*> (mkThreadIdText <$> myThreadId)
936+ <*> (mkThreadIdText <$> myThreadId)
956− <*> pure _logEnvHost
937+ <*> pure _logEnvHost
957− <*> pure _logEnvPid
938+ <*> pure _logEnvPid
958− <*> pure a
939+ <*> pure a
959− <*> pure msg
940+ <*> pure msg
960− <*> _logEnvTimer
941+ <*> _logEnvTimer
961− <*> pure (_logEnvApp <> ns)
942+ <*> pure (_logEnvApp <> ns)
962− <*> pure loc)
943+ <*> 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 $
974 FT.forM_ (M.elems _logEnvScribes) $ \ ScribeHandle {..} -> do
944 FT.forM_ (M.elems _logEnvScribes) $ \ ScribeHandle {..} -> do
975 whenM (scribePermitItem shScribe item) $
945 whenM (scribePermitItem shScribe item) $
976 void $ atomically (tryWriteTBQueue shChan (NewItem item))
946 void $ atomically (tryWriteTBQueue shChan (NewItem item))
@@ -1019,7 +989,7 @@ logException
1019logException a ns sev action = action `catchAny` \e -> f e >> throwM e
989logException a ns sev action = action `catchAny` \e -> f e >> throwM e
1020 where
990 where
1021 f e = logF a ns sev (msg e)
991 f e = logF a ns sev (msg e)
1022− msg e = ls (T.pack "An exception has occurred: ") <> showLS e
992+ msg e = ls (T.pack "An exception has occured: ") <> showLS e
1023
993
1024
994
1025-------------------------------------------------------------------------------
995-------------------------------------------------------------------------------
katip/src/Katip/Monadic.hs+6−28
@@ -44,9 +44,6 @@ import Control.Applicative
44import Control.Exception.Safe
44import Control.Exception.Safe
45import Control.Monad.Base
45import Control.Monad.Base
46import Control.Monad.Error.Class
46import Control.Monad.Error.Class
47−#if MIN_VERSION_base(4, 9, 0)
48−import qualified Control.Monad.Fail as MF
49−#endif
50import Control.Monad.IO.Class
47import Control.Monad.IO.Class
51import Control.Monad.IO.Unlift
48import Control.Monad.IO.Unlift
52import Control.Monad.Reader
49import Control.Monad.Reader
@@ -58,8 +55,7 @@ import Control.Monad.Trans.Either (EitherT, mapEitherT)
58import Control.Monad.Trans.Except (ExceptT, mapExceptT)
55import Control.Monad.Trans.Except (ExceptT, mapExceptT)
59import Control.Monad.Trans.Identity (IdentityT, mapIdentityT)
56import Control.Monad.Trans.Identity (IdentityT, mapIdentityT)
60import Control.Monad.Trans.Maybe (MaybeT, mapMaybeT)
57import Control.Monad.Trans.Maybe (MaybeT, mapMaybeT)
61−import Control.Monad.Trans.Resource (MonadResource,
58+import Control.Monad.Trans.Resource (ResourceT, transResourceT)
62− ResourceT, transResourceT)
63import Control.Monad.Trans.RWS (RWST, mapRWST)
59import Control.Monad.Trans.RWS (RWST, mapRWST)
64import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST, mapRWST)
60import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST, mapRWST)
65import qualified Control.Monad.Trans.State.Strict as Strict (StateT, mapStateT)
61import qualified Control.Monad.Trans.State.Strict as Strict (StateT, mapStateT)
@@ -316,7 +312,7 @@ logExceptionM
316logExceptionM action sev = action `catchAny` \e -> f e >> throwM e
312logExceptionM action sev = action `catchAny` \e -> f e >> throwM e
317 where
313 where
318 f e = logFM sev (msg e)
314 f e = logFM sev (msg e)
319− msg e = ls ("An exception has occurred: " :: Text) Semi.<> showLS e
315+ msg e = ls ("An exception has occured: " :: Text) Semi.<> showLS e
320
316
321
317
322-------------------------------------------------------------------------------
318-------------------------------------------------------------------------------
@@ -350,7 +346,6 @@ newtype KatipContextT m a = KatipContextT {
350 , MonadWriter w
346 , MonadWriter w
351 , MonadError e
347 , MonadError e
352 , MonadPlus
348 , MonadPlus
353− , MonadResource
354 , Alternative
349 , Alternative
355 , MonadFix
350 , MonadFix
356 , MonadTrans
351 , MonadTrans
@@ -397,21 +392,10 @@ instance (MonadIO m) => KatipContext (KatipContextT m) where
397 localKatipNamespace f (KatipContextT m) = KatipContextT $ local (\s -> s { ltsNamespace = f (ltsNamespace s)}) m
392 localKatipNamespace f (KatipContextT m) = KatipContextT $ local (\s -> s { ltsNamespace = f (ltsNamespace s)}) m
398
393
399instance MonadUnliftIO m => MonadUnliftIO (KatipContextT m) where
394instance 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
404 askUnliftIO = KatipContextT $
395 askUnliftIO = KatipContextT $
405− withUnliftIO $ \u ->
396+ withUnliftIO $ \u ->
406− pure (UnliftIO (unliftIO u . unKatipContextT))
397+ pure (UnliftIO (unliftIO u . unKatipContextT))
407−#endif
408−
409
398
410−#if MIN_VERSION_base(4, 9, 0)
411−instance MF.MonadFail m => MF.MonadFail (KatipContextT m) where
412− fail msg = lift (MF.fail msg)
413− {-# INLINE fail #-}
414−#endif
415
399
416-------------------------------------------------------------------------------
400-------------------------------------------------------------------------------
417runKatipContextT :: (LogItem c) => LogEnv -> c -> Namespace -> KatipContextT m a -> m a
401runKatipContextT :: (LogItem c) => LogEnv -> c -> Namespace -> KatipContextT m a -> m a
@@ -490,15 +474,9 @@ instance MonadBaseControl b m => MonadBaseControl b (NoLoggingT m) where
490 restoreM = NoLoggingT . restoreM
474 restoreM = NoLoggingT . restoreM
491
475
492instance MonadUnliftIO m => MonadUnliftIO (NoLoggingT m) where
476instance 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
497 askUnliftIO = NoLoggingT $
477 askUnliftIO = NoLoggingT $
498− withUnliftIO $ \u ->
478+ withUnliftIO $ \u ->
499− pure (UnliftIO (unliftIO u . runNoLoggingT))
479+ pure (UnliftIO (unliftIO u . runNoLoggingT))
500−#endif
501−
502
480
503instance MonadIO m => Katip (NoLoggingT m) where
481instance MonadIO m => Katip (NoLoggingT m) where
504 getLogEnv = liftIO (initLogEnv "NoLoggingT" "no-logging")
482 getLogEnv = liftIO (initLogEnv "NoLoggingT" "no-logging")
katip/src/Katip/Scribes/Handle.hs+8−8
@@ -56,9 +56,9 @@ data ColorStrategy
56-- and other information will be flattened out into bracketed
56-- and other information will be flattened out into bracketed
57-- fields. For example:
57-- fields. For example:
58--
58--
59−-- > [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][PID 1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:32:7] Started
59+-- > [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:32:7] Started
60−-- > [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
60+-- > [2016-05-11 21:01:15][MyApp.confrabulation][Debug][myhost.example.com][1724][ThreadId 1154][confrab_factor:42.0][main:Helpers.Logging Helpers/Logging.hs:41:9] Confrabulating widgets, with extra namespace and context
61−-- > [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
61+-- > [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:43:7] Namespace and context are back to normal
62--
62--
63-- Returns the newly-created `Scribe`. The finalizer flushes the
63-- Returns the newly-created `Scribe`. The finalizer flushes the
64-- handle. Handle mode is set to 'LineBuffering' automatically.
64-- handle. Handle mode is set to 'LineBuffering' automatically.
@@ -117,17 +117,17 @@ formatItem = bracketFormat
117-- | A traditional 'bracketed' log format. Contexts and other information will
117-- | A traditional 'bracketed' log format. Contexts and other information will
118-- be flattened out into bracketed fields. For example:
118-- be flattened out into bracketed fields. For example:
119--
119--
120−-- > [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][PID 1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:32:7] Started
120+-- > [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:32:7] Started
121−-- > [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
121+-- > [2016-05-11 21:01:15][MyApp.confrabulation][Debug][myhost.example.com][1724][ThreadId 1154][confrab_factor:42.0][main:Helpers.Logging Helpers/Logging.hs:41:9] Confrabulating widgets, with extra namespace and context
122−-- > [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
122+-- > [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:43:7] Namespace and context are back to normal
123bracketFormat :: LogItem a => ItemFormatter a
123bracketFormat :: LogItem a => ItemFormatter a
124bracketFormat withColor verb Item{..} =
124bracketFormat withColor verb Item{..} =
125 brackets nowStr <>
125 brackets nowStr <>
126 brackets (mconcat $ map fromText $ intercalateNs _itemNamespace) <>
126 brackets (mconcat $ map fromText $ intercalateNs _itemNamespace) <>
127 brackets (fromText (renderSeverity' _itemSeverity)) <>
127 brackets (fromText (renderSeverity' _itemSeverity)) <>
128 brackets (fromString _itemHost) <>
128 brackets (fromString _itemHost) <>
129− brackets ("PID " <> fromString (show _itemProcess)) <>
129+ brackets (fromString (show _itemProcess)) <>
130− brackets ("ThreadId " <> fromText (getThreadIdText _itemThread)) <>
130+ brackets (fromText (getThreadIdText _itemThread)) <>
131 mconcat ks <>
131 mconcat ks <>
132 maybe mempty (brackets . fromString . locationToString) _itemLoc <>
132 maybe mempty (brackets . fromString . locationToString) _itemLoc <>
133 fromText " " <> (unLogStr _itemMessage)
133 fromText " " <> (unLogStr _itemMessage)
katip/test/Katip/Tests/Scribes/Handle-text.golden+38−38
@@ -1,40 +1,40 @@
1−[2016-06-12 12:34:56][foo][Debug][example][PID 7331][ThreadId 1337][note.deep:some note] message
1+[2016-06-12 12:34:56][foo][Debug][example][7331][1337][note.deep:some note] message
2−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
2+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] message
3−[2016-06-12 12:34:56][foo][Notice][example][PID 7331][ThreadId 1337][note.deep:some note] message
3+[2016-06-12 12:34:56][foo][Notice][example][7331][1337][note.deep:some note] message
4−[2016-06-12 12:34:56][foo][Warning][example][PID 7331][ThreadId 1337][note.deep:some note] message
4+[2016-06-12 12:34:56][foo][Warning][example][7331][1337][note.deep:some note] message
5−[2016-06-12 12:34:56][foo][Error][example][PID 7331][ThreadId 1337][note.deep:some note] message
5+[2016-06-12 12:34:56][foo][Error][example][7331][1337][note.deep:some note] message
6−[2016-06-12 12:34:56][foo][Critical][example][PID 7331][ThreadId 1337][note.deep:some note] message
6+[2016-06-12 12:34:56][foo][Critical][example][7331][1337][note.deep:some note] message
7−[2016-06-12 12:34:56][foo][Alert][example][PID 7331][ThreadId 1337][note.deep:some note] message
7+[2016-06-12 12:34:56][foo][Alert][example][7331][1337][note.deep:some note] message
8−[2016-06-12 12:34:56][foo][Emergency][example][PID 7331][ThreadId 1337][note.deep:some note] message
8+[2016-06-12 12:34:56][foo][Emergency][example][7331][1337][note.deep:some note] message
9−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 0][note.deep:some note] message
9+[2016-06-12 12:34:56][foo][Info][example][7331][0][note.deep:some note] message
10−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1][note.deep:some note] message
10+[2016-06-12 12:34:56][foo][Info][example][7331][1][note.deep:some note] message
11−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
11+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] message
12−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 2147483647][note.deep:some note] message
12+[2016-06-12 12:34:56][foo][Info][example][7331][2147483647][note.deep:some note] message
13−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
13+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] message
14−[2016-06-12 12:34:56][foo][Info][www.example.com][PID 7331][ThreadId 1337][note.deep:some note] message
14+[2016-06-12 12:34:56][foo][Info][www.example.com][7331][1337][note.deep:some note] message
15−[2016-06-12 12:34:56][foo][Info][127.0.0.1][PID 7331][ThreadId 1337][note.deep:some note] message
15+[2016-06-12 12:34:56][foo][Info][127.0.0.1][7331][1337][note.deep:some note] message
16−[2016-06-12 12:34:56][foo][Info][example][PID 0][ThreadId 1337][note.deep:some note] message
16+[2016-06-12 12:34:56][foo][Info][example][0][1337][note.deep:some note] message
17−[2016-06-12 12:34:56][foo][Info][example][PID 1][ThreadId 1337][note.deep:some note] message
17+[2016-06-12 12:34:56][foo][Info][example][1][1337][note.deep:some note] message
18−[2016-06-12 12:34:56][foo][Info][example][PID 1337][ThreadId 1337][note.deep:some note] message
18+[2016-06-12 12:34:56][foo][Info][example][1337][1337][note.deep:some note] message
19−[2016-06-12 12:34:56][foo][Info][example][PID 2147483647][ThreadId 1337][note.deep:some note] message
19+[2016-06-12 12:34:56][foo][Info][example][2147483647][1337][note.deep:some note] message
20−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
20+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] message
21−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
21+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] message
22with newline
22with newline
23−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message
23+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message a really long message
24−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] сообщение
24+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] сообщение
25−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] 哈囉世界
25+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] 哈囉世界
26−[2000-01-01 00:00:00][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
26+[2000-01-01 00:00:00][foo][Info][example][7331][1337][note.deep:some note] message
27−[2123-12-31 23:59:59][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
27+[2123-12-31 23:59:59][foo][Info][example][7331][1337][note.deep:some note] message
28−[2016-10-10 01:01:05][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
28+[2016-10-10 01:01:05][foo][Info][example][7331][1337][note.deep:some note] message
29−[2100-12-31 12:59:10][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
29+[2100-12-31 12:59:10][foo][Info][example][7331][1337][note.deep:some note] message
30−[1982-01-01 12:30:00][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
30+[1982-01-01 12:30:00][foo][Info][example][7331][1337][note.deep:some note] message
31−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
31+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] message
32−[2016-06-12 12:34:56][foo.bar][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
32+[2016-06-12 12:34:56][foo.bar][Info][example][7331][1337][note.deep:some note] message
33−[2016-06-12 12:34:56][фу.бар][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
33+[2016-06-12 12:34:56][фу.бар][Info][example][7331][1337][note.deep:some note] message
34[2016-06-12 12:34:56][with
34[2016-06-12 12:34:56][with
35−newline][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
35+newline][Info][example][7331][1337][note.deep:some note] message
36−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note] message
36+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note] message
37−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note][main:Some.Module path/Some/Module.hs:30:1] message
37+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note][main:Some.Module path/Some/Module.hs:30:1] message
38−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][note.deep:some note][main:Some.Module путь/Some/Module.hs:3000:9000] message
38+[2016-06-12 12:34:56][foo][Info][example][7331][1337][note.deep:some note][main:Some.Module путь/Some/Module.hs:3000:9000] message
39−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][sub:null][text:][num:0][float:0] message
39+[2016-06-12 12:34:56][foo][Info][example][7331][1337][sub:null][text:][num:0][float:0] message
40−[2016-06-12 12:34:56][foo][Info][example][PID 7331][ThreadId 1337][sub.note.deep:some note][text:note][num:10][float:5.5] message
40+[2016-06-12 12:34:56][foo][Info][example][7331][1337][sub.note.deep:some note][text:note][num:10][float:5.5] message
katip/test/Katip/Tests/Scribes/Handle.hs+2−2
@@ -39,7 +39,7 @@ tests = testGroup "Katip.Scribes.Handle"
39 runKatipT le $ logItem dummyLogItem "test" Nothing InfoS "wont make it in"
39 runKatipT le $ logItem dummyLogItem "test" Nothing InfoS "wont make it in"
40 hClose h
40 hClose h
41 res <- readFile path
41 res <- readFile path
42− let pat = "\\[[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\\]\\[katip-test.test\\]\\[Info\\]\\[.+\\]\\[[PID [:digit:]]+\\]\\[ThreadId [[:digit:]]+\\]\\[note.deep:some note\\] test message" :: String
42+ let pat = "\\[[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\\]\\[katip-test.test\\]\\[Info\\]\\[.+\\]\\[[[:digit:]]+\\]\\[ThreadId [[:digit:]]+\\]\\[note.deep:some note\\] test message" :: String
43 let matches = res =~ pat
43 let matches = res =~ pat
44 assertBool (show res M.<> " did not match") matches
44 assertBool (show res M.<> " did not match") matches
45 , withResource setupFile (const (return ())) $ \setupScribe -> testCase "logs correct data to a file" $ do
45 , withResource setupFile (const (return ())) $ \setupScribe -> testCase "logs correct data to a file" $ do
@@ -48,7 +48,7 @@ tests = testGroup "Katip.Scribes.Handle"
48 fin
48 fin
49 runKatipT le $ logItem dummyLogItem "test" Nothing InfoS "wont make it in"
49 runKatipT le $ logItem dummyLogItem "test" Nothing InfoS "wont make it in"
50 res <- readFile path
50 res <- readFile path
51− let pat = "\\[[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\\]\\[katip-test.test\\]\\[Info\\]\\[.+\\]\\[[PID [:digit:]]+\\]\\[ThreadId [[:digit:]]+\\]\\[note.deep:some note\\] test message" :: String
51+ let pat = "\\[[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\\]\\[katip-test.test\\]\\[Info\\]\\[.+\\]\\[[[:digit:]]+\\]\\[ThreadId [[:digit:]]+\\]\\[note.deep:some note\\] test message" :: String
52 let matches = res =~ pat
52 let matches = res =~ pat
53 assertBool (show res <> " did not match") matches
53 assertBool (show res <> " did not match") matches
54 , withResource setupTempFile teardownTempFile $ \setupFn ->
54 , withResource setupTempFile teardownTempFile $ \setupFn ->
stack.yaml+3−8
@@ -1,14 +1,11 @@
1−resolver: lts-16.18
1+resolver: lts-12.26
2packages:
2packages:
3 - katip
3 - katip
4 - katip-elasticsearch
4 - katip-elasticsearch
5 - katip-datadog
5 - katip-datadog
6− - katip-logzio
7extra-deps:
6extra-deps:
8− # ghc 8.8 support
9− - github: wireapp/bloodhound
7+ - bloodhound-0.16.0.0
10− commit: b409f52b518f20a4c20651b5893c31b7b85cb9b1
8+ - unliftio-core-0.1.2.0
11−allow-newer: true
12flags:
9flags:
13 katip:
10 katip:
14 lib-Werror: true
11 lib-Werror: true
@@ -16,5 +13,3 @@ flags:
16 lib-Werror: true
13 lib-Werror: true
17 katip-datadog:
14 katip-datadog:
18 lib-Werror: true
15 lib-Werror: true
19− katip-logzio:
20− lib-Werror: true