helics 0.2.1 → 0.3.0
raw patch · 9 files changed
+221/−348 lines, 9 files
Files
- common/Network/Helics/Types.hs +72/−0
- dummy/Network/Helics.hs +116/−0
- dummy/Network/Helics/Sampler.hs +6/−0
- helics.cabal +24/−16
- src/Network/Helics.hs +0/−260
- src/Network/Helics/Foreign/Client.hsc +1/−4
- src/Network/Helics/Foreign/Common.hsc +1/−19
- src/Network/Helics/Foreign/Transaction.hsc +1/−5
- src/Network/Helics/Sampler.hs +0/−44
+ common/Network/Helics/Types.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}++module Network.Helics.Types where++import Control.Exception++import Foreign.C.Types+import Foreign.C.String++import Data.Typeable+import Data.Default.Class+import qualified Data.ByteString as S++-- common+newtype ReturnCode = ReturnCode CInt deriving (Eq, Typeable, Show)+instance Exception ReturnCode++-- client+type StatusCallback = CInt -> IO ()+newtype StatusCode = StatusCode CInt deriving (Eq, Show)++-- transaction+newtype TransactionId = TransactionId CLong++newtype SegmentId = SegmentId CLong+type SqlObfuscator = CString -> IO CString++-- main+data HelicsConfig = HelicsConfig+ { licenseKey :: S.ByteString+ , appName :: S.ByteString+ , language :: S.ByteString+ , languageVersion :: S.ByteString+ , statusCallback :: Maybe (StatusCode -> IO ())+ }++instance Default HelicsConfig where+ def = HelicsConfig+ (error "license key is not set.")+ "App"+ "Haskell"+ COMPILER_VERSION+ Nothing++data TransactionType+ = Default+ | Web S.ByteString+ | Other S.ByteString++instance Default TransactionType where+ def = Default++data Operation+ = SELECT+ | INSERT+ | UPDATE+ | DELETE+ deriving (Show)++data DatastoreSegment = DatastoreSegment+ { table :: S.ByteString+ , operation :: Operation+ , sql :: S.ByteString+ , sqlTraceRollupName :: S.ByteString+ , sqlObFuscator :: Maybe (S.ByteString -> S.ByteString)+ }+instance Default DatastoreSegment where+ def = DatastoreSegment "unknown" SELECT "" "unknown" Nothing++
+ dummy/Network/Helics.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.Helics+ ( HelicsConfig(..)+ , withHelics+ , sampler+ -- * metric+ , recordMetric+ , recordCpuUsage+ , recordMemoryUsage+ -- * transaction+ , TransactionType(..)+ , TransactionId+ , withTransaction+ , addAttribute+ , setRequestUrl+ , setMaxTraceSegments+ -- * segment+ , SegmentId+ , autoScope+ , rootSegment+ , genericSegment+ , Operation(..)+ , DatastoreSegment(..)+ , datastoreSegment+ , externalSegment+ -- * status code+ , StatusCode+ , statusShutdown+ , statusStarting+ , statusStopping+ , statusStarted+ -- * reexports+ , def+ ) where++import System.IO.Error++import Control.Exception+import Control.Monad+import Control.Concurrent++import Foreign.C+import Foreign.Ptr+import Foreign.Marshal+import Foreign.Storable++import Data.Word+import Data.Default.Class+import qualified Data.ByteString as S++import qualified Network.Helics.Sampler as Sampler+import Network.Helics.Types++autoScope, rootSegment :: SegmentId+autoScope = SegmentId 0+rootSegment = SegmentId 1++statusShutdown, statusStarting, statusStopping, statusStarted :: StatusCode+statusShutdown = StatusCode 0+statusStarting = StatusCode 1+statusStopping = StatusCode 2+statusStarted = StatusCode 3++-- | start new relic® collector client.+-- you must call this function when embed-mode.+withHelics :: HelicsConfig -> IO a -> IO a+withHelics _ m = m++-- | record custom metric.+recordMetric :: S.ByteString -> Double -> IO ()+recordMetric _ _ = return ()++-- | sample and send metric of cpu/memory usage.+sampler :: Int -- ^ sampling frequency (sec)+ -> IO ()+sampler _ = return ()++-- | record CPU usage. Normally, you don't need to call this function. use sampler.+recordCpuUsage :: Double -> Double -> IO ()+recordCpuUsage _ _ = return ()++-- | record memory usage. Normally, you don't need to call this function. use sampler.+recordMemoryUsage :: Double -> IO ()+recordMemoryUsage _ = return ()++withTransaction :: S.ByteString -- ^ name of transaction+ -> TransactionType -> (TransactionId -> IO c) -> IO c+withTransaction _ _ m = m (TransactionId 0)++genericSegment :: SegmentId -- ^ parent segment id+ -> S.ByteString -- ^ name of represent segment+ -> IO c -- ^ action in segment+ -> TransactionId+ -> IO c+genericSegment _ _ m _ = m++datastoreSegment :: SegmentId -> DatastoreSegment -> IO a -> TransactionId -> IO a+datastoreSegment _ _ m _ = m++externalSegment :: SegmentId+ -> S.ByteString -- ^ host of segment+ -> S.ByteString -- ^ name of segment+ -> IO a -> TransactionId -> IO a+externalSegment _ _ _ m _ = m++addAttribute :: S.ByteString -> S.ByteString -> TransactionId -> IO ()+addAttribute _ _ _ = return ()++setRequestUrl :: S.ByteString -> TransactionId -> IO ()+setRequestUrl _ _ = return ()++setMaxTraceSegments :: Int -> TransactionId -> IO ()+setMaxTraceSegments _ _ = return ()
+ dummy/Network/Helics/Sampler.hs view
@@ -0,0 +1,6 @@+module Network.Helics.Sampler where++type Callback = Double -> Double -> Int -> IO ()++sampler :: Callback -> Int -> IO ()+sampler _ _ = return ()
helics.cabal view
@@ -1,5 +1,5 @@ name: helics-version: 0.2.1+version: 0.3.0 synopsis: New Relic® agent SDK wrapper for Haskell. description: New Relic® agent SDK wrapper for Haskell.@@ -28,27 +28,35 @@ flag example default: False +flag dummy+ description: compile dummy modules on !linux.+ default: True+ library exposed-modules: Network.Helics Network.Helics.Sampler- other-modules: Network.Helics.Foreign.Common- Network.Helics.Foreign.Client- Network.Helics.Foreign.Transaction- Network.Helics.Foreign.System- -- other-extensions: + other-modules: Network.Helics.Types+ ghc-options: -Wall -O2+ default-language: Haskell2010 build-depends: base >=4.6 && <4.8 , data-default-class >=0.0 && <0.1- , time >=1.2 && <1.6- , unix >=2.6 && <2.8 , bytestring >=0.10 && <0.11- , bytestring-show >=0.3 && <0.4- ghc-options: -Wall -O2- hs-source-dirs: src- build-tools: hsc2hs- extra-libraries: newrelic-collector-client- , newrelic-transaction- , newrelic-common- default-language: Haskell2010++ if flag(dummy) && !os(linux)+ hs-source-dirs: common, dummy+ else+ other-modules: Network.Helics.Foreign.Common+ Network.Helics.Foreign.Client+ Network.Helics.Foreign.Transaction+ Network.Helics.Foreign.System+ build-depends: time >=1.2 && <1.6+ , unix >=2.6 && <2.8+ , bytestring-show >=0.3 && <0.4+ hs-source-dirs: common, src+ build-tools: hsc2hs+ extra-libraries: newrelic-collector-client+ , newrelic-transaction+ , newrelic-common executable helics-example main-is: example.hs
− src/Network/Helics.hs
@@ -1,260 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}--module Network.Helics- ( HelicsConfig(..)- , withHelics- , sampler- -- * metric- , recordMetric- , recordCpuUsage- , recordMemoryUsage- -- * transaction- , TransactionType(..)- , TransactionId- , withTransaction- , addAttribute- , setRequestUrl- , setMaxTraceSegments- -- * segment- , SegmentId- , autoScope- , rootSegment- , genericSegment- , Operation(..)- , DatastoreSegment(..)- , datastoreSegment- , externalSegment- -- * status code- , StatusCode- , statusShutdown- , statusStarting- , statusStopping- , statusStarted- -- * reexports- , def- ) where--import System.IO.Error--import Control.Exception-import Control.Monad-import Control.Concurrent--import Foreign.C-import Foreign.Ptr-import Foreign.Marshal-import Foreign.Storable--import Data.Word-import Data.Default.Class-import qualified Data.ByteString as S--import qualified Network.Helics.Sampler as Sampler-import Network.Helics.Foreign.Common-import Network.Helics.Foreign.Client-import Network.Helics.Foreign.Transaction--data HelicsConfig = HelicsConfig- { licenseKey :: S.ByteString- , appName :: S.ByteString- , language :: S.ByteString- , languageVersion :: S.ByteString- , statusCallback :: Maybe (StatusCode -> IO ())- }--instance Default HelicsConfig where- def = HelicsConfig- (error "license key is not set.")- "App"- "Haskell"- COMPILER_VERSION- Nothing--guardNr :: CInt -> IO ()-guardNr c = unless (c == 0) $ throwIO $ ReturnCode c--initialize :: HelicsConfig -> IO ()-initialize HelicsConfig{..} =- S.useAsCString licenseKey $ \key ->- S.useAsCString appName $ \app ->- S.useAsCString language $ \lng ->- S.useAsCString languageVersion $ \ver ->- newrelic_init key app lng ver >>= guardNr--shutdown :: S.ByteString -> IO ()-shutdown reason =- S.useAsCString reason $ \r ->- newrelic_request_shutdown r >>= \c ->- guardNr c---- | start new relic® collector client.--- you must call this function when embed-mode.-withHelics :: HelicsConfig -> IO a -> IO a-withHelics cfg m = bracket bra ket (const m)- where- bra = do- mv <- newEmptyMVar- cb <- makeStatusCallback (\i -> do- when (i == 3 || i == 0) $ putMVar mv ()- maybe (return ()) ($ StatusCode i) $ statusCallback cfg)- newrelic_register_status_callback cb-- newrelic_register_message_handler newrelic_message_handler-- initialize cfg- takeMVar mv- return (freeHaskellFunPtr cb, mv)-- ket (freePtr, mv) = do- shutdown "withHelics: shutdown"- takeMVar mv :: IO ()- freePtr :: IO ()---- | record custom metric.-recordMetric :: S.ByteString -> Double -> IO ()-recordMetric str d = S.useAsCString str $ \mtr ->- newrelic_record_metric mtr (realToFrac d) >>= guardNr---- | sample and send metric of cpu/memory usage.-sampler :: Int -- ^ sampling frequency (sec)- -> IO ()-sampler s = flip Sampler.sampler (s * 10^(6::Int)) $ \user cpu mem -> do- recordCpuUsage user cpu- recordMemoryUsage (fromIntegral mem / (1024 * 1024))---- | record CPU usage. Normally, you don't need to call this function. use sampler.-recordCpuUsage :: Double -> Double -> IO ()-recordCpuUsage ut p =- newrelic_record_cpu_usage (realToFrac ut) (realToFrac p) >>= guardNr---- | record memory usage. Normally, you don't need to call this function. use sampler.-recordMemoryUsage :: Double -> IO ()-recordMemoryUsage mb = - newrelic_record_memory_usage (realToFrac mb) >>= guardNr--data TransactionType- = Default- | Web S.ByteString- | Other S.ByteString--instance Default TransactionType where- def = Default--withTransaction :: S.ByteString -- ^ name of transaction- -> TransactionType -> (TransactionId -> IO c) -> IO c-withTransaction name typ act = bracket bra ket- (\tid -> act tid `catch` exceptionHandler tid)- where- bra = do- tid <- newrelic_transaction_begin- guardNr =<< S.useAsCString name (newrelic_transaction_set_name tid)- return $ TransactionId tid- ket (TransactionId tid) = do- case typ of- Default -> return ()- Web cat ->- guardNr =<< S.useAsCString cat (newrelic_transaction_set_category tid)- Other cat -> do- guardNr =<< newrelic_transaction_set_type_other tid- guardNr =<< S.useAsCString cat (newrelic_transaction_set_category tid)- guardNr =<< newrelic_transaction_end tid-- exceptionHandler (TransactionId tid) se = case fromException se of- Just e -> do- withCString (show $ ioeGetErrorType e) $ \et ->- withCString (ioeGetErrorString e) $ \msg ->- newrelic_transaction_notice_error tid et msg nullPtr nullPtr >>= guardNr- ioError e-- Nothing -> do- withCString (show se) $ \et ->- newrelic_transaction_notice_error tid et nullPtr nullPtr nullPtr >>= guardNr- throwIO se--guardSid :: CLong -> IO CLong-guardSid sid =- if sid >= 0- then return sid- else throwIO $ ReturnCode (fromIntegral sid)--segment :: CLong -> IO CLong -> IO a -> IO a-segment tid m a = bracket (m >>= guardSid) (newrelic_segment_end tid) (const a)--genericSegment :: SegmentId -- ^ parent segment id- -> S.ByteString -- ^ name of represent segment- -> IO c -- ^ action in segment- -> TransactionId- -> IO c-genericSegment (SegmentId pid) name act (TransactionId tid) = segment tid- (S.useAsCString name $ newrelic_segment_generic_begin tid pid) act--data Operation- = SELECT- | INSERT- | UPDATE- | DELETE- deriving (Show)--opToBS :: Operation -> S.ByteString-opToBS SELECT = newrelicDatastoreSelect-opToBS INSERT = newrelicDatastoreInsert-opToBS UPDATE = newrelicDatastoreUpdate-opToBS DELETE = newrelicDatastoreDelete--data DatastoreSegment = DatastoreSegment- { table :: S.ByteString- , operation :: Operation- , sql :: S.ByteString- , sqlTraceRollupName :: S.ByteString- , sqlObFuscator :: Maybe (S.ByteString -> S.ByteString)- }-instance Default DatastoreSegment where- def = DatastoreSegment "unknown" SELECT "" "unknown" Nothing--toCObfuscator :: (S.ByteString -> S.ByteString) -> CString -> IO CString-toCObfuscator f i = do- s <- S.packCString i- m <- mallocBytes (S.length s + 1)- pokeByteOff m (S.length s) (0 :: Word8)- S.useAsCString (f s) (\c -> copyBytes m c (S.length s))- return m--datastoreSegment :: SegmentId -> DatastoreSegment -> IO a -> TransactionId -> IO a-datastoreSegment (SegmentId pid) DatastoreSegment{..} act (TransactionId tid) = - S.useAsCString table $ \tbl ->- S.useAsCString (opToBS operation) $ \op ->- S.useAsCString sql $ \q ->- S.useAsCString sqlTraceRollupName $ \tr -> do- case sqlObFuscator of- Nothing -> segment tid- (newrelic_segment_datastore_begin tid pid tbl op q tr- newrelic_basic_literal_replacement_obfuscator) act- Just f ->- bracket (makeObfuscator $ toCObfuscator f) freeHaskellFunPtr $ \cf ->- segment tid- (newrelic_segment_datastore_begin tid pid tbl op q tr cf) act--externalSegment :: SegmentId- -> S.ByteString -- ^ host of segment- -> S.ByteString -- ^ name of segment- -> IO a -> TransactionId -> IO a-externalSegment (SegmentId pid) host name act (TransactionId tid) =- S.useAsCString host $ \h ->- S.useAsCString name $ \n ->- segment tid (newrelic_segment_external_begin tid pid h n) act--addAttribute :: S.ByteString -> S.ByteString -> TransactionId -> IO ()-addAttribute name value (TransactionId tid) =- S.useAsCString name $ \n ->- S.useAsCString value $ \v ->- guardNr =<< newrelic_transaction_add_attribute tid n v--setRequestUrl :: S.ByteString -> TransactionId -> IO ()-setRequestUrl req (TransactionId tid) =- guardNr =<< S.useAsCString req (newrelic_transaction_set_request_url tid)--setMaxTraceSegments :: Int -> TransactionId -> IO ()-setMaxTraceSegments mx (TransactionId tid) = guardNr =<<- newrelic_transaction_set_max_trace_segments tid (fromIntegral mx)
src/Network/Helics/Foreign/Client.hsc view
@@ -6,9 +6,7 @@ import Foreign.C import Foreign.Ptr--newtype StatusCode = StatusCode CInt- deriving (Eq, Show)+import Network.Helics.Types #{enum StatusCode, StatusCode , statusShutdown = NEWRELIC_STATUS_CODE_SHUTDOWN@@ -19,7 +17,6 @@ foreign import ccall "&newrelic_message_handler" newrelic_message_handler :: FunPtr (Ptr rawMessage -> IO ()) -type StatusCallback = CInt -> IO () foreign import ccall "wrapper" makeStatusCallback :: StatusCallback -> IO (FunPtr StatusCallback) foreign import ccall newrelic_register_status_callback :: FunPtr StatusCallback -> IO ()
src/Network/Helics/Foreign/Common.hsc view
@@ -5,27 +5,9 @@ #include <newrelic_common.h> -import Data.Typeable-import Control.Exception import Foreign.C import Foreign.Ptr--newtype ReturnCode = ReturnCode CInt- deriving (Eq, Typeable)--instance Show ReturnCode where- show c@(ReturnCode i)- | c == returnCodeOther = "ReturnCode Other(" ++ show (#{const NEWRELIC_RETURN_CODE_OTHER} :: Int) ++ ")"- | c == returnCodeDisabled = "ReturnCode Disabled(" ++ show (#{const NEWRELIC_RETURN_CODE_DISABLED} :: Int) ++ ")"- | c == returnCodeInvalidParam = "ReturnCode Invalid param(" ++ show (#{const NEWRELIC_RETURN_CODE_INVALID_PARAM} :: Int) ++ ")"- | c == returnCodeInvalidId = "ReturnCode Invalid id(" ++ show (#{const NEWRELIC_RETURN_CODE_INVALID_ID} :: Int) ++ ")"- | c == returnCodeTransactionNotStarted = "ReturnCode Transaction not started(" ++ show (#{const NEWRELIC_RETURN_CODE_TRANSACTION_NOT_STARTED} :: Int) ++ ")"- | c == returnCodeTransactionInProgress = "ReturnCode Transaction in progress(" ++ show (#{const NEWRELIC_RETURN_CODE_TRANSACTION_IN_PROGRESS} :: Int) ++ ")"- | c == returnCodeTransactionNotNamed = "ReturnCode Transaction not named(" ++ show (#{const NEWRELIC_RETURN_CODE_TRANSACTION_NOT_NAMED} :: Int) ++ ")"- | otherwise = "ReturnCode Unknown(" ++ show i ++ ")"---instance Exception ReturnCode+import Network.Helics.Types #{enum ReturnCode, ReturnCode , returnCodeOk = NEWRELIC_RETURN_CODE_OK
src/Network/Helics/Foreign/Transaction.hsc view
@@ -8,10 +8,7 @@ import Foreign.C import Foreign.Ptr import qualified Data.ByteString as S--newtype TransactionId = TransactionId CLong--newtype SegmentId = SegmentId CLong+import Network.Helics.Types autoScope, rootSegment :: SegmentId autoScope = SegmentId #const NEWRELIC_AUTOSCOPE@@ -55,7 +52,6 @@ foreign import ccall newrelic_segment_generic_begin :: CLong -> CLong -> CString -> IO CLong -type SqlObfuscator = CString -> IO CString foreign import ccall "wrapper" makeObfuscator :: SqlObfuscator -> IO (FunPtr SqlObfuscator) foreign import ccall newrelic_segment_datastore_begin :: CLong -> CLong -> CString -> CString -> CString -> CString -> FunPtr SqlObfuscator -> IO CLong
− src/Network/Helics/Sampler.hs
@@ -1,44 +0,0 @@-module Network.Helics.Sampler where--import System.Posix.Process--import Foreign.C.Types--import GHC.Conc--import Control.Applicative--import Network.Helics.Foreign.System--import Data.Time.Clock--type Callback = Double -> Double -> Int -> IO ()--sampler :: Callback -> Int -> IO ()-sampler callback sleep = do- t <- fromIntegral <$> clockTick- core <- fromIntegral <$> getNumCapabilities- cTime <- getCurrentTime- uTime <- fromIntegral <$> getUserTime- pSize <- fromIntegral <$> pageSize- pid <- getProcessID- threadDelay sleep- go t core pSize pid cTime uTime- where-- unCClock (CClock c) = c- getUserTime = unCClock . userTime <$> getProcessTimes-- go tick core pSize pid = loop- where- loop cTime uTime = do- cTime' <- getCurrentTime- uTime' <- fromIntegral <$> getUserTime- pages <- getPages pid- let real = realToFrac $ diffUTCTime cTime' cTime- user = (uTime' - uTime) / tick- cpu = user / (real * core)- mem = pages * pSize- callback user cpu mem- threadDelay sleep- loop cTime' uTime'