Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate span.type and .subtype against shared spec #1030

Merged
merged 14 commits into from
Jul 7, 2021
Merged
2 changes: 2 additions & 0 deletions lib/elastic_apm/span.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ def inspect
"<ElasticAPM::Span id:#{trace_context&.id}" \
" name:#{name.inspect}" \
" type:#{type.inspect}" \
" subtype:#{subtype.inspect}" \
" action:#{action.inspect}" \
'>'
end

Expand Down
1 change: 1 addition & 0 deletions lib/elastic_apm/subscriber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def unregister!
# AS::Notifications API

Notification = Struct.new(:id, :span)

def start(name, id, payload)
return unless (transaction = @agent.current_transaction)

Expand Down
2 changes: 1 addition & 1 deletion spec/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
set -x

bundle check || (rm Gemfile.lock && bundle)
bundle check || (rm -f Gemfile.lock && bundle)

# If first arg is a spec path, run spec(s)
if [[ $1 == spec/* ]]; then
Expand Down
279 changes: 279 additions & 0 deletions spec/fixtures/span_types.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
{
"__description": {
"<type>": "root element for type identified by '<type>'",
"<type>.__description": "description for '<type>' (optional)",
"<type>.__used_by": "list of agents that use '<type>' to help document alignment (optional)",
"<type>.allow_null_subtype": "true to allow null subtype, false by default if omitted",
"<type>.allow_unlisted_subtype": "true to allow unlisted subtypes, false by default if omitted",
"<type>.subtypes": "root element for sub-types of type '<type>', if omitted or empty subtype must be null, unless 'allow_unlisted_subtype' is set to true",
"<type>.subtypes.<subtype>": "sub-type element for <subtype>",
"<type>.subtypes.<subtype>.__description": "description of <subtype> subtype (optional)",
"<type>.subtypes.<subtype>.__used_by": "list of agents that use <subtype> to help document alignment (optional)"
},
"app": {
"allow_null_subtype": true,
"subtypes": {
"inferred": {
"__description": "Sampling profiler inferred spans",
"__used_by": [
"java"
]
},
"controller": {
"__description": "Application controller actions",
"__used_by": [
"ruby"
]
},
"graphql": {
"__description": "Incoming GraphQL requests",
"__used_by": [
"ruby"
]
},
"mailer": {
"__description": "Application mailer actions",
"__used_by": [
"ruby"
]
},
"resource": {
"__description": "Application resource actions",
"__used_by": [
"ruby"
]
}
}
},
"custom": {
"__description": "API custom instrumentation",
"__used_by": [
"java",
"ruby"
],
"allow_null_subtype": true
},
"db": {
"__description": "database span",
"subtypes": {
"cassandra": {
"__description": "Cassandra",
"__used_by": [
"java"
]
},
"cosmosdb": {
"__description": "Azure CosmosDB"
},
"db2": {
"__description": "IBM DB2",
"__used_by": [
"java"
]
},
"derby": {
"__description": "Apache Derby",
"__used_by": [
"java"
]
},
"dynamodb": {
"__description": "AWS DynamoDB",
"__used_by": [
"ruby"
]
},
"elasticsearch": {
"__description": "Elasticsearch",
"__used_by": [
"java",
"ruby"
]
},
"h2": {
"__description": "H2",
"__used_by": [
"java"
]
},
"hsqldb": {
"__description": "HSQLDB",
"__used_by": [
"java"
]
},
"ingres": {
"__description": "Ingres"
},
"mariadb": {
"__description": "MariaDB",
"__used_by": [
"java",
"ruby"
]
},
"mongodb": {
"__description": "MongoDB",
"__used_by": [
"java",
"ruby"
]
},
"mysql": {
"__description": "MySQL",
"__used_by": [
"java",
"ruby"
]
},
"oracle": {
"__description": "Oracle Database",
"__used_by": [
"java"
]
},
"postgresql": {
"__description": "PostgreSQL",
"__used_by": [
"ruby"
]
},
"redis": {
"__description": "Redis",
"__used_by": [
"java",
"ruby"
]
},
"sqlite": {
"__description": "SQLite",
"__used_by": [
"ruby"
]
},
"sqlite3": {
"__description": "SQLite 3",
"__used_by": [
"ruby"
]
},
"sqlserver": {
"__description": "Microsoft SQL Server",
"__used_by": [
"java"
]
},
"unknown": {
"__description": "Unknown database",
"__used_by": [
"java",
"ruby"
]
}
}
},
"external": {
"subtypes": {
"dubbo": {
"__description": "Apache Dubbo"
},
"grpc": {
"__description": "gRPC",
"__used_by": [
"ruby"
]
},
"http": {
"__description": "HTTP client",
"__used_by": [
"ruby"
]
}
}
},
"json": {
"__description": "JSON parsing and generation",
"subtypes": {
"parse": {
"__description": "JSON parsing"
},
"generate": {
"__description": "JSON generation"
}
},
"__used_by": [
"ruby"
]
},
"messaging": {
"__description": "Messaging",
"subtypes": {
"azurequeue": {
"__description": "Azure Queue"
},
"azureservicebus": {
"__description": "Azure Service Bus"
},
"jms": {
"__description": "Java Messaging Service",
"__used_by": [
"java"
]
},
"kafka": {
"__description": "Apache Kafka",
"__used_by": [
"java"
]
},
"rabbitmq": {
"__description": "RabbitMQ",
"__used_by": [
"java"
]
},
"sns": {
"__description": "AWS Simple Notification Service",
"__used_by": [
"ruby"
]
},
"sqs": {
"__description": "AWS Simple Queue Service",
"__used_by": [
"ruby"
]
}
}
},
"process": {
"__description": "External process",
"__used_by": [
"java"
]
},
"storage": {
"subtypes": {
"azurefile": {
"__description": "Azure Files"
},
"azuresblob": {
"__description": "Azure Blob Storage"
},
"s3": {
"__description": "AWS S3",
"__used_by": [
"ruby"
]
}
}
},
"template": {
"__description": "Template engines (no sub-type for now as really platform-specific)",
"__used_by": [
"java",
"ruby"
],
"allow_unlisted_subtype": true
}
}
23 changes: 23 additions & 0 deletions spec/support/intercept.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ def initialize
@spans = []
@errors = []
@metricsets = []

@span_types = JSON.parse(File.read('./spec/fixtures/span_types.json'))
end

attr_reader :transactions, :spans, :errors, :metricsets
Expand All @@ -33,6 +35,7 @@ def submit(obj)
when ElasticAPM::Transaction
transactions << obj
when ElasticAPM::Span
validate_span!(obj)
spans << obj
when ElasticAPM::Error
errors << obj
Expand All @@ -46,6 +49,26 @@ def submit(obj)
def start; end

def stop; end

def validate_span!(span)
type, subtype = [span.type, span.subtype]

begin
info = @span_types.fetch(type)
rescue KeyError
raise "Unknown span.type `#{type}'\nPossible types: #{@span_types.keys.join(', ')}"
end

return unless (allowed_subtypes = info['subtypes'])

if !info['allow_null_subtype'] && !subtype
raise "span.subtype missing when required,\nPossible subtypes: #{allowed_subtypes}"
end

allowed_subtypes.fetch(subtype) unless info['allow_unlisted_subtype']
rescue KeyError
raise "Unknown span.subtype `#{span.type}'\nPossible subtypes: #{allowed_subtypes}"
end
end

module Methods
Expand Down
Loading