diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for extism-pdk
 
+## 1.2.0.0
+
+* Add bindings to `http_headers` Extism host funtion
+* Add `trace` level logging
+
 ## 1.1.0.0
 
 * Remove calls to free where Extism host expects to take ownership
diff --git a/examples/CountVowels.hs b/examples/CountVowels.hs
--- a/examples/CountVowels.hs
+++ b/examples/CountVowels.hs
@@ -22,6 +22,10 @@
 countVowels = do
   -- Get input string from Extism host
   s <- input
+
+  -- Log input
+  () <- Extism.PDK.log LogInfo ("Got input: " ++ s)
+
   -- Calculate the number of vowels
   let count = length (filter isVowel s)
   -- Return a JSON object {"count": count} back to the host
diff --git a/extism-pdk.cabal b/extism-pdk.cabal
--- a/extism-pdk.cabal
+++ b/extism-pdk.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               extism-pdk
-version:            1.1.0.0
+version:            1.2.0.0
 
 -- A short (one-line) description of the package.
 synopsis: Extism Plugin Development Kit
diff --git a/src/Extism/PDK.hs b/src/Extism/PDK.hs
--- a/src/Extism/PDK.hs
+++ b/src/Extism/PDK.hs
@@ -27,8 +27,7 @@
 input :: forall a. (FromBytes a) => IO a
 input = do
   i <- inputByteString
-  let x = fromBytes i
-  case x of
+  case fromBytes i of
     Left e -> error e
     Right y -> return y
 
@@ -104,22 +103,23 @@
   extismSetError $ memoryOffset s
 
 -- | Log level
-data LogLevel = LogInfo | LogDebug | LogWarn | LogError
+data LogLevel = LogTrace | LogDebug | LogInfo | LogWarn | LogError deriving (Enum)
 
 -- | Log to configured log file
 log :: LogLevel -> String -> IO ()
-log LogInfo msg = do
-  s <- allocString msg
-  extismLogInfo (memoryOffset s)
-log LogDebug msg = do
-  s <- allocString msg
-  extismLogDebug (memoryOffset s)
-log LogWarn msg = do
-  s <- allocString msg
-  extismLogWarn (memoryOffset s)
-log LogError msg = do
-  s <- allocString msg
-  extismLogError (memoryOffset s)
+log level msg = do
+  configuredLevel <- extismGetLogLevel
+  if fromIntegral (fromEnum level) < configuredLevel
+    then return ()
+    else do
+      s <- allocString msg
+      let offs = memoryOffset s
+      case level of
+        LogTrace -> extismLogTrace offs
+        LogDebug -> extismLogDebug offs
+        LogInfo -> extismLogInfo offs
+        LogWarn -> extismLogWarn offs
+        LogError -> extismLogError offs
 
 -- Log with "error" level
 logError :: String -> IO ()
@@ -136,3 +136,7 @@
 -- Log with "warn" level
 logWarn :: String -> IO ()
 logWarn = Extism.PDK.log LogWarn
+
+-- Log with "trace" level
+logTrace :: String -> IO ()
+logTrace = Extism.PDK.log LogTrace
diff --git a/src/Extism/PDK/Bindings.hs b/src/Extism/PDK/Bindings.hs
--- a/src/Extism/PDK/Bindings.hs
+++ b/src/Extism/PDK/Bindings.hs
@@ -37,6 +37,10 @@
 
 foreign import ccall "extism_log_error" extismLogError :: MemoryOffset -> IO ()
 
+foreign import ccall "extism_log_trace" extismLogTrace :: MemoryOffset -> IO ()
+
+foreign import ccall "extism_get_log_level" extismGetLogLevel :: IO Int32
+
 foreign import ccall "extism_store_u8" extismStoreU8 :: MemoryOffset -> Word8 -> IO ()
 
 foreign import ccall "extism_store_u64" extismStoreU64 :: MemoryOffset -> Word64 -> IO ()
@@ -69,6 +73,8 @@
 
 foreign import ccall "extism_http_status_code" extismHTTPStatusCode :: IO Int32
 
+foreign import ccall "extism_http_headers" extismHTTPHeaders :: IO MemoryOffset
+
 foreign import ccall "__wasm_call_ctors" wasmConstructor :: IO ()
 
 foreign import ccall "__wasm_call_dtors" wasmDestructor :: IO ()
@@ -90,9 +96,8 @@
   if index >= total
     then return $ B.concat . Prelude.reverse $ acc
     else do
-      let diff = total - index
       (n, x) <-
