Skip to content

Commit

Permalink
Merge pull request #155 from project-tsurugi/wip/i_1003
Browse files Browse the repository at this point in the history
Wip/i 1003
  • Loading branch information
t-horikawa authored Oct 31, 2024
2 parents 79a4678 + 3c219a2 commit 7344765
Show file tree
Hide file tree
Showing 12 changed files with 935 additions and 7 deletions.
14 changes: 12 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ file(GLOB SERVER_SOURCES
"tateyama/configuration/*.cpp"
)
if (ENABLE_ALTIMETER)
set(SERVER_SOURCES ${SERVER_SOURCES} "tateyama/altimeter/altimeter_helper.cpp")
list(APPEND SERVER_SOURCES "tateyama/altimeter/altimeter_helper.cpp")
endif ()

add_executable(server
Expand Down Expand Up @@ -96,7 +96,14 @@ set(ProtoFiles
${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/session/response.proto
${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/session/diagnostic.proto
)

if (ENABLE_ALTIMETER)
list(APPEND ProtoFiles
${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/altimeter/request.proto
${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/altimeter/response.proto
${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/altimeter/common.proto
)
endif()

# By default, PROTOBUF_GENERATE_CPP generates file path for .pb.cc as if they are in the same directory.
# Work-around this with PROTOBUF_GENERATE_CPP_APPEND_PATH
set(PROTOBUF_GENERATE_CPP_APPEND_PATH OFF)
Expand All @@ -114,6 +121,9 @@ file(GLOB TGCTL_SOURCES
"tateyama/session/*.cpp"
"tateyama/metrics/*.cpp"
)
if (ENABLE_ALTIMETER)
list(APPEND TGCTL_SOURCES "tateyama/altimeter/altimeter.cpp")
endif ()

set_source_files_properties(
${GENERATED_PROTO_SRCS}
Expand Down
197 changes: 197 additions & 0 deletions src/tateyama/altimeter/altimeter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
* Copyright 2022-2024 Project Tsurugi.
*
* Licensed 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.
*/

#include <string>
#include <string_view>

#include <gflags/gflags.h>

#include "tateyama/authentication/authentication.h"
#include <tateyama/proto/altimeter/request.pb.h>
#include <tateyama/proto/altimeter/response.pb.h>
#include <tateyama/proto/altimeter/common.pb.h>

#include <tateyama/transport/transport.h>
#include <tateyama/monitor/monitor.h>
#include "altimeter.h"

DECLARE_string(monitor);

namespace tateyama::altimeter {

template <class T>
static monitor::reason post_processing(std::optional<T>& response_opt, const std::string_view sub_command) {
if (response_opt) {
auto response = response_opt.value();

switch(response.result_case()) {
case T::ResultCase::kSuccess:
return monitor::reason::absent;
case T::ResultCase::kError:
std::cerr << "altimeter " << sub_command << " error: " << response.error().message() << std::endl;
return monitor::reason::server;
default:
std::cerr << "altimeter " << sub_command << " returns illegal response" << std::endl;
return monitor::reason::unknown;
}
}
std::cerr << "altimeter " << sub_command << " returns nullopt" << std::endl;
return monitor::reason::server;
}

tgctl::return_code set_enabled(const std::string& type, bool enabled) {
std::unique_ptr<monitor::monitor> monitor_output{};

if (!FLAGS_monitor.empty()) {
monitor_output = std::make_unique<monitor::monitor>(FLAGS_monitor);
monitor_output->start();
}

auto reason = monitor::reason::absent;
authentication::auth_options();
try {
auto transport = std::make_unique<tateyama::bootstrap::wire::transport>(tateyama::framework::service_id_altimeter);
::tateyama::proto::altimeter::request::Request request{};
auto* mutable_configure = request.mutable_configure();
if (type == "event") {
mutable_configure->mutable_event_log()->set_enabled(enabled);
} else if(type == "audit") {
mutable_configure->mutable_audit_log()->set_enabled(enabled);
} else {
throw std::runtime_error("illegal type for altimeter set_enabled");
}
auto response_opt = transport->send<::tateyama::proto::altimeter::response::Configure>(request);
request.clear_configure();

reason = post_processing<::tateyama::proto::altimeter::response::Configure>(response_opt, "set_enabled");
} catch (std::runtime_error &ex) {
std::cerr << "could not connect to database with name '" << tateyama::bootstrap::wire::transport::database_name() << "'" << std::endl;
reason = monitor::reason::connection;
}

if (monitor_output) {
monitor_output->finish(reason);
}
return (reason == monitor::reason::absent) ? tgctl::return_code::err : tgctl::return_code::err; // NOLINT(misc-redundant-expression)
}

tgctl::return_code set_log_level(const std::string& type, const std::string& level) {
std::unique_ptr<monitor::monitor> monitor_output{};

if (!FLAGS_monitor.empty()) {
monitor_output = std::make_unique<monitor::monitor>(FLAGS_monitor);
monitor_output->start();
}

auto reason = monitor::reason::absent;
authentication::auth_options();
try {
auto transport = std::make_unique<tateyama::bootstrap::wire::transport>(tateyama::framework::service_id_altimeter);
::tateyama::proto::altimeter::request::Request request{};
auto* mutable_configure = request.mutable_configure();
std::uint64_t l = std::stoul(level);
if (type == "event") {
mutable_configure->mutable_event_log()->set_level(l);
} else if(type == "audit") {
mutable_configure->mutable_audit_log()->set_level(l);
} else {
throw std::runtime_error("illegal type for altimeter set_log_level");
}
auto response_opt = transport->send<::tateyama::proto::altimeter::response::Configure>(request);
request.clear_configure();

reason = post_processing<::tateyama::proto::altimeter::response::Configure>(response_opt, "set_log_level");
} catch (std::runtime_error &ex) {
std::cerr << "could not connect to database with name '" << tateyama::bootstrap::wire::transport::database_name() << "'" << std::endl;
reason = monitor::reason::connection;
}

if (monitor_output) {
monitor_output->finish(reason);
}
return (reason == monitor::reason::absent) ? tgctl::return_code::err : tgctl::return_code::err; // NOLINT(misc-redundant-expression)
}

tgctl::return_code set_statement_duration(const std::string& value) {
std::unique_ptr<monitor::monitor> monitor_output{};

if (!FLAGS_monitor.empty()) {
monitor_output = std::make_unique<monitor::monitor>(FLAGS_monitor);
monitor_output->start();
}

auto reason = monitor::reason::absent;
authentication::auth_options();
try {
auto transport = std::make_unique<tateyama::bootstrap::wire::transport>(tateyama::framework::service_id_altimeter);
::tateyama::proto::altimeter::request::Request request{};
auto* mutable_configure = request.mutable_configure();
std::uint64_t v = std::stoul(value);
mutable_configure->mutable_event_log()->set_statement_duration(v);
auto response_opt = transport->send<::tateyama::proto::altimeter::response::Configure>(request);
request.clear_configure();

reason = post_processing<::tateyama::proto::altimeter::response::Configure>(response_opt, "set_statement_duration");
} catch (std::runtime_error &ex) {
std::cerr << "could not connect to database with name '" << tateyama::bootstrap::wire::transport::database_name() << "'" << std::endl;
reason = monitor::reason::connection;
}

if (monitor_output) {
monitor_output->finish(reason);
}
return (reason == monitor::reason::absent) ? tgctl::return_code::err : tgctl::return_code::err; // NOLINT(misc-redundant-expression)
}

tgctl::return_code rotate(const std::string& type) {
std::unique_ptr<monitor::monitor> monitor_output{};

if (!FLAGS_monitor.empty()) {
monitor_output = std::make_unique<monitor::monitor>(FLAGS_monitor);
monitor_output->start();
}

auto reason = monitor::reason::absent;
authentication::auth_options();
try {
auto transport = std::make_unique<tateyama::bootstrap::wire::transport>(tateyama::framework::service_id_altimeter);
::tateyama::proto::altimeter::request::Request request{};
auto* mutable_log_rotate = request.mutable_log_rotate();
::tateyama::proto::altimeter::common::LogCategory category{};
if (type == "event") {
category = ::tateyama::proto::altimeter::common::LogCategory::EVENT;
} else if(type == "audit") {
category = ::tateyama::proto::altimeter::common::LogCategory::AUDIT;
} else {
throw std::runtime_error("illegal type for altimeter rotate");
}
mutable_log_rotate->set_category(category);
auto response_opt = transport->send<::tateyama::proto::altimeter::response::LogRotate>(request);
request.clear_log_rotate();

reason = post_processing<::tateyama::proto::altimeter::response::LogRotate>(response_opt, "rotete");
} catch (std::runtime_error &ex) {
std::cerr << "could not connect to database with name '" << tateyama::bootstrap::wire::transport::database_name() << "'" << std::endl;
reason = monitor::reason::connection;
}

if (monitor_output) {
monitor_output->finish(reason);
}
return (reason == monitor::reason::absent) ? tgctl::return_code::err : tgctl::return_code::err; // NOLINT(misc-redundant-expression)
}

} // tateyama::bootstrap::backup
31 changes: 31 additions & 0 deletions src/tateyama/altimeter/altimeter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2022-2024 Project Tsurugi.
*
* Licensed 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 <string>
#include <string_view>
#include <vector>

#include "tateyama/tgctl/tgctl.h"

namespace tateyama::altimeter {

tgctl::return_code set_enabled(const std::string&, bool);
tgctl::return_code set_log_level(const std::string&, const std::string&);
tgctl::return_code set_statement_duration(const std::string&);
tgctl::return_code rotate(const std::string&);

} // tateyama::altimeter
4 changes: 2 additions & 2 deletions src/tateyama/altimeter/altimeter_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ void altimeter_helper::setup(::altimeter::configuration& configuration, tateyama
std::cout << "Failed to flush or rotate event log files: "
<< error_message << "\n";
});
::altimeter::event::event_logger::set_stmt_duration_threshold(section->get<std::size_t>("stmt_duration_threshold").value());
::altimeter::event::event_logger::set_stmt_duration_threshold(section->get<std::uint32_t>("stmt_duration_threshold").value());
} else {
configuration.error_handler([](std::string_view error_message) {
std::cout << "Failed to flush or rotate audit log files: "
Expand Down Expand Up @@ -115,7 +115,7 @@ void altimeter_helper::setup(::altimeter::configuration& configuration, tateyama
LOG(INFO) << prefix << "flush_file_size = " << flush_file_size << ", file size to be flashed";
LOG(INFO) << prefix << "max_file_size = " << max_file_size << ", file size to rotate";
if (type == log_type::event_log) {
LOG(INFO) << prefix << "stmt_duration_threshold = " << section->get<std::size_t>("stmt_duration_threshold").value() << ", duration threshold for statement log";
LOG(INFO) << prefix << "stmt_duration_threshold = " << section->get<std::uint32_t>("stmt_duration_threshold").value() << ", duration threshold for statement log";
}
}

Expand Down
50 changes: 48 additions & 2 deletions src/tateyama/monitor/monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ enum class status : std::int64_t {
unknown = -1,
};

enum class reason : std::int64_t {
absent = 0,
connection = 1,
server = 2,
not_found = 3,
ambiguous = 4,
permission = 5,
variable_not_defined = 6,
variable_invalid_value = 7,

unknown = -1,
};

/**
* @brief returns string representation of the value.
* @param value the target value
Expand All @@ -52,6 +65,27 @@ enum class status : std::int64_t {
return "illegal state"sv;
}

/**
* @brief returns string representation of the value.
* @param value the target value
* @return the corresponded string representation
*/
[[nodiscard]] constexpr inline std::string_view to_string_view(reason value) noexcept {
using namespace std::string_view_literals;
switch (value) {
case reason::absent:return "absent"sv;
case reason::connection:return "connection"sv;
case reason::server:return "server"sv;
case reason::not_found:return "not_found"sv;
case reason::ambiguous:return "ambiguous"sv;
case reason::permission:return "permission"sv;
case reason::variable_not_defined:return "variable_not_defined"sv;
case reason::variable_invalid_value:return "variable_invalid_value"sv;
case reason::unknown: return "unknown"sv;
}
return "illegal reason"sv;
}

class monitor {
constexpr static std::string_view TIME_STAMP = R"("timestamp": )";
constexpr static std::string_view KIND_START = R"("kind": "start")";
Expand All @@ -62,6 +96,7 @@ class monitor {
// status
constexpr static std::string_view FORMAT_STATUS = R"("format": "status")";
constexpr static std::string_view STATUS = R"("status": ")";
constexpr static std::string_view REASON = R"("reason": ")";
// session info
constexpr static std::string_view FORMAT_SESSION_INFO = R"("format": "session-info")";
constexpr static std::string_view SESSION_ID = R"("session_id": ":)";
Expand Down Expand Up @@ -97,10 +132,21 @@ class monitor {
void finish(bool status) {
if (status) {
strm_ << "{ " << TIME_STAMP << time(nullptr) << ", "
<< KIND_FINISH << ", " << STATUS << "success\" }" << std::endl;
<< KIND_FINISH << ", " << STATUS << R"(success" })" << std::endl;
} else {
strm_ << "{ " << TIME_STAMP << time(nullptr) << ", "
<< KIND_FINISH << ", " << STATUS << R"(failure" })" << std::endl;
}
strm_.flush();
}
void finish(reason rc) {
if (rc == reason::absent) {
strm_ << "{ " << TIME_STAMP << time(nullptr) << ", "
<< KIND_FINISH << ", " << STATUS << R"(success" })" << std::endl;
} else {
strm_ << "{ " << TIME_STAMP << time(nullptr) << ", "
<< KIND_FINISH << ", " << STATUS << "failure\" }" << std::endl;
<< KIND_FINISH << ", " << STATUS << R"(failure", )"
<< REASON << to_string_view(rc) << R"(" })" << std::endl;
}
strm_.flush();
}
Expand Down
20 changes: 20 additions & 0 deletions src/tateyama/proto/altimeter/common.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
syntax = "proto3";

package tateyama.proto.altimeter.common;

option java_multiple_files = false;
option java_package = "com.tsurugidb.altimeter.proto";
option java_outer_classname = "AltimeterCommon";

// represents log category.
enum LogCategory {

// log category is not set.
LOG_CATEGORY_NOT_SET = 0;

// event log category.
EVENT = 1;

// audit log category.
AUDIT = 2;
}
Loading

0 comments on commit 7344765

Please sign in to comment.