require 'opentelemetry/sdk' require 'opentelemetry/exporter/otlp' require 'opentelemetry/exporter/otlp/exporter' class Hypertrace::OtlpHttpExporter < OpenTelemetry::Exporter::OTLP::Exporter def initialize(endpoint: config_opt('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'https://localhost:4318/v1/traces'), # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity certificate_file: config_opt('OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), ssl_verify_mode: self.class.ssl_verify_mode, headers: config_opt('OTEL_EXPORTER_OTLP_TRACES_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS', default: {}), compression: config_opt('OTEL_EXPORTER_OTLP_TRACES_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION', default: 'gzip'), timeout: config_opt('OTEL_EXPORTER_OTLP_TRACES_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', default: 10), metrics_reporter: nil, allowed_request_headers: [], allowed_response_headers: [], drop_attributes: [], supported_instrumentation_libraries: [], use_all: false, is_hypertrace: false, custom_resource: nil ) super( endpoint: endpoint, certificate_file: certificate_file, ssl_verify_mode: ssl_verify_mode, headers: headers, compression: compression, timeout: timeout, metrics_reporter: metrics_reporter ) # If a user specifies use_all that means we need to keep all spans # If a user specifies supported libraries that means we should drop spans that aren't from those libraries if use_all && supported_instrumentation_libraries.length > 0 raise ArgumentError.new("Must use either use_all or supported_instrumentation_libraries, cannot specify both") end # if passed is_hypertrace all spans + attributes will be kept, we should be the only one passing this @is_hypertrace = is_hypertrace # List of request headers to allow, not used by hypertrace @allowed_request_headers = [] # List of response headers to allow, not used by hypertrace @allowed_response_headers = [] allowed_request_headers.each do |req_header| @allowed_request_headers << "http.request.header.#{req_header}" end allowed_response_headers.each do |res_header| @allowed_response_headers << "http.response.header.#{res_header}" end # List of instrumentation libraries to report for this exporter @supported_instrumentation_libraries = supported_instrumentation_libraries # If c.use_all is used to configure OTEL sdk this option should be used # We won't drop any spans based on Instrumentation library @use_all = use_all # List of attributes to always drop @drop_attributes = drop_attributes # An override resource object to override default configured resource @custom_resource = custom_resource end def export(span_data, timeout: nil) # Drop spans if not from a supported library span_data = drop_extra_spans(span_data) # Remove any attributes that shouldn't be reported span_data = drop_attributes(span_data) # Override the default configured resource - only used by Hypertrace span_data = customize_resource(span_data) super end def drop_extra_spans(span_data) return span_data if @use_all filtered_spans = span_data.select do |span| name = span.instrumentation_library.name # If a user uses a manually created span we should keep it, ex: instrumentation_library.name = "My-tracer" # If a user is using an OTel Library the value will be like: OpenTelemetry::Instrumentation::Rack !name.include?("OpenTelemetry") || @supported_instrumentation_libraries.include?(name) end filtered_spans end def drop_attributes span_data return span_data if @is_hypertrace span_data.each do |span| new_attributes = {} span[:attributes].each do |key, value| if @drop_attributes.include?(key) next elsif key.start_with?('http.request.header.') if @allowed_request_headers.include?(key) new_attributes[key] = value end next elsif key.start_with?('http.response.header.') if @allowed_response_headers.include?(key) new_attributes[key] = value end next else new_attributes[key] = value end end span[:attributes] = new_attributes end span_data end def customize_resource span_data return span_data unless @custom_resource span_data.each do |span| span.resource = @custom_resource end span_data end end