-        if diff >= 8
+        if total - index >= 8
           then do
             u <- f8 index
             return (8, word64ToBS u)
@@ -114,9 +119,8 @@
   if index >= total
     then pure ()
     else do
-      let diff = total - index
       (n, sub) <-
-        if diff >= 8
+        if total - index >= 8
           then do
             let (curr, next) = B.splitAt 8 src
             u <- bsToWord64 curr
diff --git a/src/Extism/PDK/HTTP.hs b/src/Extism/PDK/HTTP.hs
--- a/src/Extism/PDK/HTTP.hs
+++ b/src/Extism/PDK/HTTP.hs
@@ -26,7 +26,8 @@
 -- | HTTP Response
 data Response = Response
   { statusCode :: Int,
-    responseData :: ByteString
+    responseData :: ByteString,
+    responseHeaders :: [(String, String)]
   }
 
 -- | Creates a new 'Request'
@@ -46,11 +47,11 @@
 
 -- | Get the 'Response' body as a 'ByteString'
 responseByteString :: Response -> ByteString
-responseByteString (Response _ mem) = mem
+responseByteString (Response _ mem _) = mem
 
 -- | Get the 'Response' body as a 'String'
 responseString :: Response -> String
-responseString (Response _ mem) = fromByteString mem
+responseString (Response _ mem _) = fromByteString mem
 
 -- | Get the 'Response' body as JSON
 responseJSON :: (Text.JSON.Generic.Data a) => Response -> IO (Either String a)
@@ -67,8 +68,21 @@
 
 -- | Get the 'Response' body and decode it
 response :: (FromBytes a) => Response -> Either String a
-response (Response _ mem) = fromBytes mem
+response (Response _ mem _) = fromBytes mem
 
+getHeaders = do
+  offs <- extismHTTPHeaders
+  if offs == 0
+    then
+      return []
+    else do
+      mem <- Extism.PDK.Memory.findMemory offs
+      h <- Extism.PDK.Memory.load mem
+      () <- Extism.PDK.Memory.free mem
+      case h of
+        Left _ -> return []
+        Right (JSON x) -> return x
+
 -- | Send HTTP request with an optional request body
 sendRequestWithBody :: (ToBytes a) => Request -> a -> IO Response
 sendRequestWithBody req b = do
@@ -83,37 +97,39 @@
   j <- allocString json
   res <- extismHTTPRequest (memoryOffset j) (memoryOffset body)
   code <- extismHTTPStatusCode
+  h <- getHeaders
   if res == 0
-    then return (Response (fromIntegral code) empty)
+    then return (Response (fromIntegral code) empty h)
     else do
       mem <- findMemory res
       bs <- loadByteString mem
       free mem
-      return (Response (fromIntegral code) bs)
+      return (Response (fromIntegral code) bs h)
 
 -- | Send HTTP request with an optional request body
 sendRequest :: (ToBytes a) => Request -> Maybe a -> IO Response
-sendRequest req b =
-  let json =
-        encode
-          Extism.Manifest.HTTPRequest
-            { Extism.Manifest.url = url req,
-              Extism.Manifest.headers = NotNull $ headers req,
-              Extism.Manifest.method = NotNull $ method req
-            }
-   in let bodyMem = case b of
-            Nothing -> return $ Memory 0 0
-            Just b -> alloc b
-       in do
-            body <- bodyMem
-            j <- allocString json
-            res <- extismHTTPRequest (memoryOffset j) (memoryOffset body)
-            code <- extismHTTPStatusCode
-            if res == 0
-              then return (Response (fromIntegral code) empty)
-              else do
-                len <- extismLengthUnsafe res
-                let mem = Memory res len
-                bs <- loadByteString mem
-                free mem
-                return (Response (fromIntegral code) bs)
+sendRequest req b = do
+  body <- bodyMem
+  j <- allocString json
+  res <- extismHTTPRequest (memoryOffset j) (memoryOffset body)
+  code <- extismHTTPStatusCode
+  h <- getHeaders
+  if res == 0
+    then return (Response (fromIntegral code) empty h)
+    else do
+      len <- extismLengthUnsafe res
+      let mem = Memory res len
+      bs <- loadByteString mem
+      free mem
+      return (Response (fromIntegral code) bs h)
+  where
+    json =
+      encode
+        Extism.Manifest.HTTPRequest
+          { Extism.Manifest.url = url req,
+            Extism.Manifest.headers = NotNull $ headers req,
+            Extism.Manifest.method = NotNull $ method req
+          }
+    bodyMem = case b of
+      Nothing -> return $ Memory 0 0
+      Just b -> alloc b
diff --git a/src/Extism/PDK/Memory.hs b/src/Extism/PDK/Memory.hs
--- a/src/Extism/PDK/Memory.hs
+++ b/src/Extism/PDK/Memory.hs
@@ -49,8 +49,7 @@
 -- | Store data into a 'Memory' block
 store :: (ToBytes a) => Memory -> a -> IO ()
 store (Memory offs len) a =
