diff --git a/cbits/hs_zk.c b/cbits/hs_zk.c
--- a/cbits/hs_zk.c
+++ b/cbits/hs_zk.c
@@ -390,6 +390,18 @@
 }
 
 // ----------------------------------------------------------------------------
+
+void hs_zoo_set_std_log_stream(int s) {
+  if (s == 1) {
+    zoo_set_log_stream(stdout);
+  } else if (s == 2) {
+    zoo_set_log_stream(stderr);
+  } else {
+    fprintf(stderr, "hs_zoo_set_std_log_stream: invalid %d\n", s);
+  }
+}
+
+// ----------------------------------------------------------------------------
 // Helpers
 
 const stat_t* dup_stat(const stat_t* old_stat) {
@@ -399,7 +411,7 @@
 }
 
 const string_vector_t* dup_string_vector(const string_vector_t* old_strings) {
-  int count = old_strings->count;
+  int32_t count = old_strings->count;
   if (count < 0) {
     fprintf(stderr, "dup_string_vector error: count %d\n", count);
     return NULL;
@@ -407,7 +419,7 @@
   string_vector_t* new_strings =
       (string_vector_t*)malloc(sizeof(string_vector_t));
   char** vals = malloc(count * sizeof(char*));
-  for (int i = 0; i < count; ++i) {
+  for (int32_t i = 0; i < count; ++i) {
     vals[i] = strdup(old_strings->data[i]);
   }
   new_strings->count = count;
@@ -415,15 +427,28 @@
   return new_strings;
 }
 
+void free_string_vector(string_vector_t* strings) {
+  if (strings == NULL || strings->data == NULL) {
+    return;
+  }
+
+  for (int32_t i = 0; i < strings->count; ++i) {
+    free(strings->data[i]);
+  }
+  free(strings->data);
+  strings->data = NULL;
+  free(strings);
+}
+
 const acl_vector_t* dup_acl_vector(const acl_vector_t* old_acls) {
-  int count = old_acls->count;
+  int32_t count = old_acls->count;
   if (count < 0) {
     fprintf(stderr, "dup_acl_vector error: count %d\n", count);
     return NULL;
   }
   acl_t* data = (acl_t*)malloc(count * sizeof(acl_t));
   acl_t* old_data = old_acls->data;
-  for (int i = 0; i < count; ++i) {
+  for (int32_t i = 0; i < count; ++i) {
     data[i].perms = old_data[i].perms;
     data[i].id.scheme = strdup(old_data[i].id.scheme);
     data[i].id.id = strdup(old_data[i].id.id);
@@ -433,4 +458,16 @@
   new_acls->count = count;
   new_acls->data = data;
   return new_acls;
+}
+
+void free_acl_vector(acl_vector_t* acls) {
+  if (acls == NULL || acls->data == NULL) {
+    return;
+  }
+  for (int32_t i = 0; i < acls->count; ++i) {
+    free(acls->data[i].id.id);
+    free(acls->data[i].id.scheme);
+  }
+  free(acls->data);
+  free(acls);
 }
diff --git a/src/ZooKeeper.hs b/src/ZooKeeper.hs
--- a/src/ZooKeeper.hs
+++ b/src/ZooKeeper.hs
@@ -1,11 +1,16 @@
 module ZooKeeper
   ( I.zooVersion
-  , I.zooSetDebugLevel
 
   , zookeeperResInit
   , U.withResource
   , U.Resource
 
+  , I.zooSetDebugLevel
+  , StdLogStream (..)
+  , zooSetStdLogStream
+  , FileLogStream
+  , withZooSetFileLogStream
+
   , zooCreate
   , zooCreateIfMissing
   , zooSet
@@ -34,16 +39,18 @@
   , zooRecvTimeout
   , isUnrecoverable
 
-    -- * Internal function (should NOT be used)
+    -- * Internal function
   , zookeeperInit
   , zookeeperClose
+  , openFileLogStream
+  , closeFileLogStream
   ) where
 
-import Control.Exception (catch)
+import           Control.Exception        (bracket, catch, throwIO)
 import           Control.Monad            (void, when, zipWithM, (<=<))
 import           Data.Bifunctor           (first)
 import           Data.Maybe               (fromMaybe)
-import           Foreign.C                (CInt)
+import           Foreign.C                (CFile, CInt)
 import           Foreign.Ptr              (FunPtr, Ptr, freeHaskellFunPtr,
                                            nullFunPtr, nullPtr, plusPtr)
 import           GHC.Stack                (HasCallStack)
@@ -747,3 +754,89 @@
 -- value may change after a server re-connect.
 zooRecvTimeout :: T.ZHandle -> IO CInt
 zooRecvTimeout = I.c_zoo_recv_timeout
+
+data StdLogStream = StdOutLogStream | StdErrLogStream
+
+newtype FileLogStream = FileLogStream (Ptr CFile)
+
+zooSetStdLogStream :: StdLogStream -> IO ()
+zooSetStdLogStream StdOutLogStream = I.hs_zoo_set_std_log_stream 1
+zooSetStdLogStream StdErrLogStream = I.hs_zoo_set_std_log_stream 2
+
+-- | Sets the file to be used by the library for logging.
+--
+-- The file will be closed after the computation you run, and the LogStream
+-- will be set to defaut stderr.
+--
+-- > let res = zookeeperResInit "127.0.0.1:2181" Nothing 5000 Nothing 0
+-- > withZooSetFileLogStream "/tmp/zk.log" "a+" $ do
+-- >   withResource res $ \zh -> do
+-- >     undefined
+withZooSetFileLogStream
+  :: CBytes
+  -- ^ File name, see 'openFileLogStream'
+  -> CBytes
+  -- ^ Mode, see 'openFileLogStream'
+  -> IO a
+  -- ^ Computation to run in-between
+  -> IO a
+withZooSetFileLogStream filename mode = bracket acquire release . const
+  where
+    acquire = do
+      new@(FileLogStream new') <- openFileLogStream filename mode
+      I.c_zoo_set_log_stream new'
+      pure new
+    -- FIXME: If zookeeper export the get LogStream api, then I can set back
+    -- to the original LogStream but the stderr.
+    release s = I.hs_zoo_set_std_log_stream 2 >> closeFileLogStream s
+
+-- | Open the file by filename using the given mode.
+--
+-- The operation may fail with IOError if open filed.
+--
+-- Also see: <https://man7.org/linux/man-pages/man3/fopen.3.html>
+openFileLogStream
+  :: CBytes
+  -- ^ File name
+  -> CBytes
+  -- ^ Mode
+  --
+  -- A string beginning with one of the following sequences:
+  --
+  -- r      Open text file for reading. The stream is positioned at
+  --        the beginning of the file.
+  --
+  -- r+     Open for reading and writing. The stream is positioned at
+  --        the beginning of the file.
+  --
+  -- w      Truncate file to zero length or create text file for
+  --        writing.  The stream is positioned at the beginning of the
+  --        file.
+  --
+  -- w+     Open for reading and writing. The file is created if it
+  --        does not exist, otherwise it is truncated. The stream is
+  --        positioned at the beginning of the file.
+  --
+  -- a      Open for appending (writing at end of file). The file is
+  --        created if it does not exist. The stream is positioned at
+  --        the end of the file.
+  --
+  -- a+     Open for reading and appending (writing at end of file).
+  --        The file is created if it does not exist. Output is
+  --        always appended to the end of the file.  POSIX is silent
+  --        on what the initial read position is when using this mode.
+  --        For glibc, the initial file position for reading is at the
+  --        beginning of the file, but for Android/BSD/MacOS, the
+  --        initial file position for reading is at the end of the
+  --        file.
+  -> IO FileLogStream
+openFileLogStream filename mode =
+  CBytes.withCBytesUnsafe filename $ \filename' ->
+  CBytes.withCBytesUnsafe mode $ \mode' -> do
+    cfilep <- I.c_fopen filename' mode'
+    if (cfilep == nullPtr)
+       then throwIO $ userError ("Open file " <> CBytes.unpack filename <> " failed!")
+       else pure $ FileLogStream cfilep
+
+closeFileLogStream :: FileLogStream -> IO ()
+closeFileLogStream (FileLogStream p) = I.c_fflush p >> void (I.c_fclose p)
diff --git a/src/ZooKeeper/Internal/FFI.hsc b/src/ZooKeeper/Internal/FFI.hsc
--- a/src/ZooKeeper/Internal/FFI.hsc
+++ b/src/ZooKeeper/Internal/FFI.hsc
@@ -7,8 +7,7 @@
 import           Control.Concurrent
 import           Control.Exception
 import           Control.Monad                (void)
-import           Data.Version                 (Version, makeVersion,
-                                               parseVersion)
+import           Data.Version                 (Version, makeVersion)
 import           Data.Word
 import           Foreign.C
 import           Foreign.ForeignPtr
@@ -16,7 +15,6 @@
 import           Foreign.StablePtr
 import           GHC.Conc
 import           GHC.Stack                    (HasCallStack)
-import           Text.ParserCombinators.ReadP (readP_to_S)
 import qualified Z.Data.CBytes                as CBytes
 import           Z.Foreign
 
@@ -25,18 +23,25 @@
 
 #include "hs_zk.h"
 
+#ifndef ZOO_MAJOR_VERSION
+import           Data.Version                 (parseVersion)
+import           Text.ParserCombinators.ReadP (readP_to_S)
+#endif
+
 -------------------------------------------------------------------------------
 
 zooVersion :: Version
 #ifdef ZOO_MAJOR_VERSION
+-- For zookeeper-3.4
 zooVersion = makeVersion [ (#const ZOO_MAJOR_VERSION)
                          , (#const ZOO_MINOR_VERSION)
                          , (#const ZOO_PATCH_VERSION)
                          ]
 #else
+-- For zookeeper-3.6+
 zooVersion = case readP_to_S parseVersion (#const_str ZOO_VERSION) of
                [_, _, (r, _)] -> r
-               otherwise      -> makeVersion [0, 0, 0]  -- unsupported
+               _otherwise     -> makeVersion [0, 0, 0]  -- unsupported
 #endif
 
 foreign import ccall unsafe "hs_zk.h &logLevel"
@@ -46,6 +51,12 @@
 foreign import ccall unsafe "hs_zk.h zoo_set_debug_level"
   zooSetDebugLevel :: ZooLogLevel -> IO ()
 
+foreign import capi unsafe "zookeeper/zookeeper.h zoo_set_log_stream"
+  c_zoo_set_log_stream :: Ptr CFile -> IO ()
+
+foreign import ccall unsafe "hs_zoo_set_std_log_stream"
+  hs_zoo_set_std_log_stream :: CInt -> IO ()
+
 foreign import ccall "wrapper"
   mkCWatcherFnPtr :: CWatcherFn -> IO (FunPtr CWatcherFn)
 
@@ -234,6 +245,17 @@
 
 foreign import ccall unsafe "zookeeper.h is_unrecoverable"
   c_is_unrecoverable :: ZHandle -> IO CInt
+
+-------------------------------------------------------------------------------
+
+foreign import capi unsafe "stdio.h fopen"
+  c_fopen :: BA## Word8 -> BA## Word8 -> IO (Ptr CFile)
+
+foreign import capi unsafe "stdio.h fclose"
+  c_fclose :: Ptr CFile -> IO CInt
+
+foreign import capi unsafe "stdio.h fflush"
+  c_fflush :: Ptr CFile -> IO CInt
 
 -------------------------------------------------------------------------------
 -- Helpers
diff --git a/src/ZooKeeper/Internal/Types.hsc b/src/ZooKeeper/Internal/Types.hsc
--- a/src/ZooKeeper/Internal/Types.hsc
+++ b/src/ZooKeeper/Internal/Types.hsc
@@ -3,7 +3,7 @@
 
 module ZooKeeper.Internal.Types where
 
-import           Control.Exception     (bracket_)
+import           Control.Exception     (finally)
 import           Control.Monad         (forM)
 import           Data.ByteString.Short (ShortByteString)
 import qualified Data.ByteString.Short as BShort (packCStringLen)
@@ -64,6 +64,10 @@
 pattern ZooLogInfo  = ZooLogLevel (#const ZOO_LOG_LEVEL_INFO)
 pattern ZooLogDebug = ZooLogLevel (#const ZOO_LOG_LEVEL_DEBUG)
 
+-- | Disable logging
+pattern ZooLogSilence :: ZooLogLevel
+pattern ZooLogSilence = ZooLogLevel 0
+
 -------------------------------------------------------------------------------
 
 -- | ACL permissions.
@@ -111,29 +115,38 @@
   { aclPerms    :: [ZooPerm]
   , aclIdScheme :: CBytes
   , aclId       :: CBytes
-  } deriving Show
+  } deriving (Show, Eq)
 
-{-# INLINE sizeOfZooAcl #-}
 sizeOfZooAcl :: Int
 sizeOfZooAcl = (#size acl_t)
+{-# INLINE sizeOfZooAcl #-}
 
-peekZooAcl :: Ptr ZooAcl -> IO ZooAcl
-peekZooAcl ptr = do
-  perms <- toZooPerms <$> (#peek acl_t, perms) ptr
-  scheme_ptr <- (#peek acl_t, id.scheme) ptr
-  id_ptr <- (#peek acl_t, id.id) ptr
-  scheme <- CBytes.fromCString scheme_ptr <* free scheme_ptr
-  acl_id <- CBytes.fromCString id_ptr <* free id_ptr
+newtype AclVector = AclVector (Ptr ())
+  deriving (Show, Eq)
+
+peekAclVector :: AclVector -> IO [ZooAcl]
+peekAclVector p@(AclVector ptr) = flip finally (free_acl_vector p) $ do
+  count <- fromIntegral @Int32 <$> (#peek acl_vector_t, count) ptr
+  data_ptr <- (#peek acl_vector_t, data) ptr
+  forM [0..count-1] $ peekAclVectorIdx data_ptr
+
+peekAclVectorIdx :: Ptr ZooAcl -> Int -> IO ZooAcl
+peekAclVectorIdx ptr offset = do
+  let ptr' = ptr `plusPtr` (offset * sizeOfZooAcl)
+  perms <- toZooPerms <$> (#peek acl_t, perms) ptr'
+  scheme_ptr <- (#peek acl_t, id.scheme) ptr'
+  id_ptr <- (#peek acl_t, id.id) ptr'
+  scheme <- CBytes.fromCString scheme_ptr
+  acl_id <- CBytes.fromCString id_ptr
   return $ ZooAcl perms scheme acl_id
 
 -- TODO
 unsafeAllocaZooAcl :: ZooAcl -> IO Z.ByteArray
 unsafeAllocaZooAcl = undefined
 
--- FIXME: consider this
--- data AclVector = AclVector (Ptr ()) | AclList [ZooAcl]
-newtype AclVector = AclVector (Ptr ())
-  deriving (Show, Eq)
+-- TODO
+fromAclList :: [ZooAcl] -> IO AclVector
+fromAclList = undefined
 
 -- | This is a completely open ACL
 foreign import ccall unsafe "hs_zk.h &ZOO_OPEN_ACL_UNSAFE"
@@ -147,17 +160,8 @@
 foreign import ccall unsafe "hs_zk.h &ZOO_CREATOR_ALL_ACL"
   zooCreatorAllAcl :: AclVector
 
-toAclList :: AclVector -> IO [ZooAcl]
-toAclList (AclVector ptr) = do
-  count <- fromIntegral @Int32 <$> (#peek acl_vector_t, count) ptr
-  data_ptr <- (#peek acl_vector_t, data) ptr
-  forM [0..count-1] $ \idx -> do
-    let data_ptr' = data_ptr `plusPtr` (idx * sizeOfZooAcl)
-    peekZooAcl data_ptr'
-
--- TODO
-fromAclList :: [ZooAcl] -> IO AclVector
-fromAclList = undefined
+foreign import ccall unsafe "free_acl_vector"
+  free_acl_vector :: AclVector -> IO ()
 
 -------------------------------------------------------------------------------
 
@@ -193,12 +197,14 @@
   deriving newtype (Text.Print)
 
 instance Show ZooState where
-  show ZooExpiredSession   = "ExpiredSession"
-  show ZooAuthFailed       = "AuthFailed"
-  show ZooConnectingState  = "ConnectingState"
-  show ZooAssociatingState = "AssociatingState"
-  show ZooConnectedState   = "ConnectedState"
-  show (ZooState x)        = "ZooState " <> show x
+  show ZooExpiredSession    = "ExpiredSession"
+  show ZooAuthFailed        = "AuthFailed"
+  show ZooConnectingState   = "ConnectingState"
+  show ZooAssociatingState  = "AssociatingState"
+  show ZooConnectedState    = "ConnectedState"
+  show ZooReadonlyState     = "ReadonlyState"
+  show ZooNotconnectedState = "NotconnectedState"
+  show (ZooState x)         = "ZooState " <> show x
 
 pattern
     ZooExpiredSession, ZooAuthFailed
@@ -209,12 +215,29 @@
 pattern ZooAssociatingState = ZooState (#const ZOO_ASSOCIATING_STATE)
 pattern ZooConnectedState   = ZooState (#const ZOO_CONNECTED_STATE)
 
--- TODO
--- pattern ZOO_READONLY_STATE :: ZooState
--- pattern ZOO_READONLY_STATE = ZooState (#const ZOO_READONLY_STATE)
--- pattern ZOO_NOTCONNECTED_STATE :: ZooState
--- pattern ZOO_NOTCONNECTED_STATE = ZooState (#const ZOO_NOTCONNECTED_STATE)
+-- This a trick to determine whether the C library expose the following apis.
+--
+-- ZOO_VERSION was introduced by ZOOKEEPER-3635 (3.6.0-pre). Also version after
+-- 3.6 exports the following states.
+#ifdef ZOO_VERSION
 
+pattern ZooReadonlyState :: ZooState
+pattern ZooReadonlyState = ZooState (#const ZOO_READONLY_STATE)
+
+pattern ZooNotconnectedState :: ZooState
+pattern ZooNotconnectedState = ZooState (#const ZOO_NOTCONNECTED_STATE)
+
+#else
+-- Hardcode for zookeeper-3.4
+
+pattern ZooReadonlyState :: ZooState
+pattern ZooReadonlyState = ZooState 5
+
+pattern ZooNotconnectedState :: ZooState
+pattern ZooNotconnectedState = ZooState 999
+
+#endif
+
 -------------------------------------------------------------------------------
 
 -- | Watch Types
@@ -280,32 +303,35 @@
 newtype CreateMode = CreateMode { unCreateMode :: CInt }
   deriving (Show, Eq, Storable)
 
--- TODO: The following C constants (such as ZOO_PERSISTENT, ZOO_PERSISTENT_SEQUENTIAL
---       and ZOO_EPTHMERAL_SEQUENTIAL) are not defined on clients <= 3.4.x. However,
---       they can be used by passing an integer directly.
---
---       The hard-coded ones will be replaced with the following patterns which use
---       C constants when the library only support clients >= 3.5.x.
-
---pattern ZooPersistent :: CreateMode
---pattern ZooPersistent = CreateMode (#const ZOO_PERSISTENT)
---
---pattern ZooPersistentSequential :: CreateMode
---pattern ZooPersistentSequential = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL)
---
---pattern ZooEphemeralSequential :: CreateMode
---pattern ZooEphemeralSequential = CreateMode (#const ZOO_EPHEMERAL_SEQUENTIAL)
---
---pattern ZooContainer :: CreateMode
---pattern ZooContainer = CreateMode (#const ZOO_CONTAINER)
+-- This a trick to determine whether the C library expose the following apis.
 --
---pattern ZooPersistentWithTTL :: CreateMode
---pattern ZooPersistentWithTTL = CreateMode (#const ZOO_PERSISTENT_WITH_TTL)
+-- ZOO_VERSION was introduced by ZOOKEEPER-3635 (3.6.0-pre).
 --
---pattern ZooPersistentSequentialWithTTL :: CreateMode
---pattern ZooPersistentSequentialWithTTL = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL)
+-- The following C constants (such as ZOO_PERSISTENT, ZOO_PERSISTENT_SEQUENTIAL
+-- and ZOO_EPTHMERAL_SEQUENTIAL) are not defined on clients <= 3.4.x.
+#ifdef ZOO_VERSION
 
 pattern ZooPersistent :: CreateMode
+pattern ZooPersistent = CreateMode (#const ZOO_PERSISTENT)
+
+pattern ZooPersistentSequential :: CreateMode
+pattern ZooPersistentSequential = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL)
+
+pattern ZooEphemeralSequential :: CreateMode
+pattern ZooEphemeralSequential = CreateMode (#const ZOO_EPHEMERAL_SEQUENTIAL)
+
+pattern ZooContainer :: CreateMode
+pattern ZooContainer = CreateMode (#const ZOO_CONTAINER)
+
+pattern ZooPersistentWithTTL :: CreateMode
+pattern ZooPersistentWithTTL = CreateMode (#const ZOO_PERSISTENT_WITH_TTL)
+
+pattern ZooPersistentSequentialWithTTL :: CreateMode
+pattern ZooPersistentSequentialWithTTL = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL)
+
+#else
+
+pattern ZooPersistent :: CreateMode
 pattern ZooPersistent = CreateMode 0
 
 pattern ZooPersistentSequential :: CreateMode
@@ -323,6 +349,8 @@
 pattern ZooPersistentSequentialWithTTL :: CreateMode
 pattern ZooPersistentSequentialWithTTL = CreateMode 6
 
+#endif
+
 -- | The znode will be deleted upon the client's disconnect.
 pattern ZooEphemeral :: CreateMode
 pattern ZooEphemeral = CreateMode (#const ZOO_EPHEMERAL)
@@ -367,8 +395,9 @@
 newtype StringVector = StringVector { unStrVec :: [CBytes] }
   deriving Show
 
+-- Peek a StringVector from point and then free the pointer
 peekStringVector :: Ptr StringVector -> IO StringVector
-peekStringVector ptr = bracket_ (return ()) (free ptr) $ do
+peekStringVector ptr = flip finally (free_string_vector ptr) $ do
   -- NOTE: Int32 is necessary, since count is int32_t in c
   count <- fromIntegral @Int32 <$> (#peek string_vector_t, count) ptr
   StringVector <$> forM [0..count-1] (peekStringVectorIdx ptr)
@@ -377,8 +406,11 @@
 peekStringVectorIdx ptr offset = do
   ptr' <- (#peek string_vector_t, data) ptr
   data_ptr <- peek $ ptr' `plusPtr` (offset * (sizeOf ptr'))
-  CBytes.fromCString data_ptr <* free data_ptr
+  CBytes.fromCString data_ptr  -- this will do a copy
 
+foreign import ccall unsafe "free_string_vector"
+  free_string_vector :: Ptr StringVector -> IO ()
+
 -------------------------------------------------------------------------------
 -- Callback datas
 
@@ -492,7 +524,7 @@
   csize = (#size hs_acl_completion_t)
   peekRet ptr = (#peek hs_acl_completion_t, rc) ptr
   peekData ptr = do
-    acls <- toAclList . AclVector =<< (#peek hs_acl_completion_t, acl) ptr
+    acls <- peekAclVector . AclVector =<< (#peek hs_acl_completion_t, acl) ptr
     stat_ptr <- (#peek hs_acl_completion_t, stat) ptr
     stat <- peekStat stat_ptr
     return $ AclCompletion acls stat
diff --git a/src/ZooKeeper/Internal/Utils.hs b/src/ZooKeeper/Internal/Utils.hs
--- a/src/ZooKeeper/Internal/Utils.hs
+++ b/src/ZooKeeper/Internal/Utils.hs
@@ -1,6 +1,6 @@
 module ZooKeeper.Internal.Utils
   ( -- * Resource management
-    Resource(..)
+    Resource (..)
   , initResource
   , initResource_
   , withResource
diff --git a/src/ZooKeeper/Types.hs b/src/ZooKeeper/Types.hs
--- a/src/ZooKeeper/Types.hs
+++ b/src/ZooKeeper/Types.hs
@@ -39,6 +39,8 @@
   , pattern I.ZooConnectingState
   , pattern I.ZooAssociatingState
   , pattern I.ZooConnectedState
+  , pattern I.ZooReadonlyState
+  , pattern I.ZooNotconnectedState
 
   , I.CreateMode
   , pattern I.ZooPersistent
@@ -55,6 +57,7 @@
   , pattern I.ZooLogWarn
   , pattern I.ZooLogInfo
   , pattern I.ZooLogDebug
+  , pattern I.ZooLogSilence
 
   , I.ZooPerm
   , pattern I.ZooPermRead
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -38,6 +38,7 @@
 opSpec zh = do
   describe "ZooKeeper.zooVersion" $ do
     it "version should be 3.4.* - 3.8.*" $ do
+      putStrLn $ "Tested with zooVersion: " <> show zooVersion
       zooVersion `shouldSatisfy` (>= makeVersion [3, 4, 0])
       zooVersion `shouldSatisfy` (<= makeVersion [3, 8, 0])
 
diff --git a/zoovisitor.cabal b/zoovisitor.cabal
--- a/zoovisitor.cabal
+++ b/zoovisitor.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               zoovisitor
-version:            0.2.5.1
+version:            0.2.6.1
 synopsis:
   A haskell binding to Apache Zookeeper C library(mt) using Haskell Z project.
 
@@ -12,7 +12,7 @@
 copyright:          Copyright (c)
 author:             mu
 maintainer:         mu@laxcat.xyz
-tested-with:        GHC ==8.10.7 || ==9.0.2 || ==9.2.8
+tested-with:        GHC ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.5
 category:           Database
 homepage:           https://github.com/ZHaskell/zoovisitor
 bug-reports:        https://github.com/ZHaskell/zoovisitor/issues
@@ -27,6 +27,26 @@
   type:     git
   location: https://github.com/ZHaskell/zoovisitor
 
+-- XXX: Hackage requires 'cabal-version' must be at most 3.0.
+-- But the flag 'zoovisitor_enable_asan' use 'hsc2hs-options' requres >= 3.6.
+-- flag zoovisitor_enable_asan
+--   default:     False
+--   description:
+--     Enable AddressSanitizer. This is only for local debug usage.
+--     Also, do not forget to set cabal-version to 3.6 manually.
+
+-- XXX: require cabal-version >= 3.6
+-- Tricky options to link static archive, see: https://github.com/haskell/cabal/issues/4677
+-- common link-asan
+--   if os(osx)
+--     ghc-options: "-optl-Wl,-lasan"
+--
+--   if !os(osx)
+--     ghc-options:
+--       -pgml g++ "-optl-Wl,--allow-multiple-definition"
+--       "-optl-Wl,--whole-archive" "-optl-Wl,-Bstatic" "-optl-Wl,-lasan"
+--       "-optl-Wl,-Bdynamic" "-optl-Wl,--no-whole-archive"
+
 library
   hs-source-dirs:     src
   exposed-modules:
@@ -45,15 +65,25 @@
 
   build-depends:
     , base        >=4.12      && <5
-    , bytestring  >=0.10.10.0
+    , bytestring  >=0.10.10.0 && <0.13
     , exceptions  ^>=0.10
-    , Z-Data      >=0.7.2     && <1.5 || ^>=2.0
+    , Z-Data      >=0.7.2     && <1.5  || ^>=2.0
 
   includes:           hs_zk.h
   c-sources:          cbits/hs_zk.c
   include-dirs:       include /usr/local/include
   build-tool-depends: hsc2hs:hsc2hs
   extra-libraries:    zookeeper_mt
+
+  -- XXX: require cabal-version >= 3.6
+  -- if flag(zoovisitor_enable_asan)
+  --   cc-options:
+  --     -fsanitize=address -fno-omit-frame-pointer -static-libasan
+
+  --   hsc2hs-options:
+  --     "--cflag=-fsanitize=address" "--lflag=-fsanitize=address"
+  --     "--cflag=-static-libasan" "--lflag=-static-libasan"
+
   default-language:   Haskell2010
   default-extensions:
     BangPatterns
@@ -80,14 +110,18 @@
     -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints
 
 test-suite zoovisitor-test
+  -- XXX: require cabal-version >= 3.6
+  -- if flag(zoovisitor_enable_asan)
+  --   import: link-asan
+
   type:             exitcode-stdio-1.0
   main-is:          Spec.hs
   hs-source-dirs:   test
   build-depends:
-    , async
-    , base        >=4.12 && <5
-    , hspec
-    , uuid
+    , async       ^>=2.2
+    , base        >=4.12  && <5
+    , hspec       ^>=2.11
+    , uuid        ^>=1.3
     , Z-Data
     , zoovisitor
 
