1
#include "source/extensions/access_loggers/grpc/http_grpc_access_log_impl.h"
2

            
3
#include "envoy/config/core/v3/base.pb.h"
4
#include "envoy/data/accesslog/v3/accesslog.pb.h"
5
#include "envoy/extensions/access_loggers/grpc/v3/als.pb.h"
6

            
7
#include "source/common/common/assert.h"
8
#include "source/common/config/utility.h"
9
#include "source/common/http/header_map_impl.h"
10
#include "source/common/http/header_utility.h"
11
#include "source/common/http/headers.h"
12
#include "source/common/network/utility.h"
13
#include "source/common/stream_info/utility.h"
14
#include "source/extensions/access_loggers/grpc/grpc_access_log_utils.h"
15

            
16
namespace Envoy {
17
namespace Extensions {
18
namespace AccessLoggers {
19
namespace HttpGrpc {
20

            
21
Http::RegisterCustomInlineHeader<Http::CustomInlineHeaderRegistry::Type::RequestHeaders>
22
    referer_handle(Http::CustomHeaders::get().Referer);
23

            
24
HttpGrpcAccessLog::ThreadLocalLogger::ThreadLocalLogger(
25
    GrpcCommon::GrpcAccessLoggerSharedPtr logger)
26
38
    : logger_(std::move(logger)) {}
27

            
28
HttpGrpcAccessLog::HttpGrpcAccessLog(AccessLog::FilterPtr&& filter,
29
                                     const HttpGrpcAccessLogConfig config,
30
                                     ThreadLocal::SlotAllocator& tls,
31
                                     GrpcCommon::GrpcAccessLoggerCacheSharedPtr access_logger_cache,
32
                                     const Formatter::CommandParserPtrVector& command_parsers)
33
24
    : Common::ImplBase(std::move(filter)),
34
24
      config_(std::make_shared<const HttpGrpcAccessLogConfig>(std::move(config))),
35
24
      tls_slot_(tls.allocateSlot()), access_logger_cache_(std::move(access_logger_cache)),
36
24
      common_properties_config_(config.common_config(), command_parsers) {
37
24
  for (const auto& header : config_->additional_request_headers_to_log()) {
38
5
    request_headers_to_log_.emplace_back(header);
39
5
  }
40

            
41
24
  for (const auto& header : config_->additional_response_headers_to_log()) {
42
5
    response_headers_to_log_.emplace_back(header);
43
5
  }
44

            
45
24
  for (const auto& header : config_->additional_response_trailers_to_log()) {
46
4
    response_trailers_to_log_.emplace_back(header);
47
4
  }
48
24
  THROW_IF_NOT_OK(Envoy::Config::Utility::checkTransportVersion(config_->common_config()));
49
24
  tls_slot_->set(
50
38
      [config = config_, access_logger_cache = access_logger_cache_](Event::Dispatcher&) {
51
38
        return std::make_shared<ThreadLocalLogger>(access_logger_cache->getOrCreateLogger(
52
38
            config->common_config(), Common::GrpcAccessLoggerType::HTTP));
53
38
      });
54
24
}
55

            
56
void HttpGrpcAccessLog::emitLog(const Formatter::Context& context,
57
43
                                const StreamInfo::StreamInfo& stream_info) {
58
  // Common log properties.
59
  // TODO(mattklein123): Populate sample_rate field.
60
43
  envoy::data::accesslog::v3::HTTPAccessLogEntry log_entry;
61

            
62
43
  const Http::RequestHeaderMap& request_headers =
63
43
      context.requestHeaders().value_or(*Http::StaticEmptyHeaders::get().request_headers);
64

            
65
43
  GrpcCommon::Utility::extractCommonAccessLogProperties(*log_entry.mutable_common_properties(),
66
43
                                                        common_properties_config_, request_headers,
67
43
                                                        stream_info, context);
68

            
69
43
  if (stream_info.protocol()) {
70
19
    switch (stream_info.protocol().value()) {
71
5
    case Http::Protocol::Http10:
72
5
      log_entry.set_protocol_version(envoy::data::accesslog::v3::HTTPAccessLogEntry::HTTP10);
73
5
      break;
74
10
    case Http::Protocol::Http11:
75
10
      log_entry.set_protocol_version(envoy::data::accesslog::v3::HTTPAccessLogEntry::HTTP11);
76
10
      break;
77
4
    case Http::Protocol::Http2:
78
4
      log_entry.set_protocol_version(envoy::data::accesslog::v3::HTTPAccessLogEntry::HTTP2);
79
4
      break;
80
    case Http::Protocol::Http3:
81
      log_entry.set_protocol_version(envoy::data::accesslog::v3::HTTPAccessLogEntry::HTTP3);
82
      break;
83
19
    }
84
19
  }
85

            
86
  // HTTP request properties.
87
  // TODO(mattklein123): Populate port field.
88
43
  auto* request_properties = log_entry.mutable_request();
89
43
  if (request_headers.Scheme() != nullptr) {
90
21
    request_properties->set_scheme(
91
21
        MessageUtil::sanitizeUtf8String(request_headers.getSchemeValue()));
92
21
  }
93
43
  if (request_headers.Host() != nullptr) {
94
21
    request_properties->set_authority(
95
21
        MessageUtil::sanitizeUtf8String(request_headers.getHostValue()));
96
21
  }
97
43
  if (request_headers.Path() != nullptr) {
98
21
    request_properties->set_path(MessageUtil::sanitizeUtf8String(request_headers.getPathValue()));
99
21
  }
100
43
  if (request_headers.UserAgent() != nullptr) {
101
1
    request_properties->set_user_agent(
102
1
        MessageUtil::sanitizeUtf8String(request_headers.getUserAgentValue()));
103
1
  }
104
43
  if (request_headers.getInline(referer_handle.handle()) != nullptr) {
105
1
    request_properties->set_referer(
106
1
        MessageUtil::sanitizeUtf8String(request_headers.getInlineValue(referer_handle.handle())));
107
1
  }
108
43
  if (request_headers.ForwardedFor() != nullptr) {
109
1
    request_properties->set_forwarded_for(
110
1
        MessageUtil::sanitizeUtf8String(request_headers.getForwardedForValue()));
111
1
  }
112
43
  if (request_headers.RequestId() != nullptr) {
113
19
    request_properties->set_request_id(
114
19
        MessageUtil::sanitizeUtf8String(request_headers.getRequestIdValue()));
115
19
  }
116
43
  if (request_headers.EnvoyOriginalPath() != nullptr) {
117
1
    request_properties->set_original_path(
118
1
        MessageUtil::sanitizeUtf8String(request_headers.getEnvoyOriginalPathValue()));
119
1
  }
120
43
  request_properties->set_request_headers_bytes(request_headers.byteSize());
121
43
  request_properties->set_request_body_bytes(stream_info.bytesReceived());
122

            
123
43
  if (request_headers.Method() != nullptr) {
124
32
    envoy::config::core::v3::RequestMethod method = envoy::config::core::v3::METHOD_UNSPECIFIED;
125
32
    envoy::config::core::v3::RequestMethod_Parse(
126
32
        MessageUtil::sanitizeUtf8String(request_headers.getMethodValue()), &method);
127
32
    request_properties->set_request_method(method);
128
32
  }
129
43
  if (!request_headers_to_log_.empty()) {
130
2
    auto* logged_headers = request_properties->mutable_request_headers();
131

            
132
5
    for (const auto& header : request_headers_to_log_) {
133
5
      const auto all_values = Http::HeaderUtility::getAllOfHeaderAsString(request_headers, header);
134
5
      if (all_values.result().has_value()) {
135
4
        logged_headers->insert(
136
4
            {header.get(), MessageUtil::sanitizeUtf8String(all_values.result().value())});
137
4
      }
138
5
    }
139
2
  }
140

            
141
  // HTTP response properties.
142
43
  const Http::ResponseHeaderMap& response_headers =
143
43
      context.responseHeaders().value_or(*Http::StaticEmptyHeaders::get().response_headers);
144
43
  const Http::ResponseTrailerMap& response_trailers =
145
43
      context.responseTrailers().value_or(*Http::StaticEmptyHeaders::get().response_trailers);
146

            
147
43
  auto* response_properties = log_entry.mutable_response();
148
43
  if (stream_info.responseCode()) {
149
19
    response_properties->mutable_response_code()->set_value(stream_info.responseCode().value());
150
19
  }
151
43
  if (stream_info.responseCodeDetails()) {
152
17
    response_properties->set_response_code_details(stream_info.responseCodeDetails().value());
153
17
  }
154
43
  response_properties->set_response_headers_bytes(response_headers.byteSize());
155
43
  response_properties->set_response_body_bytes(stream_info.bytesSent());
156
43
  if (!response_headers_to_log_.empty()) {
157
2
    auto* logged_headers = response_properties->mutable_response_headers();
158

            
159
5
    for (const auto& header : response_headers_to_log_) {
160
5
      const auto all_values = Http::HeaderUtility::getAllOfHeaderAsString(response_headers, header);
161
5
      if (all_values.result().has_value()) {
162
4
        logged_headers->insert(
163
4
            {header.get(), MessageUtil::sanitizeUtf8String(all_values.result().value())});
164
4
      }
165
5
    }
166
2
  }
167

            
168
43
  if (!response_trailers_to_log_.empty()) {
169
2
    auto* logged_headers = response_properties->mutable_response_trailers();
170

            
171
4
    for (const auto& header : response_trailers_to_log_) {
172
4
      const auto all_values =
173
4
          Http::HeaderUtility::getAllOfHeaderAsString(response_trailers, header);
174
4
      if (all_values.result().has_value()) {
175
3
        logged_headers->insert(
176
3
            {header.get(), MessageUtil::sanitizeUtf8String(all_values.result().value())});
177
3
      }
178
4
    }
179
2
  }
180

            
181
43
  if (const auto& bytes_meter = stream_info.getDownstreamBytesMeter(); bytes_meter != nullptr) {
182
18
    request_properties->set_downstream_header_bytes_received(bytes_meter->headerBytesReceived());
183
18
    response_properties->set_downstream_header_bytes_sent(bytes_meter->headerBytesSent());
184
18
  }
185
43
  if (const auto& bytes_meter = stream_info.getUpstreamBytesMeter(); bytes_meter != nullptr) {
186
18
    request_properties->set_upstream_header_bytes_sent(bytes_meter->headerBytesSent());
187
18
    response_properties->set_upstream_header_bytes_received(bytes_meter->headerBytesReceived());
188
18
  }
189

            
190
43
  tls_slot_->getTyped<ThreadLocalLogger>().logger_->log(std::move(log_entry));
191
43
}
192

            
193
} // namespace HttpGrpc
194
} // namespace AccessLoggers
195
} // namespace Extensions
196
} // namespace Envoy