1
#include "source/extensions/access_loggers/open_telemetry/config.h"
2

            
3
#include "envoy/extensions/access_loggers/open_telemetry/v3/logs_service.pb.h"
4
#include "envoy/extensions/access_loggers/open_telemetry/v3/logs_service.pb.validate.h"
5
#include "envoy/registry/registry.h"
6
#include "envoy/server/filter_config.h"
7

            
8
#include "source/common/common/assert.h"
9
#include "source/common/common/macros.h"
10
#include "source/common/formatter/substitution_format_string.h"
11
#include "source/common/grpc/async_client_impl.h"
12
#include "source/common/protobuf/protobuf.h"
13
#include "source/extensions/access_loggers/open_telemetry/access_log_impl.h"
14
#include "source/extensions/access_loggers/open_telemetry/access_log_proto_descriptors.h"
15
#include "source/extensions/access_loggers/open_telemetry/http_access_log_impl.h"
16

            
17
namespace Envoy {
18
namespace Extensions {
19
namespace AccessLoggers {
20
namespace OpenTelemetry {
21

            
22
// Singleton registration via macro defined in envoy/singleton/manager.h
23
SINGLETON_MANAGER_REGISTRATION(open_telemetry_access_logger_cache);
24
SINGLETON_MANAGER_REGISTRATION(open_telemetry_http_access_logger_cache);
25

            
26
std::shared_ptr<GrpcAccessLoggerCacheImpl>
27
8
getGrpcAccessLoggerCacheSingleton(Server::Configuration::CommonFactoryContext& context) {
28
8
  return context.singletonManager().getTyped<GrpcAccessLoggerCacheImpl>(
29
8
      SINGLETON_MANAGER_REGISTERED_NAME(open_telemetry_access_logger_cache), [&context] {
30
6
        return std::make_shared<GrpcAccessLoggerCacheImpl>(
31
6
            context.clusterManager().grpcAsyncClientManager(), context.serverScope(),
32
6
            context.threadLocal(), context.localInfo());
33
6
      });
34
8
}
35

            
36
HttpAccessLoggerCacheSharedPtr
37
10
getHttpAccessLoggerCacheSingleton(Server::Configuration::ServerFactoryContext& context) {
38
10
  return context.singletonManager().getTyped<HttpAccessLoggerCacheImpl>(
39
10
      SINGLETON_MANAGER_REGISTERED_NAME(open_telemetry_http_access_logger_cache),
40
10
      [&context] { return std::make_shared<HttpAccessLoggerCacheImpl>(context); });
41
10
}
42

            
43
::Envoy::AccessLog::InstanceSharedPtr AccessLogFactory::createAccessLogInstance(
44
    const Protobuf::Message& config, ::Envoy::AccessLog::FilterPtr&& filter,
45
    Server::Configuration::GenericFactoryContext& context,
46
20
    std::vector<Formatter::CommandParserPtr>&& command_parsers) {
47
20
  validateProtoDescriptors();
48

            
49
20
  const auto& proto_config = MessageUtil::downcastAndValidate<
50
20
      const envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig&>(
51
20
      config, context.messageValidationVisitor());
52

            
53
  // Validate transport configuration: exactly one transport must be specified.
54
20
  const bool has_grpc_service = proto_config.has_grpc_service();
55
20
  const bool has_http_service = proto_config.has_http_service();
56
20
  const bool has_common_config_grpc =
57
20
      proto_config.has_common_config() && proto_config.common_config().has_grpc_service();
58

            
59
20
  const int transport_count =
60
20
      (has_grpc_service ? 1 : 0) + (has_http_service ? 1 : 0) + (has_common_config_grpc ? 1 : 0);
61

            
62
20
  if (transport_count == 0) {
63
1
    throw EnvoyException(
64
1
        "OpenTelemetry access logger requires one of: grpc_service, http_service, or "
65
1
        "common_config.grpc_service to be configured.");
66
1
  }
67

            
68
19
  if (transport_count > 1) {
69
1
    throw EnvoyException(
70
1
        "OpenTelemetry access logger can only have one transport configured. "
71
1
        "Specify exactly one of: grpc_service, http_service, or common_config.grpc_service.");
72
1
  }
73

            
74
18
  auto commands =
75
18
      THROW_OR_RETURN_VALUE(Formatter::SubstitutionFormatStringUtils::parseFormatters(
76
18
                                proto_config.formatters(), context, std::move(command_parsers)),
77
18
                            std::vector<Formatter::CommandParserPtr>);
78

            
79
  // Create appropriate access log based on transport type.
80
18
  if (has_http_service) {
81
10
    return std::make_shared<HttpAccessLog>(
82
10
        std::move(filter), proto_config,
83
10
        getHttpAccessLoggerCacheSingleton(context.serverFactoryContext()),
84
10
        context.serverFactoryContext(), commands);
85
10
  }
86

            
87
8
  return std::make_shared<AccessLog>(
88
8
      std::move(filter), proto_config, context.serverFactoryContext().threadLocal(),
89
8
      getGrpcAccessLoggerCacheSingleton(context.serverFactoryContext()), commands);
90
18
}
91

            
92
32
ProtobufTypes::MessagePtr AccessLogFactory::createEmptyConfigProto() {
93
32
  return std::make_unique<
94
32
      envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig>();
95
32
}
96

            
97
31
std::string AccessLogFactory::name() const { return "envoy.access_loggers.open_telemetry"; }
98

            
99
/**
100
 * Static registration for the OpenTelemetry (gRPC) access log. @see RegisterFactory.
101
 */
102
REGISTER_FACTORY(AccessLogFactory,
103
                 Envoy::AccessLog::AccessLogInstanceFactory){"envoy.open_telemetry_access_log"};
104

            
105
} // namespace OpenTelemetry
106
} // namespace AccessLoggers
107
} // namespace Extensions
108
} // namespace Envoy