|
| 1 | +// Copyright 2025 The TensorStore Authors |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +#include "tensorstore/kvstore/s3/aws_api.h" |
| 16 | + |
| 17 | +#include <stdint.h> |
| 18 | + |
| 19 | +#include <cstdarg> |
| 20 | +#include <cstdio> |
| 21 | +#include <cstring> |
| 22 | +#include <memory> |
| 23 | +#include <string_view> |
| 24 | + |
| 25 | +#include "absl/base/attributes.h" |
| 26 | +#include "absl/base/call_once.h" |
| 27 | +#include "absl/base/log_severity.h" |
| 28 | +#include "absl/debugging/leak_check.h" |
| 29 | +#include "absl/log/absl_log.h" |
| 30 | +#include <aws/auth/auth.h> |
| 31 | +#include <aws/common/allocator.h> |
| 32 | +#include <aws/common/byte_buf.h> |
| 33 | +#include <aws/common/common.h> |
| 34 | +#include <aws/common/error.h> |
| 35 | +#include <aws/common/logging.h> |
| 36 | +#include <aws/common/zero.h> |
| 37 | +#include <aws/io/channel_bootstrap.h> |
| 38 | +#include <aws/io/event_loop.h> |
| 39 | +#include <aws/io/host_resolver.h> |
| 40 | +#include <aws/io/io.h> |
| 41 | +#include <aws/io/tls_channel_handler.h> |
| 42 | +#include "tensorstore/internal/log/verbose_flag.h" |
| 43 | + |
| 44 | +namespace tensorstore { |
| 45 | +namespace internal_kvstore_s3 { |
| 46 | +namespace { |
| 47 | + |
| 48 | +constexpr int kLogBufSize = 2000; |
| 49 | + |
| 50 | +// Hook AWS logging into absl logging. |
| 51 | +ABSL_CONST_INIT internal_log::VerboseFlag aws_logging("aws"); |
| 52 | + |
| 53 | +int absl_log(struct aws_logger *logger, enum aws_log_level log_level, |
| 54 | + aws_log_subject_t subject, const char *format, ...) { |
| 55 | + absl::LogSeverity severity = absl::LogSeverity::kInfo; |
| 56 | + if (log_level <= AWS_LL_FATAL) { |
| 57 | + severity = absl::LogSeverity::kFatal; |
| 58 | + } else if (log_level <= AWS_LL_ERROR) { |
| 59 | + severity = absl::LogSeverity::kError; |
| 60 | + } else if (log_level <= AWS_LL_WARN) { |
| 61 | + severity = absl::LogSeverity::kWarning; |
| 62 | + } |
| 63 | +#ifdef ABSL_MIN_LOG_LEVEL |
| 64 | + if (severity < static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) && |
| 65 | + severity < absl::LogSeverity::kFatal) { |
| 66 | + enabled = false; |
| 67 | + } |
| 68 | +#endif |
| 69 | + |
| 70 | + // AWS Logging doesn't provide a way to get the filename or line number, |
| 71 | + // instead use the aws subject name as the filename and the subject itself as |
| 72 | + // the line number. |
| 73 | + const char *subject_name = aws_log_subject_name(subject); |
| 74 | + bool is_valid_subject = |
| 75 | + (subject_name != nullptr && strcmp(subject_name, "Unknown") != 0); |
| 76 | + |
| 77 | + char buffer[kLogBufSize]; |
| 78 | + char *buf = buffer; |
| 79 | + size_t size = sizeof(buffer); |
| 80 | + |
| 81 | + va_list argptr; |
| 82 | + va_start(argptr, format); |
| 83 | + int n = vsnprintf(buf, size, format, argptr); |
| 84 | + va_end(argptr); |
| 85 | + if (n > 0 && n < size) { |
| 86 | + ABSL_LOG(LEVEL(severity)) |
| 87 | + .AtLocation(is_valid_subject ? subject_name : "aws_api.cc", subject) |
| 88 | + << std::string_view(buf, n); |
| 89 | + } |
| 90 | + return AWS_OP_SUCCESS; |
| 91 | +}; |
| 92 | + |
| 93 | +enum aws_log_level absl_get_log_level(struct aws_logger *logger, |
| 94 | + aws_log_subject_t subject) { |
| 95 | + uintptr_t lvl = reinterpret_cast<uintptr_t>(logger->p_impl); |
| 96 | + if (lvl != 0) { |
| 97 | + return static_cast<enum aws_log_level>(lvl - 1); |
| 98 | + } |
| 99 | + if (!aws_logging) { |
| 100 | + return AWS_LL_WARN; |
| 101 | + } |
| 102 | + // NOTE: AWS logging is quite verbose even at AWS_LL_INFO. |
| 103 | + if (aws_logging.Level(1)) { |
| 104 | + return aws_logging.Level(2) ? AWS_LL_TRACE : AWS_LL_DEBUG; |
| 105 | + } |
| 106 | + return AWS_LL_INFO; |
| 107 | +} |
| 108 | + |
| 109 | +int absl_set_log_level(struct aws_logger *logger, enum aws_log_level lvl) { |
| 110 | + if (lvl == AWS_LL_NONE) { |
| 111 | + reinterpret_cast<uintptr_t &>(logger->p_impl) = 0; |
| 112 | + } else { |
| 113 | + reinterpret_cast<uintptr_t &>(logger->p_impl) = |
| 114 | + 1 + static_cast<uintptr_t>(lvl); |
| 115 | + } |
| 116 | + return AWS_OP_SUCCESS; |
| 117 | +} |
| 118 | + |
| 119 | +void absl_clean_up(struct aws_logger *logger) { (void)logger; } |
| 120 | + |
| 121 | +static aws_logger_vtable s_absl_vtable = { |
| 122 | + /*log =*/absl_log, |
| 123 | + /*get_log_level =*/absl_get_log_level, |
| 124 | + /*clean_up =*/absl_clean_up, |
| 125 | + /*set_log_level =*/absl_set_log_level, |
| 126 | +}; |
| 127 | + |
| 128 | +static aws_logger s_absl_logger = { |
| 129 | + /*vtable =*/&s_absl_vtable, |
| 130 | + /*.allocator =*/nullptr, |
| 131 | + /*.p_impl =*/0, |
| 132 | +}; |
| 133 | + |
| 134 | +// AWS apis rely on global initialization; do that here. |
| 135 | +absl::once_flag g_init_once; |
| 136 | +aws_allocator *g_allocator = nullptr; |
| 137 | +struct aws_event_loop_group *g_event_loop_group = nullptr; |
| 138 | + |
| 139 | +void InitAwsGlobals() { |
| 140 | + absl::LeakCheckDisabler disabler; |
| 141 | + aws_logger_set(&s_absl_logger); |
| 142 | + g_allocator = aws_default_allocator(); |
| 143 | + |
| 144 | + // Initialize the AWS APIs used. |
| 145 | + aws_common_library_init(g_allocator); |
| 146 | + aws_io_library_init(g_allocator); |
| 147 | + aws_auth_library_init(g_allocator); |
| 148 | + |
| 149 | + /* event loop */ |
| 150 | + g_event_loop_group = |
| 151 | + aws_event_loop_group_new_default(g_allocator, 0, nullptr); |
| 152 | +} |
| 153 | + |
| 154 | +} // namespace |
| 155 | + |
| 156 | +AwsApiContext::~AwsApiContext() { |
| 157 | + aws_tls_ctx_release(tls_ctx); |
| 158 | + aws_client_bootstrap_release(client_bootstrap); |
| 159 | + aws_host_resolver_release(resolver); |
| 160 | +} |
| 161 | + |
| 162 | +std::shared_ptr<AwsApiContext> GetAwsApiContext() { |
| 163 | + absl::call_once(g_init_once, InitAwsGlobals); |
| 164 | + |
| 165 | + /* resolver */ |
| 166 | + aws_host_resolver_default_options resolver_options; |
| 167 | + AWS_ZERO_STRUCT(resolver_options); |
| 168 | + resolver_options.el_group = g_event_loop_group; |
| 169 | + resolver_options.max_entries = 32; // defaults to 8? |
| 170 | + aws_host_resolver *resolver = |
| 171 | + aws_host_resolver_new_default(g_allocator, &resolver_options); |
| 172 | + |
| 173 | + /* client bootstrap */ |
| 174 | + aws_client_bootstrap_options bootstrap_options; |
| 175 | + AWS_ZERO_STRUCT(bootstrap_options); |
| 176 | + bootstrap_options.event_loop_group = g_event_loop_group; |
| 177 | + bootstrap_options.host_resolver = resolver; |
| 178 | + auto *client_bootstrap = |
| 179 | + aws_client_bootstrap_new(g_allocator, &bootstrap_options); |
| 180 | + if (client_bootstrap == nullptr) { |
| 181 | + ABSL_LOG(FATAL) << "ERROR initializing client bootstrap: " |
| 182 | + << aws_error_debug_str(aws_last_error()); |
| 183 | + } |
| 184 | + |
| 185 | + aws_tls_ctx_options tls_options; |
| 186 | + aws_tls_ctx_options_init_default_client(&tls_options, g_allocator); |
| 187 | + aws_tls_ctx *tls_ctx = aws_tls_client_ctx_new(g_allocator, &tls_options); |
| 188 | + aws_tls_ctx_options_clean_up(&tls_options); |
| 189 | + if (tls_ctx == nullptr) { |
| 190 | + ABSL_LOG(FATAL) << "ERROR initializing TLS context: " |
| 191 | + << aws_error_debug_str(aws_last_error()); |
| 192 | + } |
| 193 | + |
| 194 | + auto result = std::make_shared<AwsApiContext>(); |
| 195 | + result->allocator = g_allocator; |
| 196 | + result->resolver = resolver; |
| 197 | + result->client_bootstrap = client_bootstrap; |
| 198 | + result->tls_ctx = tls_ctx; |
| 199 | + return result; |
| 200 | +} |
| 201 | + |
| 202 | +} // namespace internal_kvstore_s3 |
| 203 | +} // namespace tensorstore |
0 commit comments