Skip to content
This repository was archived by the owner on Apr 1, 2025. It is now read-only.

Commit cf80355

Browse files
committed
Make error reporter API generic
Instead of hard-coding the HTTP API we use for uploading errors to our internal service, let the user provide an arbitrary function (in the IO monad) for uploading them. The default config will create an error reporter that logs errors to the console, just like happened before if you didn't set the `HAYSTACK_URL` environment variable.
1 parent b2bbda5 commit cf80355

File tree

12 files changed

+308
-534
lines changed

12 files changed

+308
-534
lines changed

script/generate-example

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,20 @@ generate_example () {
3737

3838
if [ -e "$fileA" ]; then
3939
status $parseFileA
40-
cabal new-run semantic -- parse --sexpression $fileA > $parseFileA
40+
cabal new-run --verbose=0 semantic -- parse --sexpression $fileA > $parseFileA
4141
fi
4242

4343
if [ -e "$fileB" ]; then
4444
status $parseFileB
45-
cabal new-run semantic -- parse --sexpression $fileB > $parseFileB
45+
cabal new-run --verbose=0 semantic -- parse --sexpression $fileB > $parseFileB
4646
fi
4747

4848
if [ -e "$fileA" -a -e "$fileB" ]; then
4949
status $diffFileAB
50-
cabal new-run semantic -- diff --sexpression $fileA $fileB > $diffFileAB
50+
cabal new-run --verbose=0 semantic -- diff --sexpression $fileA $fileB > $diffFileAB
5151

5252
status $diffFileBA
53-
cabal new-run semantic -- diff --sexpression $fileB $fileA > $diffFileBA
53+
cabal new-run --verbose=0 semantic -- diff --sexpression $fileB $fileA > $diffFileBA
5454
fi
5555
}
5656

semantic.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ library
267267
, Semantic.Task.Files
268268
, Semantic.Telemetry
269269
, Semantic.Telemetry.AsyncQueue
270-
, Semantic.Telemetry.Haystack
270+
, Semantic.Telemetry.Error
271271
, Semantic.Telemetry.Log
272272
, Semantic.Telemetry.Stat
273273
, Semantic.Timeout