-  let bs = toBytes a
-   in writeBytes offs len bs
+  writeBytes offs len $ toBytes a
 
 -- | Set plugin output to the provided 'Memory' block
 outputMemory :: Memory -> IO ()
@@ -70,8 +69,7 @@
 -- | Store string in 'Memory' block
 storeString :: Memory -> String -> IO ()
 storeString mem s =
-  let bs = toByteString s
-   in storeByteString mem bs
+  storeByteString mem $ toByteString s
 
 -- | Store byte string in 'Memory' block
 storeByteString :: Memory -> B.ByteString -> IO ()
@@ -80,20 +78,20 @@
 
 -- | Encode a value and copy it into Extism memory, returning the Memory block
 alloc :: (ToBytes a) => a -> IO Memory
-alloc x =
-  let bs = toBytes x
-   in do
-        Memory offs len <- memAlloc (B.length bs)
-        writeBytes offs len bs
-        return $ Memory offs len
+alloc x = do
+  Memory offs len <- memAlloc (B.length bs)
+  writeBytes offs len bs
+  return $ Memory offs len
+  where
+    bs = toBytes x
 
 -- | Allocate a new 'Memory' block
 memAlloc :: Int -> IO Memory
-memAlloc n =
-  let len = fromIntegral n
-   in do
-        offs <- extismAlloc len
-        return $ Memory offs len
+memAlloc n = do
+  offs <- extismAlloc len
+  return $ Memory offs len
+  where
+    len = fromIntegral n
 
 -- | Free a 'Memory' block
 free :: Memory -> IO ()
@@ -149,39 +147,36 @@
 
 instance FromBytes String where
   fromBytes mem =
-    let s = fromBytes mem
-     in case s of
-          Left e -> Left e
-          Right x -> Right $ fromByteString x
+    case fromBytes mem of
+      Left e -> Left e
+      Right x -> Right $ fromByteString x
 
 instance ToBytes String where
   toBytes = toByteString
 
 instance (Text.JSON.Generic.Data a) => FromBytes (JSON a) where
   fromBytes mem =
-    let a = fromBytes mem
-     in case a of
-          Left e -> Left e
-          Right x ->
-            case Text.JSON.decode x of
+    case fromBytes mem of
+      Left e -> Left e
+      Right x ->
+        case Text.JSON.decode x of
+          Text.JSON.Error e -> Left e
+          Text.JSON.Ok y ->
+            case Text.JSON.Generic.fromJSON y of
               Text.JSON.Error e -> Left e
-              Text.JSON.Ok y ->
-                case Text.JSON.Generic.fromJSON y of
-                  Text.JSON.Error e -> Left e
-                  Text.JSON.Ok z -> Right (JSON z)
+              Text.JSON.Ok z -> Right (JSON z)
 
 instance (Text.JSON.Generic.Data a) => ToBytes (JSON a) where
   toBytes (JSON x) = toBytes (Text.JSON.Generic.encodeJSON x)
 
 instance (Extism.PDK.MsgPack.MsgPack a) => FromBytes (MsgPack a) where
   fromBytes mem =
-    let a = fromBytes mem
-     in case a of
+    case fromBytes mem of
+      Left e -> Left e
+      Right x ->
+        case Extism.PDK.MsgPack.decode x of
           Left e -> Left e
-          Right x ->
-            case Extism.PDK.MsgPack.decode x of
-              Left e -> Left e
-              Right y -> Right (MsgPack y)
+          Right y -> Right (MsgPack y)
 
 instance (Extism.PDK.MsgPack.MsgPack a) => ToBytes (MsgPack a) where
   toBytes (MsgPack x) = toBytes $ Extism.PDK.MsgPack.encode x
@@ -191,75 +186,69 @@
 
 instance FromBytes Int32 where
   fromBytes mem =
-    let bs = fromBytes mem
-     in case bs of
-          Left e -> Left e
-          Right x ->
-            case runGetOrFail getInt32le (B.fromStrict x) of
-              Left (_, _, e) -> Left e
-              Right (_, _, x) -> Right x
+    case fromBytes mem of
+      Left e -> Left e
+      Right x ->
+        case runGetOrFail getInt32le (B.fromStrict x) of
+          Left (_, _, e) -> Left e
+          Right (_, _, x) -> Right x
 
 instance ToBytes Int64 where
   toBytes i = toBytes $ B.toStrict (runPut (putInt64le i))
 
 instance FromBytes Int64 where
   fromBytes mem =
