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
9
getHttpAccessLoggerCacheSingleton(Server::Configuration::CommonFactoryContext& context) {
38
9
  return context.singletonManager().getTyped<HttpAccessLoggerCacheImpl>(
39
9
      SINGLETON_MANAGER_REGISTERED_NAME(open_telemetry_http_access_logger_cache), [&context] {
40
7
        return std::make_shared<HttpAccessLoggerCacheImpl>(
41
7
            context.clusterManager(), context.serverScope(), context.threadLocal(),
42
7
            context.localInfo());
43
7
      });
44
9
}
45

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

            
52
19
  const auto& proto_config = MessageUtil::downcastAndValidate<
53
19
      const envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig&>(
54
19
      config, context.messageValidationVisitor());
55

            
56
  // Validate transport configuration: exactly one transport must be specified.
57
19
  const bool has_grpc_service = proto_config.has_grpc_service();
58
19
  const bool has_http_service = proto_config.has_http_service();
59
19
  const bool has_common_config_grpc =
60
19
      proto_config.has_common_config() && proto_config.common_config().has_grpc_service();
61

            
62
19
  const int transport_count =
63
19
      (has_grpc_service ? 1 : 0) + (has_http_service ? 1 : 0) + (has_common_config_grpc ? 1 : 0);
64

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

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

            
77
17
  auto commands =
78
17
      THROW_OR_RETURN_VALUE(Formatter::SubstitutionFormatStringUtils::parseFormatters(
79
17
                                proto_config.formatters(), context, std::move(command_parsers)),
80
17
                            std::vector<Formatter::CommandParserPtr>);
81

            
82
  // Create appropriate access log based on transport type.
83
17
  if (has_http_service) {
84
9
    return std::make_shared<HttpAccessLog>(
85
9
        std::move(filter), proto_config, context.serverFactoryContext().threadLocal(),
86
9
        getHttpAccessLoggerCacheSingleton(context.serverFactoryContext()), commands);
87
9
  }
88

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

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

            
99
27
std::string AccessLogFactory::name() const { return "envoy.access_loggers.open_telemetry"; }
100

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

            
107
} // namespace OpenTelemetry
108
} // namespace AccessLoggers
109
} // namespace Extensions
110
} // namespace Envoy