Skip to content

Commit

Permalink
Validate span.type and .subtype against shared spec (#1030)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikkel Malmberg authored Jul 7, 2021
1 parent a9e12b5 commit 3cf3082
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 2 deletions.
2 changes: 2 additions & 0 deletions lib/elastic_apm/span.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,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

0 comments on commit 3cf3082

Please sign in to comment.