-    let bs = fromBytes mem
-     in case bs of
-          Left e -> Left e
-          Right x ->
-            case runGetOrFail getInt64le (B.fromStrict x) of
-              Left (_, _, e) -> Left e
-              Right (_, _, x) -> Right x
+    case fromBytes mem of
+      Left e -> Left e
+      Right x ->
+        case runGetOrFail getInt64le (B.fromStrict x) of
+          Left (_, _, e) -> Left e
+          Right (_, _, x) -> Right x
 
 instance ToBytes Word32 where
   toBytes i = toBytes $ B.toStrict (runPut (putWord32le i))
 
 instance FromBytes Word32 where
   fromBytes mem =
-    let bs = fromBytes mem
-     in case bs of
-          Left e -> Left e
-          Right x ->
-            case runGetOrFail getWord32le (B.fromStrict x) of
-              Left (_, _, e) -> Left e
-              Right (_, _, x) -> Right x
+    case fromBytes mem of
+      Left e -> Left e
+      Right x ->
+        case runGetOrFail getWord32le (B.fromStrict x) of
+          Left (_, _, e) -> Left e
+          Right (_, _, x) -> Right x
 
 instance ToBytes Word64 where
   toBytes i = toBytes $ B.toStrict (runPut (putWord64le i))
 
 instance FromBytes Word64 where
   fromBytes mem =
-    let bs = fromBytes mem
-     in case bs of
-          Left e -> Left e
-          Right x ->
-            case runGetOrFail getWord64le (B.fromStrict x) of
-              Left (_, _, e) -> Left e
-              Right (_, _, x) -> Right x
+    case fromBytes mem of
+      Left e -> Left e
+      Right x ->
+        case runGetOrFail getWord64le (B.fromStrict x) of
+          Left (_, _, e) -> Left e
+          Right (_, _, x) -> Right x
 
 instance ToBytes Float where
   toBytes i = toBytes $ B.toStrict (runPut (putFloatle i))
 
 instance FromBytes Float where
   fromBytes mem =
-    let bs = fromBytes mem
-     in case bs of
-          Left e -> Left e
-          Right x ->
-            case runGetOrFail getFloatle (B.fromStrict x) of
-              Left (_, _, e) -> Left e
-              Right (_, _, x) -> Right x
+    case fromBytes mem of
+      Left e -> Left e
+      Right x ->
+        case runGetOrFail getFloatle (B.fromStrict x) of
+          Left (_, _, e) -> Left e
+          Right (_, _, x) -> Right x
 
 instance ToBytes Double where
   toBytes i = toBytes $ B.toStrict (runPut (putDoublele i))
 
 instance FromBytes Double where
   fromBytes mem =
-    let bs = fromBytes mem
-     in case bs of
-          Left e -> Left e
-          Right x ->
-            case runGetOrFail getDoublele (B.fromStrict x) of
-              Left (_, _, e) -> Left e
-              Right (_, _, x) -> Right x
+    case fromBytes mem of
+      Left e -> Left e
+      Right x ->
+        case runGetOrFail getDoublele (B.fromStrict x) of
+          Left (_, _, e) -> Left e
+          Right (_, _, x) -> Right x
diff --git a/src/extism-pdk.c b/src/extism-pdk.c
--- a/src/extism-pdk.c
+++ b/src/extism-pdk.c
@@ -65,6 +65,9 @@
 DEFINE(http_status_code, int32_t)
 int32_t extism_http_status_code() { return _http_status_code(); }
 
+DEFINE(http_headers, ExtismPointer)
+ExtismPointer extism_http_headers() { return _http_headers(); }
+
 DEFINE(log_info, void, ExtismPointer)
 void extism_log_info(ExtismPointer p) { return _log_info(p); }
 DEFINE(log_debug, void, ExtismPointer)
@@ -73,3 +76,7 @@
 void extism_log_warn(ExtismPointer p) { return _log_warn(p); }
 DEFINE(log_error, void, ExtismPointer)
 void extism_log_error(ExtismPointer p) { return _log_error(p); }
+DEFINE(log_trace, void, ExtismPointer)
+void extism_log_trace(ExtismPointer p) { return _log_trace(p); }
+DEFINE(get_log_level, int32_t)
+int32_t extism_get_log_level() { return _get_log_level(); }