src/Semantic/Config.hs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module Semantic.Config
66
, debugOptions
77
, infoOptions
88
, lookupStatsAddr
9-
, withHaystackFromConfig
9+
, withErrorReporterFromConfig
1010
, logOptionsFromConfig
1111
, withLoggerFromConfig
1212
, withStatterFromConfig
@@ -23,12 +23,11 @@ import Data.Duration
2323
import Data.Error (LogPrintSource(..))
2424
import Data.Flag
2525
import Network.HostName
26-
import Network.HTTP.Client.TLS
2726
import Network.URI
2827
import Prologue
2928
import Semantic.Env
3029
import Semantic.Telemetry
31-
import qualified Semantic.Telemetry.Haystack as Haystack
30+
import qualified Semantic.Telemetry.Error as Error
3231
import qualified Semantic.Telemetry.Stat as Stat
3332
import System.Environment
3433
import System.IO (hIsTerminalDevice, stdout)
@@ -45,7 +44,6 @@ data Config
4544
{ configAppName :: String -- ^ Application name ("semantic")
4645
, configHostName :: String -- ^ HostName from getHostName
4746
, configProcessID :: ProcessID -- ^ ProcessID from getProcessID
48-
, configHaystackURL :: Maybe String -- ^ URL of Haystack (with creds) from environment
4947
, configStatsHost :: Stat.Host -- ^ Host of statsd/datadog (default: "127.0.0.1")
5048
, configStatsPort :: Stat.Port -- ^ Port of statsd/datadog (default: "28125")
5149
, configTreeSitterParseTimeout :: Duration -- ^ Timeout in milliseconds before canceling tree-sitter parsing (default: 6000).
@@ -81,7 +79,6 @@ defaultConfig options@Options{..} = do
8179
pid <- getProcessID
8280
hostName <- getHostName
8381
isTerminal <- hIsTerminalDevice stdout
84-
haystackURL <- lookupEnv "HAYSTACK_URL"
8582
(statsHost, statsPort) <- lookupStatsAddr
8683
size <- envLookupNum 1000 "MAX_TELEMETRY_QUEUE_SIZE"
8784
parseTimeout <- envLookupNum 6000 "TREE_SITTER_PARSE_TIMEOUT"
@@ -90,7 +87,6 @@ defaultConfig options@Options{..} = do
9087
{ configAppName = "semantic"
9188
, configHostName = hostName
9289
, configProcessID = pid
93-
, configHaystackURL = haystackURL
9490
, configStatsHost = statsHost
9591
, configStatsPort = statsPort
9692

@@ -109,9 +105,9 @@ defaultConfig options@Options{..} = do
109105
withTelemetry :: Config -> (TelemetryQueues -> IO c) -> IO c
110106
withTelemetry config action =
111107
withLoggerFromConfig config $ \logger ->
112-
withHaystackFromConfig config (queueLogMessage logger Error) $ \haystack ->
108+
withErrorReporterFromConfig config (queueLogMessage logger Error) $ \errorReporter ->
113109
withStatterFromConfig config $ \statter ->
114-
action (TelemetryQueues logger statter haystack)
110+
action (TelemetryQueues logger statter errorReporter)
115111

116112
logOptionsFromConfig :: Config -> LogOptions
117113
logOptionsFromConfig Config{..} = LogOptions
@@ -132,9 +128,9 @@ withLoggerFromConfig :: Config -> (LogQueue -> IO c) -> IO c
132128
withLoggerFromConfig config = withLogger (logOptionsFromConfig config) (configMaxTelemetyQueueSize config)
133129

134130

135-
withHaystackFromConfig :: Config -> Haystack.ErrorLogger -> (HaystackQueue -> IO c) -> IO c
136-
withHaystackFromConfig Config{..} errorLogger =
137-
withHaystack configHaystackURL tlsManagerSettings configAppName errorLogger configMaxTelemetyQueueSize
131+
withErrorReporterFromConfig :: Config -> Error.ErrorLogger -> (ErrorQueue -> IO c) -> IO c
132+
withErrorReporterFromConfig Config{..} errorLogger =
133+
withErrorReporter (nullErrorReporter errorLogger) configMaxTelemetyQueueSize
138134

139135
withStatterFromConfig :: Config -> (StatQueue -> IO c) -> IO c
140136
withStatterFromConfig Config{..} =

src/Semantic/Telemetry.hs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ module Semantic.Telemetry
33
(
44
-- Async telemetry interface
55
withLogger
6-
, withHaystack
6+
, withErrorReporter
77
, withStatter
88
, LogQueue
99
, StatQueue
10-
, HaystackQueue
10+
, ErrorQueue
1111
, TelemetryQueues(..)
1212
, queueLogMessage
1313
, queueErrorReport
@@ -27,9 +27,8 @@ module Semantic.Telemetry
2727
, statsClient
2828
, StatsClient
2929

30-
-- Haystack client
31-
, haystackClient
32-
, HaystackClient
30+
-- Error reporters
31+
, nullErrorReporter
3332

3433
-- Logging options and formatters
3534
, Level(..)
@@ -58,21 +57,20 @@ import Control.Exception
5857
import Control.Monad.IO.Class
5958
import qualified Data.Time.Clock.POSIX as Time (getCurrentTime)
6059
import qualified Data.Time.LocalTime as LocalTime
61-
import Network.HTTP.Client
6260
import Semantic.Telemetry.AsyncQueue
63-
import Semantic.Telemetry.Haystack
61+
import Semantic.Telemetry.Error
6462
import Semantic.Telemetry.Log
6563
import Semantic.Telemetry.Stat as Stat
6664

6765
type LogQueue = AsyncQueue Message LogOptions
6866
type StatQueue = AsyncQueue Stat StatsClient
69-
type HaystackQueue = AsyncQueue ErrorReport HaystackClient
67+
type ErrorQueue = AsyncQueue ErrorReport ErrorReporter
7068

7169
data TelemetryQueues
7270
= TelemetryQueues
73-
{ telemetryLogger :: LogQueue
74-
, telemetryStatter :: StatQueue
75-
, telemetryHaystack :: HaystackQueue
71+
{ telemetryLogger :: LogQueue
72+
, telemetryStatter :: StatQueue
73+
, telemetryErrorReporter :: ErrorQueue
7674
}
7775

7876
-- | Execute an action in IO with access to a logger (async log queue).
@@ -83,10 +81,10 @@ withLogger :: LogOptions -- ^ Log options
8381
withLogger options size = bracket setup closeAsyncQueue
8482
where setup = newAsyncQueue size writeLogMessage options
8583

86-
-- | Execute an action in IO with access to haystack (async error reporting queue).
87-
withHaystack :: Maybe String -> ManagerSettings -> String -> ErrorLogger -> Int -> (HaystackQueue -> IO c) -> IO c
88-
withHaystack url settings appName errorLogger size = bracket setup closeAsyncQueue
89-
where setup = haystackClient url settings appName >>= newAsyncQueue size (reportError errorLogger)
84+
-- | Execute an action in IO with access to an error reporter (async error reporting queue).
85+
withErrorReporter :: IO ErrorReporter -> Int -> (ErrorQueue -> IO c) -> IO c
86+
withErrorReporter errorReporter size = bracket setup closeAsyncQueue
87+
where setup = errorReporter >>= newAsyncQueue size ($)
9088

9189
-- | Execute an action in IO with access to a statter (async stat queue).
9290
-- Handles the bracketed setup and teardown of the underlying 'AsyncQueue' and
@@ -108,8 +106,8 @@ queueLogMessage q@AsyncQueue{..} level message pairs
108106
, level <= logLevel = liftIO Time.getCurrentTime >>= liftIO . LocalTime.utcToLocalZonedTime >>= liftIO . writeAsyncQueue q . Message level message pairs
109107
| otherwise = pure ()
110108

111-
-- | Queue an error to be reported to haystack.
112-
queueErrorReport :: MonadIO io => HaystackQueue -> SomeException -> [(String, String)] -> io ()
109+
-- | Queue an error to be reported.
110+
queueErrorReport :: MonadIO io => ErrorQueue -> SomeException -> [(String, String)] -> io ()
113111
queueErrorReport q@AsyncQueue{..} message = liftIO . writeAsyncQueue q . ErrorReport message
114112

115113
-- | Queue a stat to be sent to statsd.

src/Semantic/Telemetry/Error.hs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
module Semantic.Telemetry.Error
2+
( ErrorLogger
3+
, ErrorReport (..)
4+
, ErrorReporter
5+
, nullErrorReporter
6+
) where
7+
8+
import Control.Exception
9+
10+
data ErrorReport
11+
= ErrorReport
12+
{ errorReportException :: SomeException
13+
, errorReportContext :: [(String, String)]
14+
} deriving (Show)
15+
16+
-- | Function to log if there are errors reporting an error.
17+
type ErrorLogger = String -> [(String, String)] -> IO ()
18+
19+
type ErrorReporter = ErrorReport -> IO ()
20+
21+
-- | Doesn't send error reports anywhere. Useful in tests or for basic command-line usage.
22+
nullErrorReporter :: ErrorLogger -> IO ErrorReporter
23+
nullErrorReporter logger = pure reportError
24+
where
25+
reportError ErrorReport{..} = let
26+
msg = takeWhile (/= '\n') (displayException errorReportException)
27+
in logger msg errorReportContext

src/Semantic/Telemetry/Haystack.hs

Lines changed: 0 additions & 82 deletions
This file was deleted.

test/fixtures/haskell/corpus/function-declarations.A.hs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,8 @@ ifte :: ( IvoryStore a
147147
-> Ivory eff a
148148
-> Ivory eff a
149149

150-
haystackClient maybeURL managerSettings appName
151-
| Just url <- maybeURL = do
152-
manager <- newManager managerSettings
153-
request' <- parseRequest url
154-
let request = request'
155-
{ method = "POST"
156-
, requestHeaders = ("Content-Type", "application/json; charset=utf-8") : requestHeaders request'
157-
}
158-
pure $ HaystackClient request manager appName
159-
| otherwise = pure NullHaystackClient
150+
nullErrorReporter logger = pure reportError
151+
where
152+
reportError ErrorReport{..} = let
153+
msg = takeWhile (/= '\n') (displayException errorReportException)
154+
in logger msg errorReportContext

test/fixtures/haskell/corpus/function-declarations.B.hs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,8 @@ g :: ( IvoryStore a
148148
-> Ivory eff a
149149
-> Ivory eff a
150150

151-
needlestackClient maybeURL directorSettings appName
152-
| Just url <- maybeURL = do
153-
director <- newDirector directorSettings
154-
request' <- parseRequest url
155-
let request = request'
156-
{ method = "POST"
157-
, requestHeaders = ("Content-Type", "application/json; charset=utf-8") : requestHeaders request'
158-
}
159-
pure $ NeedlestackClient request director appName
160-
| otherwise = pure NullNeedlestackClient
151+
emptyErrorReporter logger = pure reportError
152+
where
153+
reportError ErrorReport{..} = let
154+
msg = takeWhile (/= '\n') (displayException errorReportException)
155+
in logger msg errorReportContext

0 commit comments

Comments
 (0)