packages feed

hotel-california 0.0.3.0 → 0.0.4.0

raw patch · 4 files changed

+94/−10 lines, 4 filesdep +unordered-containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: unordered-containers

API changes (from Hackage documentation)

+ HotelCalifornia.Exec: SpanError :: SpanStatus'
+ HotelCalifornia.Exec: SpanOk :: SpanStatus'
+ HotelCalifornia.Exec: SpanUnset :: SpanStatus'
+ HotelCalifornia.Exec: [execArgsAttributes] :: ExecArgs -> HashMap Text Attribute
+ HotelCalifornia.Exec: [execArgsSigintStatus] :: ExecArgs -> SpanStatus'
+ HotelCalifornia.Exec: data SpanStatus'
+ HotelCalifornia.Exec: parseAttribute :: String -> Either String (Text, Attribute)
+ HotelCalifornia.Exec: parseSpanStatus' :: ReadM SpanStatus'
- HotelCalifornia.Exec: ExecArgs :: Subprocess -> Maybe Text -> ExecArgs
+ HotelCalifornia.Exec: ExecArgs :: Subprocess -> Maybe Text -> SpanStatus' -> HashMap Text Attribute -> ExecArgs

Files

CHANGELOG.md view
@@ -6,6 +6,18 @@ and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/). +## Unreleased++## 0.0.4.0 - 2024-01-26++- [#14](https://github.com/parsonsmatt/hotel-california/pull/14/)+    - Add the `--set-sigint-status` to control how `Ctrl-C` and `SIGINT` are+      reported.+- [#16](https://github.com/parsonsmatt/hotel-california/pull/16)+    - You can now provide attributes for the span that `hotel-california`+      creates by passing the CLI argument `--attribute KEY=VALUE`. Only string+      attributes are currently supported.+ ## 0.0.3.0 - 2023-09-18  - [#13](https://github.com/parsonsmatt/hotel-california/pull/13)
README.md view
@@ -11,6 +11,8 @@ ```sh $ hotel exec --help Usage: hotel exec [-s|--span-name SPAN_NAME]+                  [-i|--set-sigint-status SPAN_STATUS]+                  [-a|--attribute KEY=VALUE]...                   (COMMAND [ARGUMENT]... | --shell SCRIPT)    Execute the given command with tracing enabled@@ -19,6 +21,10 @@   -h,--help                Show this help text   -s,--span-name SPAN_NAME The name of the span that the program reports. By                            default, this is the script you pass in.+  -i,--set-sigint-status SPAN_STATUS+                           The status reported when the process is killed with+                           SIGINT.+  -a,--attribute KEY=VALUE A string attribute to add to the span.   --shell SCRIPT           Run an arbitrary shell script instead of running an                            executable command ```
hotel-california.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.2.+-- This file has been generated from package.yaml by hpack version 0.35.4. -- -- see: https://github.com/sol/hpack  name:           hotel-california-version:        0.0.3.0+version:        0.0.4.0 description:    Please see the README on GitHub at <https://github.com/parsonsmatt/hotel-california#readme> homepage:       https://github.com/parsonsmatt/hotel-california#readme bug-reports:    https://github.com/parsonsmatt/hotel-california/issues@@ -87,6 +87,7 @@     , time     , typed-process     , unliftio+    , unordered-containers   default-language: Haskell2010  executable hotel@@ -150,6 +151,7 @@     , time     , typed-process     , unliftio+    , unordered-containers   default-language: Haskell2010  test-suite hotel-california-test@@ -214,4 +216,5 @@     , time     , typed-process     , unliftio+    , unordered-containers   default-language: Haskell2010
src/HotelCalifornia/Exec.hs view
@@ -2,17 +2,23 @@ -- enabled. module HotelCalifornia.Exec where +import qualified Control.Exception as Exception+import qualified Data.Char as Char import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NEL import Data.Maybe (fromMaybe)-import Data.Text (Text)+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap import qualified Data.Text as Text+import Data.Text (Text) import HotelCalifornia.Tracing import HotelCalifornia.Tracing.TraceParent-import System.Environment (getEnvironment)-import qualified System.Posix.Escape.Unicode as Escape+import qualified OpenTelemetry.Trace.Core as Otel+import OpenTelemetry.Trace (Attribute(..), PrimitiveAttribute(..)) import Options.Applicative hiding (command)+import System.Environment (getEnvironment) import System.Exit+import qualified System.Posix.Escape.Unicode as Escape import System.Process.Typed  data Subprocess = Proc (NonEmpty String) | Shell String@@ -28,8 +34,24 @@ data ExecArgs = ExecArgs     { execArgsSubprocess :: Subprocess     , execArgsSpanName :: Maybe Text+    , execArgsSigintStatus :: SpanStatus'+    , execArgsAttributes :: HashMap Text Attribute     } +-- | A variant of 'SpanStatus' that does not include a 'Text' for error.+data SpanStatus'+    = SpanUnset+    | SpanOk+    | SpanError++parseSpanStatus' :: ReadM SpanStatus'+parseSpanStatus' = eitherReader \s ->+    case map Char.toLower s of+        "unset" -> Right SpanUnset+        "ok" -> Right SpanOk+        "error" -> Right SpanError+        _ -> Left $ mconcat ["Expected one of `unset`, `ok`, or `error` for SPAN_STATUS. Got: ", s]+ parseProc :: Parser (NonEmpty String) parseProc = do     command <- argument str (metavar "COMMAND")@@ -47,6 +69,14 @@ parseSubprocess :: Parser Subprocess parseSubprocess = fmap Proc parseProc <|> fmap Shell parseShell +-- | Parse a `key=value` string into an attribute.+parseAttribute :: String -> Either String (Text, Attribute)+parseAttribute input = do+    let (key, value') = Text.breakOn "=" $ Text.pack input+    if Text.null value' || Text.null key+    then Left $ "Attributes must contain a non-empty key and value separated by `=`: " <> input+    else pure $ (key, AttributeValue $ TextAttribute $ Text.drop 1 value')+ parseExecArgs :: Parser ExecArgs parseExecArgs = do     execArgsSpanName <- optional do@@ -56,6 +86,21 @@             , short 's'             , help "The name of the span that the program reports. By default, this is the script you pass in."             ]+    execArgsSigintStatus <-+        option parseSpanStatus' $ mconcat+            [ metavar "SPAN_STATUS"+            , long "set-sigint-status"+            , short 'i'+            , help "The status reported when the process is killed with SIGINT."+            , value SpanUnset+            ]+    execArgsAttributes <-+        HashMap.fromList <$> (many $ option (eitherReader parseAttribute) $ mconcat+            [ metavar "KEY=VALUE"+            , long "attribute"+            , short 'a'+            , help "A string attribute to add to the span."+            ])     execArgsSubprocess <- parseSubprocess     pure ExecArgs{..} @@ -64,16 +109,34 @@     let script = commandToString execArgsSubprocess         spanName =             fromMaybe (Text.pack script) execArgsSpanName+        spanArguments = defaultSpanArguments { Otel.attributes = execArgsAttributes } -    inSpan' spanName \span_ -> do+    inSpanWith' spanName spanArguments \span_ -> do         newEnv <- spanContextToEnvironment span_         fullEnv <- mappend newEnv <$> getEnvironment          let processConfig = commandToProcessConfig execArgsSubprocess -        exitCode <- runProcess $ setEnv fullEnv processConfig-        case exitCode of-            ExitSuccess ->+        let handleSigInt =+                \case+                    Exception.UserInterrupt ->+                        case execArgsSigintStatus of+                            SpanUnset ->+                                pure Nothing+                            SpanOk -> do+                                Otel.setStatus span_ Otel.Ok+                                pure Nothing+                            SpanError -> do+                                Exception.throwIO Exception.UserInterrupt+                    other ->+                        Exception.throwIO other++        mexitCode <- Exception.handle handleSigInt $ fmap Just $ runProcess $ setEnv fullEnv processConfig++        case mexitCode of+            Just ExitSuccess ->                 pure ()-            _ ->+            Just exitCode ->                 exitWith exitCode+            Nothing ->+                pure ()