This is an automated email from the ASF dual-hosted git repository.

twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new 7770f13f3 refactor: replace glog with spdlog (#2881)
7770f13f3 is described below

commit 7770f13f394748763f5f02a954a88821b9492310
Author: Twice <[email protected]>
AuthorDate: Sun Apr 20 17:50:59 2025 +0800

    refactor: replace glog with spdlog (#2881)
    
    Signed-off-by: PragmaTwice <[email protected]>
    Co-authored-by: Aleks Lozovyuk <[email protected]>
    Co-authored-by: hulk <[email protected]>
---
 CMakeLists.txt                                  |   8 +-
 NOTICE                                          |   2 +-
 cmake/{glog.cmake => spdlog.cmake}              |  12 +--
 kvrocks.conf                                    |  19 ++--
 licenses/LICENSE-glog.txt                       |  28 ------
 licenses/LICENSE-spdlog.txt                     |  25 +++++
 src/cli/daemon_util.h                           |   4 +-
 src/cli/main.cc                                 |  64 ++++++++----
 src/cli/signal_util.h                           |   9 +-
 src/cluster/replication.cc                      |   2 +-
 src/cluster/slot_import.h                       |   3 +-
 src/cluster/slot_migrate.h                      |   2 +-
 src/commands/cmd_hll.cc                         |   6 +-
 src/commands/commander.h                        |   2 +-
 src/common/logging.h                            | 123 ++++++++++++++++++++++++
 src/common/status.h                             |   2 +-
 src/config/config.cc                            |  34 ++-----
 src/config/config.h                             |   8 +-
 src/server/redis_connection.cc                  |   2 +-
 src/server/redis_request.cc                     |   2 +-
 src/server/server.cc                            |   2 +-
 src/server/worker.cc                            |   2 +-
 src/storage/batch_extractor.cc                  |   3 +-
 src/storage/batch_indexer.h                     |   6 +-
 src/storage/compact_filter.cc                   |   3 +-
 src/storage/compaction_checker.cc               |   3 +-
 src/storage/event_listener.h                    |   2 +-
 src/storage/rdb/rdb.cc                          |   3 +-
 src/storage/scripting.cc                        |   6 +-
 src/storage/scripting.h                         |   2 +-
 src/storage/storage.cc                          |  18 ++--
 src/types/hyperloglog.cc                        |   4 +-
 src/types/redis_bitmap.cc                       |   2 +-
 src/types/redis_bitmap_string.cc                |   3 +-
 src/types/redis_hyperloglog.cc                  |  12 +--
 src/types/sample_helper.h                       |   2 +-
 src/types/tdigest.cc                            |  10 +-
 src/types/tdigest.h                             |   4 +-
 tests/cppunit/main.cc                           |   2 -
 tests/cppunit/types/tdigest_test.cc             |   2 +-
 tests/gocase/unit/connection/connection_test.go |   1 +
 tests/gocase/unit/log/logclean_test.go          |   4 +-
 tests/gocase/util/server.go                     |   4 +-
 utils/kvrocks2redis/config.cc                   |   5 +-
 utils/kvrocks2redis/config.h                    |   3 +-
 utils/kvrocks2redis/main.cc                     |  22 +++--
 utils/kvrocks2redis/parser.cc                   |   2 +-
 utils/kvrocks2redis/redis_writer.h              |   3 +-
 utils/kvrocks2redis/sync.cc                     |   2 +-
 49 files changed, 320 insertions(+), 174 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ec1e85acb..2fdff566a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,8 +45,8 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
         message(FATAL_ERROR "It is expected to build kvrocks with GCC 8 or 
above")
     endif()
 elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
-    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)
-        message(FATAL_ERROR "It is expected to build kvrocks with Clang 8 or 
above")
+    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9)
+        message(FATAL_ERROR "It is expected to build kvrocks with Clang 9 or 
above")
     endif()
 elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
     if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11)
@@ -151,7 +151,6 @@ if(ENABLE_OPENSSL)
 endif()
 
 include(cmake/gtest.cmake)
-include(cmake/glog.cmake)
 include(cmake/snappy.cmake)
 include(cmake/lz4.cmake)
 include(cmake/zlib.cmake)
@@ -160,6 +159,7 @@ include(cmake/tbb.cmake)
 include(cmake/rocksdb.cmake)
 include(cmake/libevent.cmake)
 include(cmake/fmt.cmake)
+include(cmake/spdlog.cmake)
 include(cmake/jsoncons.cmake)
 include(cmake/xxhash.cmake)
 include(cmake/span.cmake)
@@ -176,7 +176,6 @@ endif()
 
 find_package(Threads REQUIRED)
 
-list(APPEND EXTERNAL_LIBS glog)
 list(APPEND EXTERNAL_LIBS snappy)
 list(APPEND EXTERNAL_LIBS rocksdb_with_headers)
 list(APPEND EXTERNAL_LIBS event_with_headers)
@@ -184,6 +183,7 @@ list(APPEND EXTERNAL_LIBS lz4)
 list(APPEND EXTERNAL_LIBS zstd)
 list(APPEND EXTERNAL_LIBS zlib_with_headers)
 list(APPEND EXTERNAL_LIBS fmt)
+list(APPEND EXTERNAL_LIBS spdlog)
 if (ENABLE_LUAJIT)
     list(APPEND EXTERNAL_LIBS luajit)
 else()
diff --git a/NOTICE b/NOTICE
index f379027dd..d87bf7922 100644
--- a/NOTICE
+++ b/NOTICE
@@ -50,7 +50,6 @@ BSD-3-Clause licenses
 The following components are provided under the BSD-3-Clause License. See 
project link for details.
 The text of each license is also included in licenses/LICENSE-[project].txt.
 
-* glog(https://github.com/google/glog)
 * googletest(https://github.com/google/googletest)
 * libevent(https://github.com/libevent/libevent)
 * snappy(https://github.com/google/snappy)
@@ -71,6 +70,7 @@ The text of each license is also included in 
licenses/LICENSE-[project].txt
 * hat-trie(https://github.com/Tessil/hat-trie)
 * pegtl(https://github.com/taocpp/PEGTL, NOTE: changed to Boost Software 
License Version 1.0 in main branch)
 * cpptrace(https://github.com/jeremy-rifkin/cpptrace)
+* spdlog(https://github.com/gabime/spdlog)
 
 ================================================================
 Boost Software License Version 1.0
diff --git a/cmake/glog.cmake b/cmake/spdlog.cmake
similarity index 80%
rename from cmake/glog.cmake
rename to cmake/spdlog.cmake
index 50b892fe4..e5963fcb1 100644
--- a/cmake/glog.cmake
+++ b/cmake/spdlog.cmake
@@ -19,13 +19,11 @@ include_guard()
 
 include(cmake/utils.cmake)
 
-FetchContent_DeclareGitHubWithMirror(glog
-  google/glog v0.7.1
-  MD5=fa30180d4284c454bdd324ad3baf7f5f
+FetchContent_DeclareGitHubWithMirror(spdlog
+  gabime/spdlog v1.15.2
+  MD5=fb2697a809c3744033be3fea4a8f75d1
 )
 
-FetchContent_MakeAvailableWithArgs(glog
-  WITH_GFLAGS=OFF
-  WITH_GTEST=OFF
-  BUILD_SHARED_LIBS=OFF
+FetchContent_MakeAvailableWithArgs(spdlog
+  SPDLOG_FMT_EXTERNAL=ON
 )
diff --git a/kvrocks.conf b/kvrocks.conf
index 2d230b9fd..1c4bcaf34 100644
--- a/kvrocks.conf
+++ b/kvrocks.conf
@@ -128,22 +128,27 @@ db-name change.me.db
 dir /tmp/kvrocks
 
 # You can configure where to store your server logs by the log-dir.
-# If you don't specify one, we will use the above `dir` as our default log 
directory.
-# We also can send logs to stdout/stderr is as simple as:
+# If you don't specify one, we will use the above `dir` and
+# also stdout as our default log directory, e.g. `/tmp/kvrocks,stdout`.
+# `log-dir` can contain multiple destinations, separated by comma (,).
+# And every destination can be optionally followed by a corresponding log 
level,
+# separated by colon (:), e.g. 
`/tmp/my-log-dir:info,stdout:warning,stderr:error`.
+# If no log level attached with a destination,
+# the config option `log-level` will be used.
 #
-log-dir stdout
+# log-dir /tmp/kvrocks,stdout
 
 # Log level
-# Possible values: info, warning, error, fatal
+# Possible values: debug, info, warning, error, fatal
 # Default: info
 log-level info
 
 # You can configure log-retention-days to control whether to enable the log 
cleaner
 # and the maximum retention days that the INFO level logs will be kept.
 #
-# if set to -1, that means to disable the log cleaner.
-# if set to 0, all previous INFO level logs will be immediately removed.
-# if set to between 0 to INT_MAX, that means it will retent latest 
N(log-retention-days) day logs.
+# if set to negative or 0, that means to disable the log cleaner.
+# if set to between 1 to INT_MAX,
+# that means it will retent latest N(log-retention-days) day logs.
 
 # By default the log-retention-days is -1.
 log-retention-days -1
diff --git a/licenses/LICENSE-glog.txt b/licenses/LICENSE-glog.txt
deleted file mode 100644
index a62dde7a3..000000000
--- a/licenses/LICENSE-glog.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright (c) 2008, Google Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-    * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-    * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/licenses/LICENSE-spdlog.txt b/licenses/LICENSE-spdlog.txt
new file mode 100644
index 000000000..c4073451b
--- /dev/null
+++ b/licenses/LICENSE-spdlog.txt
@@ -0,0 +1,25 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Gabi Melman.                                       
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+-- NOTE: Third party dependency used by this software --
+This software depends on the fmt lib (MIT License),
+and users must comply to its license: 
https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE
diff --git a/src/cli/daemon_util.h b/src/cli/daemon_util.h
index d336e00a7..31be6a24d 100644
--- a/src/cli/daemon_util.h
+++ b/src/cli/daemon_util.h
@@ -21,7 +21,6 @@
 #pragma once
 
 #include <config/config.h>
-#include <glog/logging.h>
 #include <signal.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
@@ -29,6 +28,7 @@
 
 #include <cstdlib>
 
+#include "logging.h"
 #include "unique_fd.h"
 
 inline bool SupervisedUpstart() {
@@ -114,7 +114,7 @@ inline void Daemonize() {
   // change the file mode
   umask(0);
   if (setsid() < 0) {
-    LOG(ERROR) << "Failed to setsid, err: %s" << strerror(errno);
+    LOG(ERROR) << "Failed to setsid, err: " << strerror(errno);
     exit(1);
   }
 
diff --git a/src/cli/main.cc b/src/cli/main.cc
index 46b506046..19c4ae26e 100644
--- a/src/cli/main.cc
+++ b/src/cli/main.cc
@@ -18,6 +18,7 @@
  *
  */
 
+#include "spdlog/common.h"
 #ifdef __linux__
 #define _XOPEN_SOURCE 700  // NOLINT
 #else
@@ -25,18 +26,20 @@
 #endif
 
 #include <event2/thread.h>
-#include <glog/logging.h>
 
 #include <iomanip>
+#include <memory>
 #include <ostream>
 
 #include "daemon_util.h"
-#include "glog/log_severity.h"
 #include "io_util.h"
+#include "logging.h"
 #include "pid_util.h"
 #include "scope_exit.h"
 #include "server/server.h"
 #include "signal_util.h"
+#include "spdlog/sinks/daily_file_sink.h"
+#include "spdlog/sinks/stdout_color_sinks.h"
 #include "storage/storage.h"
 #include "string_util.h"
 #include "time_util.h"
@@ -94,24 +97,46 @@ static CLIOptions ParseCommandLineOptions(int argc, char 
**argv) {
   return opts;
 }
 
-static void InitGoogleLog(const Config *config) {
-  FLAGS_minloglevel = config->log_level;
-  FLAGS_max_log_size = 100;
-  FLAGS_logbufsecs = 0;
+static Status InitSpdlog(const Config &config) {
+  std::vector<spdlog::sink_ptr> sinks;
 
-  if (util::EqualICase(config->log_dir, "stdout")) {
-    for (int level = google::INFO; level <= google::FATAL; level++) {
-      google::SetLogDestination(static_cast<google::LogSeverity>(level), "");
+  // NOTE: to be compatible with old behaviors, we allow negative 
log_retention_days (-1)
+  auto retention_days = config.log_retention_days < 0 ? 0 : 
config.log_retention_days;
+
+  for (const auto &i : util::Split(config.log_dir, ",")) {
+    auto item = util::Trim(i, " ");
+    auto vals = util::Split(item, ":");
+    if (vals.empty()) {
+      return {Status::NotOK, "cannot get valid directory in config option 
log-dir"};
+    }
+
+    auto dir = vals[0];
+    auto level_str = vals.size() >= 2 ? vals[1] : "info";
+    auto it = std::find_if(log_levels.begin(), log_levels.end(), [&](const 
auto &v) { return v.name == level_str; });
+    if (it == log_levels.end()) {
+      return {Status::NotOK, "failed to set log level with config option 
log-dir"};
     }
-    FLAGS_stderrthreshold = google::ERROR;
-    FLAGS_logtostdout = true;
-    std::setbuf(stdout, nullptr);
-  } else {
-    FLAGS_log_dir = config->log_dir + "/";
-    if (config->log_retention_days != -1) {
-      google::EnableLogCleaner(std::chrono::hours(24) * 
config->log_retention_days);
+    auto level = it->val;
+
+    if (util::EqualICase(dir, "stdout")) {
+      sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
+    } else if (util::EqualICase(dir, "stderr")) {
+      sinks.push_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
+    } else {
+      sinks.push_back(
+          std::make_shared<spdlog::sinks::daily_file_sink_mt>(dir + 
"/kvrocks.log", 0, 0, false, retention_days));
     }
+
+    sinks.back()->set_level(level);
   }
+
+  auto logger = std::make_shared<spdlog::logger>("kvrocks", sinks.begin(), 
sinks.end());
+  logger->set_level(config.log_level);
+  logger->set_pattern("[%Y-%m-%dT%H:%M:%S.%f%z][%^%L%$][%s:%#] %v");
+  logger->flush_on(spdlog::level::info);
+  spdlog::set_default_logger(logger);
+
+  return Status::OK();
 }
 
 int main(int argc, char *argv[]) {
@@ -138,9 +163,10 @@ int main(int argc, char *argv[]) {
     }
   });
 
-  InitGoogleLog(&config);
-  google::InitGoogleLogging("kvrocks");
-  auto glog_exit = MakeScopeExit(google::ShutdownGoogleLogging);
+  if (auto s = InitSpdlog(config); !s) {
+    std::cout << "Failed to initialize logging system. Error: " << s.Msg() << 
std::endl;
+    return 1;
+  }
   LOG(INFO) << "kvrocks " << PrintVersion;
   // Tricky: We don't expect that different instances running on the same port,
   // but the server use REUSE_PORT to support the multi listeners. So we 
connect
diff --git a/src/cli/signal_util.h b/src/cli/signal_util.h
index eee3d2c05..51368a631 100644
--- a/src/cli/signal_util.h
+++ b/src/cli/signal_util.h
@@ -20,20 +20,25 @@
 
 #pragma once
 
-#include <glog/logging.h>
 #include <signal.h>
 
 #include <cpptrace/cpptrace.hpp>
 #include <cstddef>
 #include <iomanip>
+#include <sstream>
 
+#include "logging.h"
 #include "version_util.h"
 
 extern "C" inline void SegvHandler(int sig, [[maybe_unused]] siginfo_t *info, 
[[maybe_unused]] void *secret) {
   LOG(ERROR) << "Ooops! Apache Kvrocks " << PrintVersion << " got signal: " << 
strsignal(sig) << " (" << sig << ")";
   auto trace = cpptrace::generate_trace();
-  trace.print(LOG(ERROR));
+
+  std::string trace_str;
+  std::ostringstream os(trace_str);
+  trace.print(os);
   LOG(ERROR)
+      << os.str()
       << "It would be greatly appreciated if you could submit this crash to 
https://github.com/apache/kvrocks/issues "
          "along with the stacktrace above, logs and any relevant information.";
 
diff --git a/src/cluster/replication.cc b/src/cluster/replication.cc
index 78a0a02c2..3541cebb6 100644
--- a/src/cluster/replication.cc
+++ b/src/cluster/replication.cc
@@ -24,7 +24,6 @@
 #include <event2/buffer.h>
 #include <event2/bufferevent.h>
 #include <event2/event.h>
-#include <glog/logging.h>
 
 #include <algorithm>
 #include <atomic>
@@ -39,6 +38,7 @@
 #include "event_util.h"
 #include "fmt/format.h"
 #include "io_util.h"
+#include "logging.h"
 #include "rocksdb/write_batch.h"
 #include "rocksdb_crc32c.h"
 #include "scope_exit.h"
diff --git a/src/cluster/slot_import.h b/src/cluster/slot_import.h
index e3a5d32c4..a724edfe2 100644
--- a/src/cluster/slot_import.h
+++ b/src/cluster/slot_import.h
@@ -20,14 +20,13 @@
 
 #pragma once
 
-#include <glog/logging.h>
-
 #include <mutex>
 #include <string>
 #include <vector>
 
 #include "cluster_defs.h"
 #include "config/config.h"
+#include "logging.h"
 #include "server/server.h"
 #include "storage/redis_db.h"
 
diff --git a/src/cluster/slot_migrate.h b/src/cluster/slot_migrate.h
index 8b5cf493f..71107a5af 100644
--- a/src/cluster/slot_migrate.h
+++ b/src/cluster/slot_migrate.h
@@ -20,7 +20,6 @@
 
 #pragma once
 
-#include <glog/logging.h>
 #include <rocksdb/db.h>
 #include <rocksdb/status.h>
 #include <rocksdb/transaction_log.h>
@@ -33,6 +32,7 @@
 #include <vector>
 
 #include "batch_sender.h"
+#include "logging.h"
 #include "server/server.h"
 #include "status.h"
 #include "storage/redis_db.h"
diff --git a/src/commands/cmd_hll.cc b/src/commands/cmd_hll.cc
index 3af558cdc..35d63103e 100644
--- a/src/commands/cmd_hll.cc
+++ b/src/commands/cmd_hll.cc
@@ -35,7 +35,7 @@ class CommandPfAdd final : public Commander {
  public:
   Status Execute(engine::Context &ctx, Server *srv, Connection *conn, 
std::string *output) override {
     redis::HyperLogLog hll(srv->storage, conn->GetNamespace());
-    DCHECK_GE(args_.size(), 2u);
+    DCHECK(args_.size() >= 2u);
     std::vector<uint64_t> hashes(args_.size() - 2);
     for (size_t i = 2; i < args_.size(); i++) {
       hashes[i - 2] = redis::HyperLogLog::HllHash(args_[i]);
@@ -61,7 +61,7 @@ class CommandPfCount final : public Commander {
     uint64_t ret{};
     rocksdb::Status s;
     // The first argument is the command name, so we need to skip it.
-    DCHECK_GE(args_.size(), 2u);
+    DCHECK(args_.size() >= 2u);
 
     if (args_.size() > 2) {
       std::vector<Slice> keys(args_.begin() + 1, args_.end());
@@ -86,7 +86,7 @@ class CommandPfCount final : public Commander {
 class CommandPfMerge final : public Commander {
   Status Execute(engine::Context &ctx, Server *srv, Connection *conn, 
std::string *output) override {
     redis::HyperLogLog hll(srv->storage, conn->GetNamespace());
-    DCHECK_GT(args_.size(), 1u);
+    DCHECK(args_.size() > 1u);
     std::vector<Slice> src_user_keys(args_.begin() + 2, args_.end());
 
     auto s = hll.Merge(ctx, /*dest_user_key=*/args_[1], src_user_keys);
diff --git a/src/commands/commander.h b/src/commands/commander.h
index 8517b84f8..8b012ff27 100644
--- a/src/commands/commander.h
+++ b/src/commands/commander.h
@@ -22,7 +22,6 @@
 
 #include <event2/bufferevent.h>
 #include <event2/event.h>
-#include <glog/logging.h>
 #include <rocksdb/types.h>
 #include <rocksdb/utilities/backup_engine.h>
 
@@ -39,6 +38,7 @@
 
 #include "cluster/cluster_defs.h"
 #include "error_constants.h"
+#include "logging.h"
 #include "parse_util.h"
 #include "server/redis_reply.h"
 #include "status.h"
diff --git a/src/common/logging.h b/src/common/logging.h
new file mode 100644
index 000000000..4951ab9c5
--- /dev/null
+++ b/src/common/logging.h
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#pragma once
+
+#include <cstdlib>
+#include <iterator>
+
+#include "fmt/base.h"
+#include "fmt/ostream.h"
+#include "spdlog/common.h"
+#include "spdlog/spdlog.h"
+
+// just like std::source_location::current() in C++20 and 
__builtin_source_location(),
+// but works in lower version compilers (GCC and Clang)
+inline spdlog::source_loc CurrentLocation(const char *filename = 
__builtin_FILE(), int lineno = __builtin_LINE(),
+                                          const char *funcname = 
__builtin_FUNCTION()) {
+  return {filename, lineno, funcname};
+}
+
+template <typename... Args>
+struct FormatMessageWithLoc {
+  template <typename T>
+  FormatMessageWithLoc(T &&v, spdlog::source_loc loc = CurrentLocation())  // 
NOLINT
+      : fmt(std::forward<T>(v)), current_loc(loc) {}
+
+  spdlog::format_string_t<Args...> fmt;
+  spdlog::source_loc current_loc;
+};
+
+template <typename... Args>
+inline void debug(FormatMessageWithLoc<Args...> fmt, Args &&...args) {  // 
NOLINT
+  spdlog::default_logger_raw()->log(fmt.current_loc, spdlog::level::debug, 
fmt.fmt, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+inline void info(FormatMessageWithLoc<Args...> fmt, Args &&...args) {  // 
NOLINT
+  spdlog::default_logger_raw()->log(fmt.current_loc, spdlog::level::info, 
fmt.fmt, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+inline void warn(FormatMessageWithLoc<Args...> fmt, Args &&...args) {  // 
NOLINT
+  spdlog::default_logger_raw()->log(fmt.current_loc, spdlog::level::warn, 
fmt.fmt, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+inline void error(FormatMessageWithLoc<Args...> fmt, Args &&...args) {  // 
NOLINT
+  spdlog::default_logger_raw()->log(fmt.current_loc, spdlog::level::err, 
fmt.fmt, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+[[noreturn]] inline void fatal(FormatMessageWithLoc<Args...> fmt, Args 
&&...args) {  // NOLINT
+  spdlog::default_logger_raw()->log(fmt.current_loc, spdlog::level::critical, 
fmt.fmt, std::forward<Args>(args)...);
+  std::abort();
+}
+
+// This is a simulation of glog API, with a spdlog backend.
+// TODO: We use it as a transition from glog to spdlog,
+// and it will be removed when the migration is complete.
+template <spdlog::level::level_enum level>
+struct GlogInterface {  // NOLINT
+  explicit GlogInterface(spdlog::source_loc loc = CurrentLocation()) : 
loc(loc) {}
+
+  template <typename T>
+  friend GlogInterface &&operator<<(GlogInterface &&self, const T &v) {
+    fmt::format_to(std::back_inserter(self.os), "{}", fmt::streamed(v));
+    return std::move(self);
+  }
+
+  ~GlogInterface() {
+    spdlog::default_logger_raw()->log(loc, level, "{}", os);
+    if constexpr (level == spdlog::level::critical) {
+      std::abort();
+    }
+  }
+
+  std::string os;
+  spdlog::source_loc loc;
+};
+
+using GlogInterface_INFO = GlogInterface<spdlog::level::info>;       // NOLINT
+using GlogInterface_WARNING = GlogInterface<spdlog::level::warn>;    // NOLINT
+using GlogInterface_ERROR = GlogInterface<spdlog::level::err>;       // NOLINT
+using GlogInterface_FATAL = GlogInterface<spdlog::level::critical>;  // NOLINT
+
+inline constexpr bool GLOG_IN_DEBUG =
+#ifdef NDEBUG
+    false;
+#else
+    true;
+#endif
+
+// NOLINTNEXTLINE
+#define LOG(level) GlogInterface_##level()
+
+// NOLINTNEXTLINE
+#define DLOG(level) \
+  if constexpr (GLOG_IN_DEBUG) LOG(level)
+
+// NOLINTNEXTLINE
+#define CHECK(cond) \
+  if (!(cond)) LOG(FATAL) << "Check `" << #cond << "` failed. "
+
+// NOLINTNEXTLINE
+#define DCHECK(cond) \
+  if constexpr (GLOG_IN_DEBUG) CHECK(cond)
diff --git a/src/common/status.h b/src/common/status.h
index 823e5681c..e88e250c2 100644
--- a/src/common/status.h
+++ b/src/common/status.h
@@ -21,7 +21,6 @@
 #pragma once
 
 #include <fmt/format.h>
-#include <glog/logging.h>
 
 #include <algorithm>
 #include <memory>
@@ -29,6 +28,7 @@
 #include <type_traits>
 #include <utility>
 
+#include "logging.h"
 #include "rocksdb/status.h"
 #include "type_util.h"
 
diff --git a/src/config/config.cc b/src/config/config.cc
index 8728fabe4..e90d5c262 100644
--- a/src/config/config.cc
+++ b/src/config/config.cc
@@ -22,6 +22,7 @@
 
 #include <fmt/format.h>
 #include <rocksdb/env.h>
+#include <spdlog/spdlog.h>
 #include <strings.h>
 
 #include <cstddef>
@@ -59,13 +60,6 @@ const std::vector<ConfigEnum<SupervisedMode>> 
supervised_modes{
     {"systemd", kSupervisedSystemd},
 };
 
-const std::vector<ConfigEnum<int>> log_levels{
-    {"info", google::INFO},
-    {"warning", google::WARNING},
-    {"error", google::ERROR},
-    {"fatal", google::FATAL},
-};
-
 const std::vector<ConfigEnum<JsonStorageFormat>> json_storage_formats{{"json", 
JsonStorageFormat::JSON},
                                                                       {"cbor", 
JsonStorageFormat::CBOR}};
 
@@ -192,7 +186,7 @@ Config::Config() {
       {"dir", true, new StringField(&dir, kDefaultDir)},
       {"backup-dir", false, new StringField(&backup_dir, kDefaultBackupDir)},
       {"log-dir", true, new StringField(&log_dir, "")},
-      {"log-level", false, new EnumField<int>(&log_level, log_levels, 
google::INFO)},
+      {"log-level", false, new 
EnumField<spdlog::level::level_enum>(&log_level, log_levels, 
spdlog::level::info)},
       {"pidfile", true, new StringField(&pidfile, kDefaultPidfile)},
       {"max-io-mb", false, new IntField(&max_io_mb, 0, 0, INT_MAX)},
       {"max-bitmap-to-string-mb", false, new 
IntField(&max_bitmap_to_string_mb, 16, 0, INT_MAX)},
@@ -227,7 +221,7 @@ Config::Config() {
       {"migrate-batch-rate-limit-mb", false, new 
IntField(&migrate_batch_rate_limit_mb, 16, 0, INT_MAX)},
       {"unixsocket", true, new StringField(&unixsocket, "")},
       {"unixsocketperm", true, new OctalField(&unixsocketperm, 0777, 1, 
INT_MAX)},
-      {"log-retention-days", false, new IntField(&log_retention_days, -1, -1, 
INT_MAX)},
+      {"log-retention-days", true, new IntField(&log_retention_days, -1, -1, 
INT_MAX)},
       {"persist-cluster-nodes-enabled", false, new 
YesNoField(&persist_cluster_nodes_enabled, true)},
       {"redis-cursor-compatible", false, new 
YesNoField(&redis_cursor_compatible, true)},
       {"resp3-enabled", false, new YesNoField(&resp3_enabled, true)},
@@ -475,7 +469,7 @@ void Config::initFieldCallback() {
        [this]([[maybe_unused]] Server *srv, [[maybe_unused]] const std::string 
&k,
               [[maybe_unused]] const std::string &v) -> Status {
          db_dir = dir + "/db";
-         if (log_dir.empty()) log_dir = dir;
+         if (log_dir.empty()) log_dir = dir + ",stdout";
          checkpoint_dir = dir + "/checkpoint";
          sync_checkpoint_dir = dir + "/sync_checkpoint";
          backup_sync_dir = dir + "/backup_for_sync";
@@ -595,21 +589,7 @@ void Config::initFieldCallback() {
       {"log-level",
        [this](Server *srv, [[maybe_unused]] const std::string &k, 
[[maybe_unused]] const std::string &v) -> Status {
          if (!srv) return Status::OK();
-         FLAGS_minloglevel = log_level;
-         return Status::OK();
-       }},
-      {"log-retention-days",
-       [this](Server *srv, [[maybe_unused]] const std::string &k, 
[[maybe_unused]] const std::string &v) -> Status {
-         if (!srv) return Status::OK();
-         if (util::ToLower(log_dir) == "stdout") {
-           return {Status::NotOK, "can't set the 'log-retention-days' when the 
log dir is stdout"};
-         }
-
-         if (log_retention_days != -1) {
-           google::EnableLogCleaner(std::chrono::hours(24) * 
log_retention_days);
-         } else {
-           google::DisableLogCleaner();
-         }
+         spdlog::set_level(log_level);
          return Status::OK();
        }},
       {"persist-cluster-nodes-enabled",
@@ -900,8 +880,8 @@ Status Config::Load(const CLIOptions &opts) {
       line_num++;
     }
   } else {
-    std::cout << "WARNING: No config file specified, using the default 
configuration. "
-              << "In order to specify a config file use 'kvrocks -c 
/path/to/kvrocks.conf'" << std::endl;
+    std::cout << "WARNING: No config file specified, default configuration 
applied. "
+              << "In order to specify a config file, use `kvrocks -c 
/path/to/kvrocks.conf`." << std::endl;
   }
 
   for (const auto &opt : opts.cli_options) {
diff --git a/src/config/config.h b/src/config/config.h
index 6323b3b09..615dea649 100644
--- a/src/config/config.h
+++ b/src/config/config.h
@@ -31,6 +31,7 @@
 
 #include "config_type.h"
 #include "cron.h"
+#include "spdlog/common.h"
 #include "status.h"
 #include "storage/redis_metadata.h"
 
@@ -56,6 +57,11 @@ constexpr const uint32_t kDefaultPort = 6666;
 constexpr const char *kDefaultNamespace = "__namespace";
 constexpr int KVROCKS_MAX_LSM_LEVEL = 7;
 
+const std::vector<ConfigEnum<spdlog::level::level_enum>> log_levels{
+    {"debug", spdlog::level::debug}, {"info", spdlog::level::info},      
{"warning", spdlog::level::warn},
+    {"error", spdlog::level::err},   {"fatal", spdlog::level::critical},
+};
+
 enum class BlockCacheType { kCacheTypeLRU = 0, kCacheTypeHCC };
 
 struct CLIOptions {
@@ -91,7 +97,7 @@ struct Config {
 
   int workers = 0;
   int timeout = 0;
-  int log_level = 0;
+  spdlog::level::level_enum log_level = spdlog::level::info;
   int backlog = 511;
   int maxclients = 10000;
   int max_backup_to_keep = 1;
diff --git a/src/server/redis_connection.cc b/src/server/redis_connection.cc
index 0103b5910..765a0e517 100644
--- a/src/server/redis_connection.cc
+++ b/src/server/redis_connection.cc
@@ -18,7 +18,6 @@
  *
  */
 
-#include <glog/logging.h>
 #include <rocksdb/iostats_context.h>
 #include <rocksdb/perf_context.h>
 
@@ -28,6 +27,7 @@
 #include "commands/commander.h"
 #include "commands/error_constants.h"
 #include "fmt/format.h"
+#include "logging.h"
 #include "nonstd/span.hpp"
 #include "search/indexer.h"
 #include "server/redis_reply.h"
diff --git a/src/server/redis_request.cc b/src/server/redis_request.cc
index f729e85c2..a216416c6 100644
--- a/src/server/redis_request.cc
+++ b/src/server/redis_request.cc
@@ -20,7 +20,6 @@
 
 #include "redis_request.h"
 
-#include <glog/logging.h>
 #include <rocksdb/perf_context.h>
 
 #include <chrono>
@@ -29,6 +28,7 @@
 
 #include "cluster/redis_slot.h"
 #include "event_util.h"
+#include "logging.h"
 #include "parse_util.h"
 #include "redis_connection.h"
 #include "redis_reply.h"
diff --git a/src/server/server.cc b/src/server/server.cc
index 253a48029..2d69c5d88 100644
--- a/src/server/server.cc
+++ b/src/server/server.cc
@@ -20,7 +20,6 @@
 
 #include "server.h"
 
-#include <glog/logging.h>
 #include <rocksdb/convenience.h>
 #include <rocksdb/statistics.h>
 #include <sys/resource.h>
@@ -43,6 +42,7 @@
 #include "common/string_util.h"
 #include "config/config.h"
 #include "fmt/format.h"
+#include "logging.h"
 #include "redis_connection.h"
 #include "rocksdb/version.h"
 #include "storage/compaction_checker.h"
diff --git a/src/server/worker.cc b/src/server/worker.cc
index 3a5642c3b..2984c7686 100644
--- a/src/server/worker.cc
+++ b/src/server/worker.cc
@@ -21,7 +21,6 @@
 #include "worker.h"
 
 #include <event2/util.h>
-#include <glog/logging.h>
 #include <unistd.h>
 
 #include <stdexcept>
@@ -29,6 +28,7 @@
 
 #include "event2/bufferevent.h"
 #include "io_util.h"
+#include "logging.h"
 #include "scope_exit.h"
 #include "thread_util.h"
 
diff --git a/src/storage/batch_extractor.cc b/src/storage/batch_extractor.cc
index 968c70880..45fb70e59 100644
--- a/src/storage/batch_extractor.cc
+++ b/src/storage/batch_extractor.cc
@@ -20,9 +20,8 @@
 
 #include "batch_extractor.h"
 
-#include <glog/logging.h>
-
 #include "cluster/redis_slot.h"
+#include "logging.h"
 #include "parse_util.h"
 #include "server/redis_reply.h"
 #include "server/server.h"
diff --git a/src/storage/batch_indexer.h b/src/storage/batch_indexer.h
index eb8bb282f..b0aaafff7 100644
--- a/src/storage/batch_indexer.h
+++ b/src/storage/batch_indexer.h
@@ -36,9 +36,9 @@ class WriteBatchIndexer : public rocksdb::WriteBatch::Handler 
{
   explicit WriteBatchIndexer(engine::Storage* storage, 
rocksdb::WriteBatchWithIndex* dest_batch,
                              const rocksdb::Snapshot* snapshot)
       : storage_(storage), dest_batch_(dest_batch), snapshot_(snapshot) {
-    DCHECK_NE(storage, nullptr);
-    DCHECK_NE(dest_batch, nullptr);
-    DCHECK_NE(snapshot, nullptr);
+    DCHECK(storage != nullptr);
+    DCHECK(dest_batch != nullptr);
+    DCHECK(snapshot != nullptr);
   }
   explicit WriteBatchIndexer(engine::Context& ctx)
       : WriteBatchIndexer(ctx.storage, ctx.batch.get(), ctx.GetSnapshot()) {}
diff --git a/src/storage/compact_filter.cc b/src/storage/compact_filter.cc
index b79219ec4..809556c81 100644
--- a/src/storage/compact_filter.cc
+++ b/src/storage/compact_filter.cc
@@ -20,12 +20,11 @@
 
 #include "compact_filter.h"
 
-#include <glog/logging.h>
-
 #include <string>
 #include <utility>
 
 #include "db_util.h"
+#include "logging.h"
 #include "time_util.h"
 #include "types/redis_bitmap.h"
 
diff --git a/src/storage/compaction_checker.cc 
b/src/storage/compaction_checker.cc
index 4473d0f85..0bf90785a 100644
--- a/src/storage/compaction_checker.cc
+++ b/src/storage/compaction_checker.cc
@@ -20,8 +20,7 @@
 
 #include "compaction_checker.h"
 
-#include <glog/logging.h>
-
+#include "logging.h"
 #include "parse_util.h"
 #include "storage.h"
 #include "time_util.h"
diff --git a/src/storage/event_listener.h b/src/storage/event_listener.h
index 3e978c238..6f16d69b1 100644
--- a/src/storage/event_listener.h
+++ b/src/storage/event_listener.h
@@ -20,9 +20,9 @@
 
 #pragma once
 
-#include <glog/logging.h>
 #include <rocksdb/listener.h>
 
+#include "logging.h"
 #include "storage.h"
 
 class EventListener : public rocksdb::EventListener {
diff --git a/src/storage/rdb/rdb.cc b/src/storage/rdb/rdb.cc
index 95a9a757c..40b1bd3f6 100644
--- a/src/storage/rdb/rdb.cc
+++ b/src/storage/rdb/rdb.cc
@@ -20,11 +20,10 @@
 
 #include "rdb.h"
 
-#include <glog/logging.h>
-
 #include "common/encoding.h"
 #include "common/rdb_stream.h"
 #include "common/time_util.h"
+#include "logging.h"
 #include "rdb_intset.h"
 #include "rdb_listpack.h"
 #include "rdb_ziplist.h"
diff --git a/src/storage/scripting.cc b/src/storage/scripting.cc
index 6a93ad9f8..26bd34548 100644
--- a/src/storage/scripting.cc
+++ b/src/storage/scripting.cc
@@ -271,7 +271,7 @@ int RedisRegisterFunction(lua_State *lua) {
 
   // store the map from function name to library name
   auto *script_run_ctx = GetFromRegistry<ScriptRunCtx>(lua, 
REGISTRY_SCRIPT_RUN_CTX_NAME);
-  CHECK_NOTNULL(script_run_ctx);
+  CHECK(script_run_ctx != nullptr);
 
   auto s = script_run_ctx->conn->GetServer()->FunctionSetLib(name, libname);
   if (!s) {
@@ -723,7 +723,7 @@ int RedisPCallCommand(lua_State *lua) { return 
RedisGenericCommand(lua, 0); }
 // so the function need to be refactored
 int RedisGenericCommand(lua_State *lua, int raise_error) {
   auto *script_run_ctx = GetFromRegistry<ScriptRunCtx>(lua, 
REGISTRY_SCRIPT_RUN_CTX_NAME);
-  CHECK_NOTNULL(script_run_ctx);
+  CHECK(script_run_ctx != nullptr);
 
   int argc = lua_gettop(lua);
   if (argc == 0) {
@@ -909,7 +909,7 @@ int RedisReturnSingleFieldTable(lua_State *lua, const char 
*field) {
 
 int RedisSetResp(lua_State *lua) {
   auto *script_run_ctx = GetFromRegistry<ScriptRunCtx>(lua, 
REGISTRY_SCRIPT_RUN_CTX_NAME);
-  CHECK_NOTNULL(script_run_ctx);
+  CHECK(script_run_ctx != nullptr);
   auto *conn = script_run_ctx->conn;
   auto *srv = conn->GetServer();
 
diff --git a/src/storage/scripting.h b/src/storage/scripting.h
index a7550a0ec..3e7254528 100644
--- a/src/storage/scripting.h
+++ b/src/storage/scripting.h
@@ -181,7 +181,7 @@ T *GetFromRegistry(lua_State *lua, const char *name) {
   CHECK(lua_islightuserdata(lua, -1));
   auto *ptr = static_cast<T *>(lua_touserdata(lua, -1));
 
-  CHECK_NOTNULL(ptr);
+  CHECK(ptr != nullptr);
 
   // pops the value
   lua_pop(lua, 1);
diff --git a/src/storage/storage.cc b/src/storage/storage.cc
index 4738c1a3f..64820907e 100644
--- a/src/storage/storage.cc
+++ b/src/storage/storage.cc
@@ -22,7 +22,6 @@
 
 #include <event2/buffer.h>
 #include <fcntl.h>
-#include <glog/logging.h>
 #include <rocksdb/convenience.h>
 #include <rocksdb/env.h>
 #include <rocksdb/filter_policy.h>
@@ -40,6 +39,7 @@
 #include "db_util.h"
 #include "event_listener.h"
 #include "event_util.h"
+#include "logging.h"
 #include "redis_db.h"
 #include "redis_metadata.h"
 #include "rocksdb/cache.h"
@@ -606,8 +606,8 @@ rocksdb::Status Storage::Get(engine::Context &ctx, const 
rocksdb::ReadOptions &o
                              rocksdb::ColumnFamilyHandle *column_family, const 
rocksdb::Slice &key,
                              std::string *value) {
   if (ctx.txn_context_enabled) {
-    DCHECK_NE(options.snapshot, nullptr);
-    DCHECK_EQ(ctx.GetSnapshot()->GetSequenceNumber(), 
options.snapshot->GetSequenceNumber());
+    DCHECK(options.snapshot != nullptr);
+    DCHECK(ctx.GetSnapshot()->GetSequenceNumber() == 
options.snapshot->GetSequenceNumber());
   }
   rocksdb::Status s;
   if (is_txn_mode_ && txn_write_batch_->GetWriteBatch()->Count() > 0) {
@@ -631,8 +631,8 @@ rocksdb::Status Storage::Get(engine::Context &ctx, const 
rocksdb::ReadOptions &o
                              rocksdb::ColumnFamilyHandle *column_family, const 
rocksdb::Slice &key,
                              rocksdb::PinnableSlice *value) {
   if (ctx.txn_context_enabled) {
-    DCHECK_NE(options.snapshot, nullptr);
-    DCHECK_EQ(ctx.GetSnapshot()->GetSequenceNumber(), 
options.snapshot->GetSequenceNumber());
+    DCHECK(options.snapshot != nullptr);
+    DCHECK(ctx.GetSnapshot()->GetSequenceNumber() == 
options.snapshot->GetSequenceNumber());
   }
   rocksdb::Status s;
   if (is_txn_mode_ && txn_write_batch_->GetWriteBatch()->Count() > 0) {
@@ -664,8 +664,8 @@ void Storage::recordKeyspaceStat(const 
rocksdb::ColumnFamilyHandle *column_famil
 rocksdb::Iterator *Storage::NewIterator(engine::Context &ctx, const 
rocksdb::ReadOptions &options,
                                         rocksdb::ColumnFamilyHandle 
*column_family) {
   if (ctx.txn_context_enabled) {
-    DCHECK_NE(options.snapshot, nullptr);
-    DCHECK_EQ(ctx.GetSnapshot()->GetSequenceNumber(), 
options.snapshot->GetSequenceNumber());
+    DCHECK(options.snapshot != nullptr);
+    DCHECK(ctx.GetSnapshot()->GetSequenceNumber() == 
options.snapshot->GetSequenceNumber());
   }
   auto iter = db_->NewIterator(options, column_family);
   if (is_txn_mode_ && txn_write_batch_->GetWriteBatch()->Count() > 0) {
@@ -680,8 +680,8 @@ void Storage::MultiGet(engine::Context &ctx, const 
rocksdb::ReadOptions &options
                        rocksdb::ColumnFamilyHandle *column_family, const 
size_t num_keys, const rocksdb::Slice *keys,
                        rocksdb::PinnableSlice *values, rocksdb::Status 
*statuses) {
   if (ctx.txn_context_enabled) {
-    DCHECK_NE(options.snapshot, nullptr);
-    DCHECK_EQ(ctx.GetSnapshot()->GetSequenceNumber(), 
options.snapshot->GetSequenceNumber());
+    DCHECK(options.snapshot != nullptr);
+    DCHECK(ctx.GetSnapshot()->GetSequenceNumber() == 
options.snapshot->GetSequenceNumber());
   }
   if (is_txn_mode_ && txn_write_batch_->GetWriteBatch()->Count() > 0) {
     txn_write_batch_->MultiGetFromBatchAndDB(db_.get(), options, 
column_family, num_keys, keys, values, statuses,
diff --git a/src/types/hyperloglog.cc b/src/types/hyperloglog.cc
index e2d0b46c6..509c867bf 100644
--- a/src/types/hyperloglog.cc
+++ b/src/types/hyperloglog.cc
@@ -100,7 +100,7 @@ DenseHllResult ExtractDenseHllResult(uint64_t hash) {
    * This may sound like inefficient, but actually in the average case
    * there are high probabilities to find a 1 after a few iterations. */
   uint32_t index = hash & kHyperLogLogRegisterCountMask; /* Register index. */
-  DCHECK_LT(index, kHyperLogLogRegisterCount);
+  DCHECK(index < kHyperLogLogRegisterCount);
   hash >>= kHyperLogLogRegisterCountPow; /* Remove bits used to address the 
register. */
   hash |= (static_cast<uint64_t>(1U) << kHyperLogLogHashBitCount);
   uint8_t ctz = __builtin_ctzll(hash) + 1;
@@ -165,7 +165,7 @@ void HllMerge(std::vector<std::string> *dest_registers, 
const std::vector<nonstd
       continue;
     }
     if (dest_segment->empty()) {
-      DCHECK_EQ(kHyperLogLogSegmentBytes, src_segment.size());
+      DCHECK(kHyperLogLogSegmentBytes == src_segment.size());
       *dest_segment = std::string(src_segment.begin(), src_segment.end());
       continue;
     }
diff --git a/src/types/redis_bitmap.cc b/src/types/redis_bitmap.cc
index d7216d7df..fb9d9d102 100644
--- a/src/types/redis_bitmap.cc
+++ b/src/types/redis_bitmap.cc
@@ -404,7 +404,7 @@ rocksdb::Status Bitmap::BitPos(engine::Context &ctx, const 
Slice &user_key, bool
     }
     size_t stop_byte_in_segment = pin_value.size();
     if (i == stop_segment_index) {
-      DCHECK_LE((u_stop / to_bit_factor) % kBitmapSegmentBytes + 1, 
pin_value.size());
+      DCHECK((u_stop / to_bit_factor) % kBitmapSegmentBytes + 1 <= 
pin_value.size());
       stop_byte_in_segment = (u_stop / to_bit_factor) % kBitmapSegmentBytes + 
1;
       byte_with_bit_stop = stop_byte_in_segment;
     }
diff --git a/src/types/redis_bitmap_string.cc b/src/types/redis_bitmap_string.cc
index b8e161b65..71a898f37 100644
--- a/src/types/redis_bitmap_string.cc
+++ b/src/types/redis_bitmap_string.cc
@@ -20,11 +20,10 @@
 
 #include "redis_bitmap_string.h"
 
-#include <glog/logging.h>
-
 #include <cstdint>
 
 #include "common/bit_util.h"
+#include "logging.h"
 #include "redis_string.h"
 #include "server/redis_reply.h"
 #include "storage/redis_metadata.h"
diff --git a/src/types/redis_hyperloglog.cc b/src/types/redis_hyperloglog.cc
index b7b2197f2..f7d2b12ee 100644
--- a/src/types/redis_hyperloglog.cc
+++ b/src/types/redis_hyperloglog.cc
@@ -140,7 +140,7 @@ rocksdb::Status HyperLogLog::Add(engine::Context &ctx, 
const Slice &user_key,
         &entry);
     if (!s.ok()) return s;
     DCHECK(entry != nullptr);
-    DCHECK_EQ(kHyperLogLogSegmentBytes, entry->data.size());
+    DCHECK(kHyperLogLogSegmentBytes == entry->data.size());
     auto *segment_data = reinterpret_cast<uint8_t *>(entry->data.data());
     uint8_t old_count = HllDenseGetRegister(segment_data, 
register_index_in_segment);
     if (dense_hll_result.hll_trailing_zero > old_count) {
@@ -185,7 +185,7 @@ rocksdb::Status HyperLogLog::Count(engine::Context &ctx, 
const Slice &user_key,
   if (!s.ok()) {
     return s;
   }
-  DCHECK_EQ(kHyperLogLogSegmentCount, registers.size());
+  DCHECK(kHyperLogLogSegmentCount == registers.size());
   std::vector<nonstd::span<const uint8_t>> register_segments = 
TransformToSpan(registers);
   *ret = HllDenseEstimate(register_segments);
   return rocksdb::Status::OK();
@@ -193,7 +193,7 @@ rocksdb::Status HyperLogLog::Count(engine::Context &ctx, 
const Slice &user_key,
 
 rocksdb::Status HyperLogLog::mergeUserKeys(engine::Context &ctx, const 
std::vector<Slice> &user_keys,
                                            std::vector<std::string> 
*register_segments) {
-  DCHECK_GE(user_keys.size(), static_cast<size_t>(1));
+  DCHECK(user_keys.size() >= static_cast<size_t>(1));
 
   std::string first_ns_key = AppendNamespacePrefix(user_keys[0]);
   rocksdb::Status s = getRegisters(ctx, first_ns_key, register_segments);
@@ -212,8 +212,8 @@ rocksdb::Status HyperLogLog::mergeUserKeys(engine::Context 
&ctx, const std::vect
     std::vector<rocksdb::PinnableSlice> source_registers;
     s = getRegisters(ctx, source_key, &source_registers);
     if (!s.ok()) return s;
-    DCHECK_EQ(kHyperLogLogSegmentCount, source_registers.size());
-    DCHECK_EQ(kHyperLogLogSegmentCount, register_segments->size());
+    DCHECK(kHyperLogLogSegmentCount == source_registers.size());
+    DCHECK(kHyperLogLogSegmentCount == register_segments->size());
     std::vector<nonstd::span<const uint8_t>> source_register_span = 
TransformToSpan(source_registers);
     HllMerge(register_segments, source_register_span);
   }
@@ -221,7 +221,7 @@ rocksdb::Status HyperLogLog::mergeUserKeys(engine::Context 
&ctx, const std::vect
 }
 
 rocksdb::Status HyperLogLog::CountMultiple(engine::Context &ctx, const 
std::vector<Slice> &user_key, uint64_t *ret) {
-  DCHECK_GT(user_key.size(), static_cast<size_t>(1));
+  DCHECK(user_key.size() > static_cast<size_t>(1));
   std::vector<std::string> register_segments;
   auto s = mergeUserKeys(ctx, user_key, &register_segments);
   if (!s.ok()) return s;
diff --git a/src/types/sample_helper.h b/src/types/sample_helper.h
index 1f1618063..07ef234ad 100644
--- a/src/types/sample_helper.h
+++ b/src/types/sample_helper.h
@@ -37,7 +37,7 @@ rocksdb::Status ExtractRandMemberFromSet(bool unique, size_t 
count, const GetAll
   if (!s.ok() || samples.empty()) return s;
 
   size_t all_element_size = samples.size();
-  DCHECK_GE(all_element_size, 1U);
+  DCHECK(all_element_size >= 1U);
   elements->reserve(std::min(all_element_size, count));
 
   if (!unique || count == 1) {
diff --git a/src/types/tdigest.cc b/src/types/tdigest.cc
index a51967a4f..af3a71bbb 100644
--- a/src/types/tdigest.cc
+++ b/src/types/tdigest.cc
@@ -26,13 +26,13 @@ refer to 
https://github.com/apache/arrow/blob/27bbd593625122a4a25d9471c8aaf5df54
 #include "tdigest.h"
 
 #include <fmt/format.h>
-#include <glog/logging.h>
 
 #include <algorithm>
 #include <iterator>
 #include <queue>
 
 #include "common/status.h"
+#include "logging.h"
 
 namespace {
 // scale function K1
@@ -281,7 +281,7 @@ class TDigestImpl {
         break;
       }
     }
-    DCHECK_LT(ci, td.size());
+    DCHECK(ci < td.size());
 
     // deviation of index from the centroid center
     double diff = index + td[ci].weight / 2 - weight_sum;
@@ -297,9 +297,9 @@ class TDigestImpl {
     if (diff > 0) {
       if (ci_right == td.size() - 1) {
         // index larger than center of last bin
-        DCHECK_EQ(weight_sum, total_weight_);
+        DCHECK(weight_sum == total_weight_);
         const Centroid* c = &td[ci_right];
-        DCHECK_GE(c->weight, 2);
+        DCHECK(c->weight >= 2);
         return Lerp(c->mean, max_, diff / (c->weight / 2));
       }
       ++ci_right;
@@ -307,7 +307,7 @@ class TDigestImpl {
       if (ci_left == 0) {
         // index smaller than center of first bin
         const Centroid* c = &td[0];
-        DCHECK_GE(c->weight, 2);
+        DCHECK(c->weight >= 2);
         return Lerp(min_, c->mean, index / (c->weight / 2));
       }
       --ci_left;
diff --git a/src/types/tdigest.h b/src/types/tdigest.h
index 6e8b03825..dd6d39e5b 100644
--- a/src/types/tdigest.h
+++ b/src/types/tdigest.h
@@ -124,7 +124,7 @@ inline StatusOr<double> TDigestQuantile(TD&& td, double q) {
     if (ci_right == td.End()) {
       // index larger than center of last bin
       auto c = GET_OR_RET(ci_left->GetCentroid());
-      DCHECK_GE(c.weight, 2);
+      DCHECK(c.weight >= 2);
       return Lerp(c.mean, td.Max(), diff / (c.weight / 2));
     }
     ci_right->Next();
@@ -132,7 +132,7 @@ inline StatusOr<double> TDigestQuantile(TD&& td, double q) {
     if (ci_left == td.Begin()) {
       // index smaller than center of first bin
       auto c = GET_OR_RET(ci_left->GetCentroid());
-      DCHECK_GE(c.weight, 2);
+      DCHECK(c.weight >= 2);
       return Lerp(td.Min(), c.mean, index / (c.weight / 2));
     }
     ci_left->Prev();
diff --git a/tests/cppunit/main.cc b/tests/cppunit/main.cc
index ae4c4a3dc..74aa1bd18 100644
--- a/tests/cppunit/main.cc
+++ b/tests/cppunit/main.cc
@@ -26,7 +26,5 @@ Server *GetServer() { return nullptr; }
 
 int main(int argc, char **argv) {
   testing::InitGoogleTest(&argc, argv);
-  google::InitGoogleLogging(argv[0]);
-  google::InstallFailureSignalHandler();
   return RUN_ALL_TESTS();
 }
diff --git a/tests/cppunit/types/tdigest_test.cc 
b/tests/cppunit/types/tdigest_test.cc
index 4bc8ae039..994f7b1bc 100644
--- a/tests/cppunit/types/tdigest_test.cc
+++ b/tests/cppunit/types/tdigest_test.cc
@@ -21,7 +21,6 @@
 #include "types/tdigest.h"
 
 #include <fmt/format.h>
-#include <glog/logging.h>
 #include <gtest/gtest.h>
 
 #include <algorithm>
@@ -37,6 +36,7 @@
 #include <string>
 #include <vector>
 
+#include "logging.h"
 #include "storage/redis_metadata.h"
 #include "test_base.h"
 #include "time_util.h"
diff --git a/tests/gocase/unit/connection/connection_test.go 
b/tests/gocase/unit/connection/connection_test.go
index 267d23da0..22a76c884 100644
--- a/tests/gocase/unit/connection/connection_test.go
+++ b/tests/gocase/unit/connection/connection_test.go
@@ -35,6 +35,7 @@ func TestConnection(t *testing.T) {
        t.Run("HTTP requests will be dropped", func(t *testing.T) {
                _, err := http.Get(fmt.Sprintf("http://%s";, srv.HostPort())) 
//nolint:bodyclose
                require.Error(t, err)
+               srv.Restart()
                require.True(t, srv.LogFileMatches(t, "HTTP request.*Connection 
aborted"), "should contain HTTP drop log")
 
                c := srv.NewTCPClient()
diff --git a/tests/gocase/unit/log/logclean_test.go 
b/tests/gocase/unit/log/logclean_test.go
index e4f073638..cf5e9247b 100644
--- a/tests/gocase/unit/log/logclean_test.go
+++ b/tests/gocase/unit/log/logclean_test.go
@@ -28,9 +28,11 @@ import (
        "github.com/stretchr/testify/require"
 )
 
-const infoLogFileNamePart = ".INFO."
+const infoLogFileNamePart = ".log"
 
 func TestInfoLogClean(t *testing.T) {
+       t.Skip("the new log system does not support zero log-retention-days for 
immediately removing all log files")
+
        logDir := "/tmp/kvrocks/logfile"
        require.NoError(t, os.RemoveAll(logDir))
        require.NoError(t, os.MkdirAll(logDir, os.ModePerm))
diff --git a/tests/gocase/util/server.go b/tests/gocase/util/server.go
index 4b9e0428e..2e020c15e 100644
--- a/tests/gocase/util/server.go
+++ b/tests/gocase/util/server.go
@@ -73,7 +73,9 @@ func (s *KvrocksServer) TLSAddr() string {
 
 func (s *KvrocksServer) LogFileMatches(t testing.TB, pattern string) bool {
        dir := s.configs["dir"]
-       content, err := os.ReadFile(dir + "/kvrocks.INFO")
+       now := time.Now()
+       filename := dir + fmt.Sprintf("/kvrocks_%d-%02d-%02d.log", now.Year(), 
now.Month(), now.Day())
+       content, err := os.ReadFile(filename)
        require.NoError(t, err)
        p := regexp.MustCompile(pattern)
        return p.Match(content)
diff --git a/utils/kvrocks2redis/config.cc b/utils/kvrocks2redis/config.cc
index ac6f24edd..8d5fbb882 100644
--- a/utils/kvrocks2redis/config.cc
+++ b/utils/kvrocks2redis/config.cc
@@ -31,11 +31,14 @@
 
 #include "config/config.h"
 #include "config/config_util.h"
+#include "spdlog/common.h"
 #include "string_util.h"
 
 namespace kvrocks2redis {
 
 static constexpr const char *kLogLevels[] = {"info", "warning", "error", 
"fatal"};
+static constexpr spdlog::level::level_enum kLogLevelVals[] = 
{spdlog::level::info, spdlog::level::warn,
+                                                              
spdlog::level::err, spdlog::level::critical};
 static constexpr size_t kNumLogLevel = std::size(kLogLevels);
 
 StatusOr<bool> Config::yesnotoi(const std::string &input) {
@@ -81,7 +84,7 @@ Status Config::parseConfigFromString(const std::string 
&input) {
   } else if (size == 1 && key == "log-level") {
     for (size_t i = 0; i < kNumLogLevel; i++) {
       if (util::ToLower(args[0]) == kLogLevels[i]) {
-        loglevel = static_cast<int>(i);
+        loglevel = kLogLevelVals[i];
         break;
       }
     }
diff --git a/utils/kvrocks2redis/config.h b/utils/kvrocks2redis/config.h
index a9a4bf3bc..21fabafd0 100644
--- a/utils/kvrocks2redis/config.h
+++ b/utils/kvrocks2redis/config.h
@@ -24,6 +24,7 @@
 #include <string>
 #include <vector>
 
+#include "spdlog/common.h"
 #include "status.h"
 
 namespace kvrocks2redis {
@@ -37,7 +38,7 @@ struct RedisServer {
 
 struct Config {
  public:
-  int loglevel = 0;
+  spdlog::level::level_enum loglevel = spdlog::level::info;
   bool daemonize = false;
 
   std::string data_dir = "./data";
diff --git a/utils/kvrocks2redis/main.cc b/utils/kvrocks2redis/main.cc
index 8522fd096..32ad12458 100644
--- a/utils/kvrocks2redis/main.cc
+++ b/utils/kvrocks2redis/main.cc
@@ -21,10 +21,10 @@
 #include <event2/thread.h>
 #include <fcntl.h>
 #include <getopt.h>
-#include <glog/logging.h>
 #include <sys/stat.h>
 
 #include <csignal>
+#include <memory>
 
 #include "cli/daemon_util.h"
 #include "cli/pid_util.h"
@@ -32,8 +32,13 @@
 #include "config.h"
 #include "config/config.h"
 #include "io_util.h"
+#include "logging.h"
 #include "parser.h"
 #include "redis_writer.h"
+#include "spdlog/logger.h"
+#include "spdlog/sinks/daily_file_sink.h"
+#include "spdlog/sinks/stdout_color_sinks.h"
+#include "spdlog/spdlog.h"
 #include "storage/storage.h"
 #include "sync.h"
 #include "version.h"
@@ -78,17 +83,18 @@ static Options ParseCommandLineOptions(int argc, char 
**argv) {
   return opts;
 }
 
-static void InitGoogleLog(const kvrocks2redis::Config *config) {
-  FLAGS_minloglevel = config->loglevel;
-  FLAGS_max_log_size = 100;
-  FLAGS_logbufsecs = 0;
-  FLAGS_log_dir = config->output_dir;
+static void InitSpdlog(const kvrocks2redis::Config &config) {
+  std::vector<spdlog::sink_ptr> sinks = {
+      std::make_shared<spdlog::sinks::daily_file_sink_mt>(config.output_dir + 
"/kvrocks2redis.log", 0, 0),
+      std::make_shared<spdlog::sinks::stdout_color_sink_mt>()};
+  auto logger = std::make_shared<spdlog::logger>("kvrocks2redis", 
sinks.begin(), sinks.end());
+  logger->set_level(config.loglevel);
+  spdlog::set_default_logger(logger);
 }
 
 Server *GetServer() { return nullptr; }
 
 int main(int argc, char *argv[]) {
-  google::InitGoogleLogging("kvrocks2redis");
   evthread_use_pthreads();
 
   signal(SIGPIPE, SIG_IGN);
@@ -105,7 +111,7 @@ int main(int argc, char *argv[]) {
     exit(1);
   }
 
-  InitGoogleLog(&config);
+  InitSpdlog(config);
   LOG(INFO) << "kvrocks2redis " << PrintVersion;
 
   if (config.daemonize) Daemonize();
diff --git a/utils/kvrocks2redis/parser.cc b/utils/kvrocks2redis/parser.cc
index 22a5cd614..e8b186786 100644
--- a/utils/kvrocks2redis/parser.cc
+++ b/utils/kvrocks2redis/parser.cc
@@ -20,13 +20,13 @@
 
 #include "parser.h"
 
-#include <glog/logging.h>
 #include <rocksdb/write_batch.h>
 
 #include <memory>
 
 #include "cluster/redis_slot.h"
 #include "db_util.h"
+#include "logging.h"
 #include "server/redis_reply.h"
 #include "storage/redis_metadata.h"
 #include "types/redis_string.h"
diff --git a/utils/kvrocks2redis/redis_writer.h 
b/utils/kvrocks2redis/redis_writer.h
index 9068bc8a3..3c89d55d0 100644
--- a/utils/kvrocks2redis/redis_writer.h
+++ b/utils/kvrocks2redis/redis_writer.h
@@ -20,13 +20,12 @@
 
 #pragma once
 
-#include <glog/logging.h>
-
 #include <map>
 #include <string>
 #include <thread>
 #include <vector>
 
+#include "logging.h"
 #include "writer.h"
 
 class RedisWriter : public Writer {
diff --git a/utils/kvrocks2redis/sync.cc b/utils/kvrocks2redis/sync.cc
index 61303a0ae..78c95e43d 100644
--- a/utils/kvrocks2redis/sync.cc
+++ b/utils/kvrocks2redis/sync.cc
@@ -23,7 +23,6 @@
 #include <event2/buffer.h>
 #include <event2/bufferevent.h>
 #include <fcntl.h>
-#include <glog/logging.h>
 #include <rocksdb/write_batch.h>
 #include <unistd.h>
 
@@ -32,6 +31,7 @@
 
 #include "event_util.h"
 #include "io_util.h"
+#include "logging.h"
 #include "server/redis_reply.h"
 
 void SendStringToEvent(bufferevent *bev, const std::string &data) {

Reply via email to