forked from haskell/haskell-language-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTracing.hs
141 lines (130 loc) · 5.73 KB
/
Tracing.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
{-# LANGUAGE CPP #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE PatternSynonyms #-}
{-# HLINT ignore #-}
module Development.IDE.Core.Tracing
( otTracedHandler
, otTracedAction
, otTracedProvider
, otSetUri
, otTracedGarbageCollection
, withTrace
, withEventTrace
, withTelemetryLogger
)
where
import Control.Exception.Safe (generalBracket)
import Control.Monad.Catch (ExitCase (..), MonadMask)
import Control.Monad.IO.Unlift
import Data.ByteString (ByteString)
import Data.ByteString.Char8 (pack)
import Data.String (IsString (fromString))
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import Data.Word (Word16)
import Debug.Trace.Flags (userTracingEnabled)
import Development.IDE.Graph (Action)
import Development.IDE.Graph.Rule
import Development.IDE.Types.Diagnostics (FileDiagnostic,
showDiagnostics)
import Development.IDE.Types.Location (Uri (..))
import Development.IDE.Types.Logger (Logger (Logger))
import Ide.Types (PluginId (..))
import Language.LSP.Types (NormalizedFilePath,
fromNormalizedFilePath)
import OpenTelemetry.Eventlog (SpanInFlight (..), addEvent,
beginSpan, endSpan, setTag,
withSpan)
withTrace :: (MonadMask m, MonadIO m) => String -> ((String -> String -> m ()) -> m a) -> m a
withTrace name act
| userTracingEnabled
= withSpan (fromString name) $ \sp -> do
let setSpan' k v = setTag sp (fromString k) (fromString v)
act setSpan'
| otherwise = act (\_ _ -> pure ())
withEventTrace :: (MonadMask m, MonadIO m) => String -> ((ByteString -> m ()) -> m a) -> m a
withEventTrace name act
| userTracingEnabled
= withSpan (fromString name) $ \sp -> do
act (addEvent sp "")
| otherwise = act (\_ -> pure ())
-- | Returns a logger that produces telemetry events in a single span
withTelemetryLogger :: (MonadIO m, MonadMask m) => (Logger -> m a) -> m a
withTelemetryLogger k = withSpan "Logger" $ \sp ->
-- Tracy doesn't like when we create a new span for every log line.
-- To workaround that, we create a single span for all log events.
-- This is fine since we don't care about the span itself, only about the events
k $ Logger $ \p m ->
addEvent sp (fromString $ show p) (encodeUtf8 $ trim m)
where
-- eventlog message size is limited by EVENT_PAYLOAD_SIZE_MAX = STG_WORD16_MAX
trim = T.take (fromIntegral(maxBound :: Word16) - 10)
-- | Trace a handler using OpenTelemetry. Adds various useful info into tags in the OpenTelemetry span.
otTracedHandler
:: MonadUnliftIO m
=> String -- ^ Message type
-> String -- ^ Message label
-> (SpanInFlight -> m a)
-> m a
otTracedHandler requestType label act
| userTracingEnabled = do
let !name =
if null label
then requestType
else requestType <> ":" <> show label
-- Add an event so all requests can be quickly seen in the viewer without searching
runInIO <- askRunInIO
liftIO $ withSpan (fromString name) (\sp -> addEvent sp "" (fromString $ name <> " received") >> runInIO (act sp))
| otherwise = act (SpanInFlight 0)
otSetUri :: SpanInFlight -> Uri -> IO ()
otSetUri sp (Uri t) = setTag sp "uri" (encodeUtf8 t)
-- | Trace a Shake action using opentelemetry.
otTracedAction
:: Show k
=> k -- ^ The Action's Key
-> NormalizedFilePath -- ^ Path to the file the action was run for
-> RunMode
-> (a -> String)
-> (([FileDiagnostic] -> Action ()) -> Action (RunResult a)) -- ^ The action
-> Action (RunResult a)
otTracedAction key file mode result act
| userTracingEnabled = fst <$>
generalBracket
(do
sp <- beginSpan (fromString (show key))
setTag sp "File" (fromString $ fromNormalizedFilePath file)
setTag sp "Mode" (fromString $ show mode)
return sp
)
(\sp ec -> do
case ec of
ExitCaseAbort -> setTag sp "aborted" "1"
ExitCaseException e -> setTag sp "exception" (pack $ show e)
ExitCaseSuccess res -> do
setTag sp "result" (pack $ result $ runValue res)
setTag sp "changed" $ case res of
RunResult x _ _ -> fromString $ show x
endSpan sp)
(\sp -> act (liftIO . setTag sp "diagnostics" . encodeUtf8 . showDiagnostics ))
| otherwise = act (\_ -> return ())
otTracedGarbageCollection :: (MonadMask f, MonadIO f, Show a) => ByteString -> f [a] -> f [a]
otTracedGarbageCollection label act
| userTracingEnabled = fst <$>
generalBracket
(beginSpan label)
(\sp ec -> do
case ec of
ExitCaseAbort -> setTag sp "aborted" "1"
ExitCaseException e -> setTag sp "exception" (pack $ show e)
ExitCaseSuccess res -> setTag sp "keys" (pack $ unlines $ map show res)
endSpan sp)
(const act)
| otherwise = act
otTracedProvider :: MonadUnliftIO m => PluginId -> ByteString -> m a -> m a
otTracedProvider (PluginId pluginName) provider act
| userTracingEnabled = do
runInIO <- askRunInIO
liftIO $ withSpan (provider <> " provider") $ \sp -> do
setTag sp "plugin" (encodeUtf8 pluginName)
runInIO act
| otherwise = act