github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/registered_span.go (about)

     1  // (c) Copyright IBM Corp. 2021
     2  // (c) Copyright Instana Inc. 2021
     3  
     4  package instana
     5  
     6  import (
     7  	"strings"
     8  
     9  	"github.com/opentracing/opentracing-go/ext"
    10  )
    11  
    12  // Registered types supported by Instana. The span type is determined based on
    13  // the operation name passed to the `StartSpan()` call of a tracer.
    14  //
    15  // It is NOT RECOMMENDED to use operation names that match any of these constants in your
    16  // custom instrumentation code unless you explicitly wish to send data as a registered span.
    17  // The list of supported tags can be found in the godoc of the respective span tags type below.
    18  const (
    19  	// SDK span, a generic span containing arbitrary data. Spans with operation name
    20  	// not listed in the subsequent list will be sent as an SDK spans forwarding all
    21  	// attached tags to the agent
    22  	SDKSpanType = RegisteredSpanType("sdk")
    23  	// HTTP server and client spans
    24  	HTTPServerSpanType = RegisteredSpanType("g.http")
    25  	HTTPClientSpanType = RegisteredSpanType("http")
    26  	// RPC server and client spans
    27  	RPCServerSpanType = RegisteredSpanType("rpc-server")
    28  	RPCClientSpanType = RegisteredSpanType("rpc-client")
    29  	// Kafka consumer/producer span
    30  	KafkaSpanType = RegisteredSpanType("kafka")
    31  	// Google Cloud Storage client span
    32  	GCPStorageSpanType = RegisteredSpanType("gcs")
    33  	// Google Cloud PubSub client span
    34  	GCPPubSubSpanType = RegisteredSpanType("gcps")
    35  	// AWS Lambda entry span
    36  	AWSLambdaEntrySpanType = RegisteredSpanType("aws.lambda.entry")
    37  	// AWS S3 client span
    38  	AWSS3SpanType = RegisteredSpanType("s3")
    39  	// AWS SQS client span
    40  	AWSSQSSpanType = RegisteredSpanType("sqs")
    41  	// AWS SNS client span
    42  	AWSSNSSpanType = RegisteredSpanType("sns")
    43  	// AWS DynamoDB client span
    44  	AWSDynamoDBSpanType = RegisteredSpanType("dynamodb")
    45  	// AWS Lambda invoke span
    46  	AWSLambdaInvokeSpanType = RegisteredSpanType("aws.lambda.invoke")
    47  	// Logging span
    48  	LogSpanType = RegisteredSpanType("log.go")
    49  	// MongoDB client span
    50  	MongoDBSpanType = RegisteredSpanType("mongo")
    51  	// PostgreSQL client span
    52  	PostgreSQLSpanType = RegisteredSpanType("postgres")
    53  	// MySQL client span
    54  	MySQLSpanType = RegisteredSpanType("mysql")
    55  	// Redis client span
    56  	RedisSpanType = RegisteredSpanType("redis")
    57  	// Couchbase client span
    58  	CouchbaseSpanType = RegisteredSpanType("couchbase")
    59  	// Cosmos client span
    60  	CosmosSpanType = RegisteredSpanType("cosmos")
    61  	// RabbitMQ client span
    62  	RabbitMQSpanType = RegisteredSpanType("rabbitmq")
    63  	// Azure function span
    64  	AzureFunctionType = RegisteredSpanType("azf")
    65  	// GraphQL server span
    66  	GraphQLServerType = RegisteredSpanType("graphql.server")
    67  	// GraphQL client span
    68  	GraphQLClientType = RegisteredSpanType("graphql.client")
    69  )
    70  
    71  // RegisteredSpanType represents the span type supported by Instana
    72  type RegisteredSpanType string
    73  
    74  // extractData is a factory method to create the `data` section for a typed span
    75  func (st RegisteredSpanType) extractData(span *spanS) typedSpanData {
    76  	switch st {
    77  	case HTTPServerSpanType, HTTPClientSpanType:
    78  		return newHTTPSpanData(span)
    79  	case RPCServerSpanType, RPCClientSpanType:
    80  		return newRPCSpanData(span)
    81  	case KafkaSpanType:
    82  		return newKafkaSpanData(span)
    83  	case GCPStorageSpanType:
    84  		return newGCPStorageSpanData(span)
    85  	case GCPPubSubSpanType:
    86  		return newGCPPubSubSpanData(span)
    87  	case AWSLambdaEntrySpanType:
    88  		return newAWSLambdaSpanData(span)
    89  	case AWSS3SpanType:
    90  		return newAWSS3SpanData(span)
    91  	case AWSSQSSpanType:
    92  		return newAWSSQSSpanData(span)
    93  	case AWSSNSSpanType:
    94  		return newAWSSNSSpanData(span)
    95  	case AWSDynamoDBSpanType:
    96  		return newAWSDynamoDBSpanData(span)
    97  	case AWSLambdaInvokeSpanType:
    98  		return newAWSLambdaInvokeSpanData(span)
    99  	case LogSpanType:
   100  		return newLogSpanData(span)
   101  	case MongoDBSpanType:
   102  		return newMongoDBSpanData(span)
   103  	case PostgreSQLSpanType:
   104  		return newPostgreSQLSpanData(span)
   105  	case CouchbaseSpanType:
   106  		return newCouchbaseSpanData(span)
   107  	case CosmosSpanType:
   108  		return newCosmosSpanData(span)
   109  	case MySQLSpanType:
   110  		return newMySQLSpanData(span)
   111  	case RedisSpanType:
   112  		return newRedisSpanData(span)
   113  	case RabbitMQSpanType:
   114  		return newRabbitMQSpanData(span)
   115  	case AzureFunctionType:
   116  		return newAZFSpanData(span)
   117  	case GraphQLServerType, GraphQLClientType:
   118  		return newGraphQLSpanData(span)
   119  	default:
   120  		return newSDKSpanData(span)
   121  	}
   122  }
   123  
   124  // TagsNames returns a set of tag names known to the registered span type
   125  func (st RegisteredSpanType) TagsNames() map[string]struct{} {
   126  	var yes struct{}
   127  
   128  	switch st {
   129  	case HTTPServerSpanType, HTTPClientSpanType:
   130  		return map[string]struct{}{
   131  			"http.url": yes, string(ext.HTTPUrl): yes,
   132  			"http.status": yes, "http.status_code": yes,
   133  			"http.method": yes, string(ext.HTTPMethod): yes,
   134  			"http.path":     yes,
   135  			"http.params":   yes,
   136  			"http.header":   yes,
   137  			"http.path_tpl": yes,
   138  			"http.route_id": yes,
   139  			"http.host":     yes,
   140  			"http.protocol": yes,
   141  			"http.error":    yes,
   142  		}
   143  	case RPCServerSpanType, RPCClientSpanType:
   144  		return map[string]struct{}{
   145  			"rpc.host":      yes,
   146  			"rpc.port":      yes,
   147  			"rpc.call":      yes,
   148  			"rpc.call_type": yes,
   149  			"rpc.flavor":    yes,
   150  			"rpc.error":     yes,
   151  		}
   152  	case KafkaSpanType:
   153  		return map[string]struct{}{
   154  			"kafka.service": yes,
   155  			"kafka.access":  yes,
   156  		}
   157  	case GCPStorageSpanType:
   158  		return map[string]struct{}{
   159  			"gcs.op":                 yes,
   160  			"gcs.bucket":             yes,
   161  			"gcs.object":             yes,
   162  			"gcs.entity":             yes,
   163  			"gcs.range":              yes,
   164  			"gcs.sourceBucket":       yes,
   165  			"gcs.sourceObject":       yes,
   166  			"gcs.destinationBucket":  yes,
   167  			"gcs.destinationObject":  yes,
   168  			"gcs.numberOfOperations": yes,
   169  			"gcs.projectId":          yes,
   170  			"gcs.accessId":           yes,
   171  		}
   172  	case GCPPubSubSpanType:
   173  		return map[string]struct{}{
   174  			"gcps.projid": yes,
   175  			"gcps.op":     yes,
   176  			"gcps.top":    yes,
   177  			"gcps.sub":    yes,
   178  			"gcps.msgid":  yes,
   179  		}
   180  	case AWSLambdaEntrySpanType:
   181  		return map[string]struct{}{
   182  			"lambda.arn":                    yes,
   183  			"lambda.name":                   yes,
   184  			"lambda.version":                yes,
   185  			"lambda.trigger":                yes,
   186  			"lambda.coldStart":              yes,
   187  			"lambda.msleft":                 yes,
   188  			"lambda.error":                  yes,
   189  			"cloudwatch.events.id":          yes,
   190  			"cloudwatch.events.resources":   yes,
   191  			"cloudwatch.logs.group":         yes,
   192  			"cloudwatch.logs.stream":        yes,
   193  			"cloudwatch.logs.decodingError": yes,
   194  			"cloudwatch.logs.events":        yes,
   195  			"s3.events":                     yes,
   196  			"sqs.messages":                  yes,
   197  		}
   198  	case AWSS3SpanType:
   199  		return map[string]struct{}{
   200  			"s3.region": yes,
   201  			"s3.op":     yes,
   202  			"s3.bucket": yes,
   203  			"s3.key":    yes,
   204  			"s3.error":  yes,
   205  		}
   206  	case AWSSQSSpanType:
   207  		return map[string]struct{}{
   208  			"sqs.sort":  yes,
   209  			"sqs.queue": yes,
   210  			"sqs.type":  yes,
   211  			"sqs.group": yes,
   212  			"sqs.size":  yes,
   213  			"sqs.error": yes,
   214  		}
   215  	case AWSSNSSpanType:
   216  		return map[string]struct{}{
   217  			"sns.topic":   yes,
   218  			"sns.target":  yes,
   219  			"sns.phone":   yes,
   220  			"sns.subject": yes,
   221  			"sns.error":   yes,
   222  		}
   223  	case AWSDynamoDBSpanType:
   224  		return map[string]struct{}{
   225  			"dynamodb.table": yes,
   226  			"dynamodb.op":    yes,
   227  			"dynamodb.error": yes,
   228  		}
   229  	case AWSLambdaInvokeSpanType:
   230  		return map[string]struct{}{
   231  			"function": yes,
   232  			"type":     yes,
   233  			"error":    yes,
   234  		}
   235  	case LogSpanType:
   236  		return map[string]struct{}{
   237  			"log.message":    yes,
   238  			"log.level":      yes,
   239  			"log.parameters": yes,
   240  			"log.logger":     yes,
   241  		}
   242  	case MongoDBSpanType:
   243  		return map[string]struct{}{
   244  			"mongo.service":   yes,
   245  			"mongo.namespace": yes,
   246  			"mongo.command":   yes,
   247  			"mongo.query":     yes,
   248  			"mongo.json":      yes,
   249  			"mongo.filter":    yes,
   250  			"mongo.error":     yes,
   251  		}
   252  	case PostgreSQLSpanType:
   253  		return map[string]struct{}{
   254  			"pg.db":    yes,
   255  			"pg.user":  yes,
   256  			"pg.stmt":  yes,
   257  			"pg.host":  yes,
   258  			"pg.port":  yes,
   259  			"pg.error": yes,
   260  		}
   261  	case CouchbaseSpanType:
   262  		return map[string]struct{}{
   263  			"couchbase.bucket":   yes,
   264  			"couchbase.hostname": yes,
   265  			"couchbase.type":     yes,
   266  			"couchbase.sql":      yes,
   267  			"couchbase.error":    yes,
   268  		}
   269  	case MySQLSpanType:
   270  		return map[string]struct{}{
   271  			"mysql.db":    yes,
   272  			"mysql.user":  yes,
   273  			"mysql.stmt":  yes,
   274  			"mysql.host":  yes,
   275  			"mysql.port":  yes,
   276  			"mysql.error": yes,
   277  		}
   278  	case RedisSpanType:
   279  		return map[string]struct{}{
   280  			"redis.connection":  yes,
   281  			"redis.command":     yes,
   282  			"redis.subCommands": yes,
   283  			"redis.error":       yes,
   284  		}
   285  	case RabbitMQSpanType:
   286  		return map[string]struct{}{
   287  			"rabbitmq.exchange": yes,
   288  			"rabbitmq.key":      yes,
   289  			"rabbitmq.sort":     yes,
   290  			"rabbitmq.address":  yes,
   291  			"rabbitmq.error":    yes,
   292  		}
   293  	case AzureFunctionType:
   294  		return map[string]struct{}{
   295  			"azf.name":         yes,
   296  			"azf.functionname": yes,
   297  			"azf.methodname":   yes,
   298  			"azf.triggername":  yes,
   299  			"azf.runtime":      yes,
   300  			"azf.error":        yes,
   301  		}
   302  	case GraphQLServerType, GraphQLClientType:
   303  		return map[string]struct{}{
   304  			"graphql.operationName": yes,
   305  			"graphql.operationType": yes,
   306  			"graphql.fields":        yes,
   307  			"graphql.args":          yes,
   308  			"graphql.error":         yes,
   309  		}
   310  	default:
   311  		return nil
   312  	}
   313  }
   314  
   315  // HTTPSpanData represents the `data` section of an HTTP span sent within an OT span document
   316  type HTTPSpanData struct {
   317  	SpanData
   318  	Tags HTTPSpanTags `json:"http"`
   319  
   320  	clientSpan bool
   321  }
   322  
   323  // newHTTPSpanData initializes a new HTTP span data from tracer span
   324  func newHTTPSpanData(span *spanS) HTTPSpanData {
   325  	data := HTTPSpanData{
   326  		SpanData: NewSpanData(span, RegisteredSpanType(span.Operation)),
   327  		Tags:     newHTTPSpanTags(span),
   328  	}
   329  
   330  	kindTag := span.Tags[string(ext.SpanKind)]
   331  	data.clientSpan = kindTag == ext.SpanKindRPCClientEnum || kindTag == string(ext.SpanKindRPCClientEnum)
   332  
   333  	return data
   334  }
   335  
   336  // Kind returns instana.EntrySpanKind for server spans and instana.ExitSpanKind otherwise
   337  func (d HTTPSpanData) Kind() SpanKind {
   338  	if d.clientSpan {
   339  		return ExitSpanKind
   340  	}
   341  
   342  	return EntrySpanKind
   343  }
   344  
   345  // HTTPSpanTags contains fields within the `data.http` section of an OT span document
   346  type HTTPSpanTags struct {
   347  	// Full request/response URL
   348  	URL string `json:"url,omitempty"`
   349  	// The HTTP status code returned with client/server response
   350  	Status int `json:"status,omitempty"`
   351  	// The HTTP method of the request
   352  	Method string `json:"method,omitempty"`
   353  	// Path is the path part of the request URL
   354  	Path string `json:"path,omitempty"`
   355  	// Params are the request query string parameters
   356  	Params string `json:"params,omitempty"`
   357  	// Headers are the captured request/response headers
   358  	Headers map[string]string `json:"header,omitempty"`
   359  	// PathTemplate is the raw template string used to route the request
   360  	PathTemplate string `json:"path_tpl,omitempty"`
   361  	// RouteID is an optional name/identifier for the matched route
   362  	RouteID string `json:"route_id,omitempty"`
   363  	// The name:port of the host to which the request had been sent
   364  	Host string `json:"host,omitempty"`
   365  	// The name of the protocol used for request ("http" or "https")
   366  	Protocol string `json:"protocol,omitempty"`
   367  	// The message describing an error occurred during the request handling
   368  	Error string `json:"error,omitempty"`
   369  }
   370  
   371  // newHTTPSpanTags extracts HTTP-specific span tags from a tracer span
   372  func newHTTPSpanTags(span *spanS) HTTPSpanTags {
   373  	var tags HTTPSpanTags
   374  	for k, v := range span.Tags {
   375  		switch k {
   376  		case "http.url", string(ext.HTTPUrl):
   377  			readStringTag(&tags.URL, v)
   378  		case "http.status", "http.status_code":
   379  			readIntTag(&tags.Status, v)
   380  		case "http.method", string(ext.HTTPMethod):
   381  			readStringTag(&tags.Method, v)
   382  		case "http.path":
   383  			readStringTag(&tags.Path, v)
   384  		case "http.params":
   385  			readStringTag(&tags.Params, v)
   386  		case "http.header":
   387  			if m, ok := v.(map[string]string); ok {
   388  				tags.Headers = m
   389  			}
   390  		case "http.path_tpl":
   391  			readStringTag(&tags.PathTemplate, v)
   392  		case "http.route_id":
   393  			readStringTag(&tags.RouteID, v)
   394  		case "http.host":
   395  			readStringTag(&tags.Host, v)
   396  		case "http.protocol":
   397  			readStringTag(&tags.Protocol, v)
   398  		case "http.error":
   399  			readStringTag(&tags.Error, v)
   400  		}
   401  	}
   402  
   403  	return tags
   404  }
   405  
   406  // RPCSpanData represents the `data` section of an RPC span sent within an OT span document
   407  type RPCSpanData struct {
   408  	SpanData
   409  	Tags RPCSpanTags `json:"rpc"`
   410  
   411  	clientSpan bool
   412  }
   413  
   414  // newRPCSpanData initializes a new RPC span data from tracer span
   415  func newRPCSpanData(span *spanS) RPCSpanData {
   416  	data := RPCSpanData{
   417  		SpanData: NewSpanData(span, RegisteredSpanType(span.Operation)),
   418  		Tags:     newRPCSpanTags(span),
   419  	}
   420  
   421  	kindTag := span.Tags[string(ext.SpanKind)]
   422  	data.clientSpan = kindTag == ext.SpanKindRPCClientEnum || kindTag == (ext.SpanKindRPCClientEnum)
   423  
   424  	return data
   425  }
   426  
   427  // Kind returns instana.EntrySpanKind for server spans and instana.ExitSpanKind otherwise
   428  func (d RPCSpanData) Kind() SpanKind {
   429  	if d.clientSpan {
   430  		return ExitSpanKind
   431  	}
   432  
   433  	return EntrySpanKind
   434  }
   435  
   436  // RPCSpanTags contains fields within the `data.rpc` section of an OT span document
   437  type RPCSpanTags struct {
   438  	// The name of the remote host for an RPC call
   439  	Host string `json:"host,omitempty"`
   440  	// The port of the remote host for an RPC call
   441  	Port string `json:"port,omitempty"`
   442  	// The name of the remote method to invoke
   443  	Call string `json:"call,omitempty"`
   444  	// The type of an RPC call, e.g. either "unary" or "stream" for GRPC requests
   445  	CallType string `json:"call_type,omitempty"`
   446  	// The RPC flavor used for this call, e.g. "grpc" for GRPC requests
   447  	Flavor string `json:"flavor,omitempty"`
   448  	// The message describing an error occurred during the request handling
   449  	Error string `json:"error,omitempty"`
   450  }
   451  
   452  // newRPCSpanTags extracts RPC-specific span tags from a tracer span
   453  func newRPCSpanTags(span *spanS) RPCSpanTags {
   454  	var tags RPCSpanTags
   455  	for k, v := range span.Tags {
   456  		switch k {
   457  		case "rpc.host":
   458  			readStringTag(&tags.Host, v)
   459  		case "rpc.port":
   460  			readStringTag(&tags.Port, v)
   461  		case "rpc.call":
   462  			readStringTag(&tags.Call, v)
   463  		case "rpc.call_type":
   464  			readStringTag(&tags.CallType, v)
   465  		case "rpc.flavor":
   466  			readStringTag(&tags.Flavor, v)
   467  		case "rpc.error":
   468  			readStringTag(&tags.Error, v)
   469  		}
   470  	}
   471  
   472  	return tags
   473  }
   474  
   475  // KafkaSpanData represents the `data` section of an Kafka span sent within an OT span document
   476  type KafkaSpanData struct {
   477  	SpanData
   478  	Tags KafkaSpanTags `json:"kafka"`
   479  
   480  	producerSpan bool
   481  }
   482  
   483  // newKafkaSpanData initializes a new Kafka span data from tracer span
   484  func newKafkaSpanData(span *spanS) KafkaSpanData {
   485  	data := KafkaSpanData{
   486  		SpanData: NewSpanData(span, RegisteredSpanType(span.Operation)),
   487  		Tags:     newKafkaSpanTags(span),
   488  	}
   489  
   490  	kindTag := span.Tags[string(ext.SpanKind)]
   491  	data.producerSpan = kindTag == ext.SpanKindProducerEnum || kindTag == string(ext.SpanKindProducerEnum)
   492  
   493  	return data
   494  }
   495  
   496  // Kind returns instana.ExitSpanKind for producer spans and instana.EntrySpanKind otherwise
   497  func (d KafkaSpanData) Kind() SpanKind {
   498  	if d.producerSpan {
   499  		return ExitSpanKind
   500  	}
   501  
   502  	return EntrySpanKind
   503  }
   504  
   505  // KafkaSpanTags contains fields within the `data.kafka` section of an OT span document
   506  type KafkaSpanTags struct {
   507  	// Kafka topic
   508  	Service string `json:"service"`
   509  	// The access mode:, either "send" for publisher or "consume" for consumer
   510  	Access string `json:"access"`
   511  }
   512  
   513  // newKafkaSpanTags extracts Kafka-specific span tags from a tracer span
   514  func newKafkaSpanTags(span *spanS) KafkaSpanTags {
   515  	var tags KafkaSpanTags
   516  	for k, v := range span.Tags {
   517  		switch k {
   518  		case "kafka.service":
   519  			readStringTag(&tags.Service, v)
   520  		case "kafka.access":
   521  			readStringTag(&tags.Access, v)
   522  		}
   523  	}
   524  
   525  	return tags
   526  }
   527  
   528  // RabbitMQSpanData represents the `data` section of an RabbitMQ span
   529  type RabbitMQSpanData struct {
   530  	SpanData
   531  	Tags RabbitMQSpanTags `json:"rabbitmq"`
   532  
   533  	producerSpan bool
   534  }
   535  
   536  // newRabbitMQSpanData initializes a new RabbitMQ span data from tracer span
   537  func newRabbitMQSpanData(span *spanS) RabbitMQSpanData {
   538  	data := RabbitMQSpanData{
   539  		SpanData: NewSpanData(span, RegisteredSpanType(span.Operation)),
   540  		Tags:     newRabbitMQSpanTags(span),
   541  	}
   542  
   543  	kindTag := span.Tags[string(ext.SpanKind)]
   544  	data.producerSpan = kindTag == ext.SpanKindProducerEnum || kindTag == string(ext.SpanKindProducerEnum)
   545  
   546  	return data
   547  }
   548  
   549  // Kind returns instana.ExitSpanKind for producer spans and instana.EntrySpanKind otherwise
   550  func (d RabbitMQSpanData) Kind() SpanKind {
   551  	if d.producerSpan {
   552  		return ExitSpanKind
   553  	}
   554  
   555  	return EntrySpanKind
   556  }
   557  
   558  // RabbitMQSpanTags contains fields within the `data.rabbitmq` section
   559  type RabbitMQSpanTags struct {
   560  	// The RabbitMQ exchange name
   561  	Exchange string `json:"exchange"`
   562  	// The routing key
   563  	Key string `json:"key"`
   564  	// Indicates wether the message is being produced or consumed
   565  	Sort string `json:"sort"`
   566  	// The AMQP URI used to establish a connection to RabbitMQ
   567  	Address string `json:"address"`
   568  	// Error is the optional error that can be thrown by RabbitMQ when executing a command
   569  	Error string `json:"error,omitempty"`
   570  }
   571  
   572  // newRabbitMQSpanTags extracts RabbitMQ-specific span tags from a tracer span
   573  func newRabbitMQSpanTags(span *spanS) RabbitMQSpanTags {
   574  	var tags RabbitMQSpanTags
   575  	for k, v := range span.Tags {
   576  		switch k {
   577  		case "rabbitmq.exchange":
   578  			readStringTag(&tags.Exchange, v)
   579  		case "rabbitmq.key":
   580  			readStringTag(&tags.Key, v)
   581  		case "rabbitmq.sort":
   582  			readStringTag(&tags.Sort, v)
   583  		case "rabbitmq.address":
   584  			readStringTag(&tags.Address, v)
   585  		case "rabbitmq.error":
   586  			readStringTag(&tags.Error, v)
   587  		}
   588  	}
   589  
   590  	return tags
   591  }
   592  
   593  // GCPStorageSpanData represents the `data` section of a Google Cloud Storage span sent within an OT span document
   594  type GCPStorageSpanData struct {
   595  	SpanData
   596  	Tags GCPStorageSpanTags `json:"gcs"`
   597  }
   598  
   599  // newGCPStorageSpanData initializes a new Google Cloud Storage span data from tracer span
   600  func newGCPStorageSpanData(span *spanS) GCPStorageSpanData {
   601  	data := GCPStorageSpanData{
   602  		SpanData: NewSpanData(span, GCPStorageSpanType),
   603  		Tags:     newGCPStorageSpanTags(span),
   604  	}
   605  
   606  	return data
   607  }
   608  
   609  // Kind returns the span kind for a Google Cloud Storage span
   610  func (d GCPStorageSpanData) Kind() SpanKind {
   611  	return ExitSpanKind
   612  }
   613  
   614  // GCPStorageSpanTags contains fields within the `data.gcs` section of an OT span document
   615  type GCPStorageSpanTags struct {
   616  	Operation          string `json:"op,omitempty"`
   617  	Bucket             string `json:"bucket,omitempty"`
   618  	Object             string `json:"object,omitempty"`
   619  	Entity             string `json:"entity,omitempty"`
   620  	Range              string `json:"range,omitempty"`
   621  	SourceBucket       string `json:"sourceBucket,omitempty"`
   622  	SourceObject       string `json:"sourceObject,omitempty"`
   623  	DestinationBucket  string `json:"destinationBucket,omitempty"`
   624  	DestinationObject  string `json:"destinationObject,omitempty"`
   625  	NumberOfOperations string `json:"numberOfOperations,omitempty"`
   626  	ProjectID          string `json:"projectId,omitempty"`
   627  	AccessID           string `json:"accessId,omitempty"`
   628  }
   629  
   630  // newGCPStorageSpanTags extracts Google Cloud Storage span tags from a tracer span
   631  func newGCPStorageSpanTags(span *spanS) GCPStorageSpanTags {
   632  	var tags GCPStorageSpanTags
   633  	for k, v := range span.Tags {
   634  		switch k {
   635  		case "gcs.op":
   636  			readStringTag(&tags.Operation, v)
   637  		case "gcs.bucket":
   638  			readStringTag(&tags.Bucket, v)
   639  		case "gcs.object":
   640  			readStringTag(&tags.Object, v)
   641  		case "gcs.entity":
   642  			readStringTag(&tags.Entity, v)
   643  		case "gcs.range":
   644  			readStringTag(&tags.Range, v)
   645  		case "gcs.sourceBucket":
   646  			readStringTag(&tags.SourceBucket, v)
   647  		case "gcs.sourceObject":
   648  			readStringTag(&tags.SourceObject, v)
   649  		case "gcs.destinationBucket":
   650  			readStringTag(&tags.DestinationBucket, v)
   651  		case "gcs.destinationObject":
   652  			readStringTag(&tags.DestinationObject, v)
   653  		case "gcs.numberOfOperations":
   654  			readStringTag(&tags.NumberOfOperations, v)
   655  		case "gcs.projectId":
   656  			readStringTag(&tags.ProjectID, v)
   657  		case "gcs.accessId":
   658  			readStringTag(&tags.AccessID, v)
   659  		}
   660  	}
   661  
   662  	return tags
   663  }
   664  
   665  // GCPPubSubSpanData represents the `data` section of a Google Cloud Pub/Sub span sent within an OT span document
   666  type GCPPubSubSpanData struct {
   667  	SpanData
   668  	Tags GCPPubSubSpanTags `json:"gcps"`
   669  }
   670  
   671  // newGCPPubSubSpanData initializes a new Google Cloud Pub/Span span data from tracer span
   672  func newGCPPubSubSpanData(span *spanS) GCPPubSubSpanData {
   673  	data := GCPPubSubSpanData{
   674  		SpanData: NewSpanData(span, GCPPubSubSpanType),
   675  		Tags:     newGCPPubSubSpanTags(span),
   676  	}
   677  
   678  	return data
   679  }
   680  
   681  // Kind returns the span kind for a Google Cloud Pub/Sub span
   682  func (d GCPPubSubSpanData) Kind() SpanKind {
   683  	switch strings.ToLower(d.Tags.Operation) {
   684  	case "consume":
   685  		return EntrySpanKind
   686  	default:
   687  		return ExitSpanKind
   688  	}
   689  }
   690  
   691  // GCPPubSubSpanTags contains fields within the `data.gcps` section of an OT span document
   692  type GCPPubSubSpanTags struct {
   693  	ProjectID    string `json:"projid"`
   694  	Operation    string `json:"op"`
   695  	Topic        string `json:"top,omitempty"`
   696  	Subscription string `json:"sub,omitempty"`
   697  	MessageID    string `json:"msgid,omitempty"`
   698  }
   699  
   700  // newGCPPubSubSpanTags extracts Google Cloud Pub/Sub span tags from a tracer span
   701  func newGCPPubSubSpanTags(span *spanS) GCPPubSubSpanTags {
   702  	var tags GCPPubSubSpanTags
   703  	for k, v := range span.Tags {
   704  		switch k {
   705  		case "gcps.projid":
   706  			readStringTag(&tags.ProjectID, v)
   707  		case "gcps.op":
   708  			readStringTag(&tags.Operation, v)
   709  		case "gcps.top":
   710  			readStringTag(&tags.Topic, v)
   711  		case "gcps.sub":
   712  			readStringTag(&tags.Subscription, v)
   713  		case "gcps.msgid":
   714  			readStringTag(&tags.MessageID, v)
   715  		}
   716  	}
   717  
   718  	return tags
   719  }
   720  
   721  // AWSLambdaCloudWatchSpanTags contains fields within the `data.lambda.cw` section of an OT span document
   722  type AWSLambdaCloudWatchSpanTags struct {
   723  	Events *AWSLambdaCloudWatchEventTags `json:"events,omitempty"`
   724  	Logs   *AWSLambdaCloudWatchLogsTags  `json:"logs,omitempty"`
   725  }
   726  
   727  // newAWSLambdaCloudWatchSpanTags extracts CloudWatch tags for an AWS Lambda entry span
   728  func newAWSLambdaCloudWatchSpanTags(span *spanS) AWSLambdaCloudWatchSpanTags {
   729  	var tags AWSLambdaCloudWatchSpanTags
   730  
   731  	if events := newAWSLambdaCloudWatchEventTags(span); !events.IsZero() {
   732  		tags.Events = &events
   733  	}
   734  
   735  	if logs := newAWSLambdaCloudWatchLogsTags(span); !logs.IsZero() {
   736  		tags.Logs = &logs
   737  	}
   738  
   739  	return tags
   740  }
   741  
   742  // IsZero returns true if an AWSLambdaCloudWatchSpanTags struct was populated with event data
   743  func (tags AWSLambdaCloudWatchSpanTags) IsZero() bool {
   744  	return (tags.Events == nil || tags.Events.IsZero()) && (tags.Logs == nil || tags.Logs.IsZero())
   745  }
   746  
   747  // AWSLambdaCloudWatchEventTags contains fields within the `data.lambda.cw.events` section of an OT span document
   748  type AWSLambdaCloudWatchEventTags struct {
   749  	// ID is the ID of the event
   750  	ID string `json:"id"`
   751  	// Resources contains the event resources
   752  	Resources []string `json:"resources"`
   753  	// More is set to true if the event resources list was truncated
   754  	More bool `json:"more,omitempty"`
   755  }
   756  
   757  // newAWSLambdaCloudWatchEventTags extracts CloudWatch event tags for an AWS Lambda entry span. It truncates
   758  // the resources list to the first 3 items, populating the `data.lambda.cw.events.more` tag and limits each
   759  // resource string to the first 200 characters to reduce the payload.
   760  func newAWSLambdaCloudWatchEventTags(span *spanS) AWSLambdaCloudWatchEventTags {
   761  	var tags AWSLambdaCloudWatchEventTags
   762  
   763  	if v, ok := span.Tags["cloudwatch.events.id"]; ok {
   764  		readStringTag(&tags.ID, v)
   765  	}
   766  
   767  	if v, ok := span.Tags["cloudwatch.events.resources"]; ok {
   768  		switch v := v.(type) {
   769  		case []string:
   770  			if len(v) > 3 {
   771  				v = v[:3]
   772  				tags.More = true
   773  			}
   774  
   775  			tags.Resources = v
   776  		case string:
   777  			tags.Resources = []string{v}
   778  		case []byte:
   779  			tags.Resources = []string{string(v)}
   780  		}
   781  	}
   782  
   783  	// truncate resources
   784  	if len(tags.Resources) > 3 {
   785  		tags.Resources, tags.More = tags.Resources[:3], true
   786  	}
   787  
   788  	for i := range tags.Resources {
   789  		if len(tags.Resources[i]) > 200 {
   790  			tags.Resources[i] = tags.Resources[i][:200]
   791  		}
   792  	}
   793  
   794  	return tags
   795  }
   796  
   797  // IsZero returns true if an AWSCloudWatchEventTags struct was populated with event data
   798  func (tags AWSLambdaCloudWatchEventTags) IsZero() bool {
   799  	return tags.ID == ""
   800  }
   801  
   802  // AWSLambdaCloudWatchLogsTags contains fields within the `data.lambda.cw.logs` section of an OT span document
   803  type AWSLambdaCloudWatchLogsTags struct {
   804  	Group         string   `json:"group"`
   805  	Stream        string   `json:"stream"`
   806  	Events        []string `json:"events"`
   807  	More          bool     `json:"more,omitempty"`
   808  	DecodingError string   `json:"decodingError,omitempty"`
   809  }
   810  
   811  // newAWSLambdaCloudWatchLogsTags extracts CloudWatch Logs tags for an AWS Lambda entry span. It truncates
   812  // the log events list to the first 3 items, populating the `data.lambda.cw.logs.more` tag and limits each
   813  // log string to the first 200 characters to reduce the payload.
   814  func newAWSLambdaCloudWatchLogsTags(span *spanS) AWSLambdaCloudWatchLogsTags {
   815  	var tags AWSLambdaCloudWatchLogsTags
   816  
   817  	if v, ok := span.Tags["cloudwatch.logs.group"]; ok {
   818  		readStringTag(&tags.Group, v)
   819  	}
   820  
   821  	if v, ok := span.Tags["cloudwatch.logs.stream"]; ok {
   822  		readStringTag(&tags.Stream, v)
   823  	}
   824  
   825  	if v, ok := span.Tags["cloudwatch.logs.decodingError"]; ok {
   826  		switch v := v.(type) {
   827  		case error:
   828  			tags.DecodingError = v.Error()
   829  		case string:
   830  			tags.DecodingError = v
   831  		}
   832  	}
   833  
   834  	if v, ok := span.Tags["cloudwatch.logs.events"]; ok {
   835  		switch v := v.(type) {
   836  		case []string:
   837  			if len(v) > 3 {
   838  				v = v[:3]
   839  				tags.More = true
   840  			}
   841  
   842  			tags.Events = v
   843  		case string:
   844  			tags.Events = []string{v}
   845  		case []byte:
   846  			tags.Events = []string{string(v)}
   847  		}
   848  	}
   849  
   850  	// truncate events
   851  	if len(tags.Events) > 3 {
   852  		tags.Events, tags.More = tags.Events[:3], true
   853  	}
   854  
   855  	for i := range tags.Events {
   856  		if len(tags.Events[i]) > 200 {
   857  			tags.Events[i] = tags.Events[i][:200]
   858  		}
   859  	}
   860  
   861  	return tags
   862  }
   863  
   864  // IsZero returns true if an AWSLambdaCloudWatchLogsTags struct was populated with logs data
   865  func (tags AWSLambdaCloudWatchLogsTags) IsZero() bool {
   866  	return tags.Group == "" && tags.Stream == "" && tags.DecodingError == ""
   867  }
   868  
   869  // AWSS3EventTags represens metadata for an S3 event
   870  type AWSS3EventTags struct {
   871  	Name   string `json:"event"`
   872  	Bucket string `json:"bucket"`
   873  	Object string `json:"object,omitempty"`
   874  }
   875  
   876  // AWSLambdaS3SpanTags contains fields within the `data.lambda.s3` section of an OT span document
   877  type AWSLambdaS3SpanTags struct {
   878  	Events []AWSS3EventTags `json:"events,omitempty"`
   879  }
   880  
   881  // newAWSLambdaS3SpanTags extracts S3 Event tags for an AWS Lambda entry span. It truncates
   882  // the events list to the first 3 items and limits each object names to the first 200 characters to reduce the payload.
   883  func newAWSLambdaS3SpanTags(span *spanS) AWSLambdaS3SpanTags {
   884  	var tags AWSLambdaS3SpanTags
   885  
   886  	if events, ok := span.Tags["s3.events"]; ok {
   887  		events, ok := events.([]AWSS3EventTags)
   888  		if ok {
   889  			tags.Events = events
   890  		}
   891  	}
   892  
   893  	if len(tags.Events) > 3 {
   894  		tags.Events = tags.Events[:3]
   895  	}
   896  
   897  	for i := range tags.Events {
   898  		if len(tags.Events[i].Object) > 200 {
   899  			tags.Events[i].Object = tags.Events[i].Object[:200]
   900  		}
   901  	}
   902  
   903  	return tags
   904  }
   905  
   906  // IsZero returns true if an AWSLambdaS3SpanTags struct was populated with events data
   907  func (tags AWSLambdaS3SpanTags) IsZero() bool {
   908  	return len(tags.Events) == 0
   909  }
   910  
   911  // AWSSQSMessageTags represents span tags for an SQS message delivery
   912  type AWSSQSMessageTags struct {
   913  	Queue string `json:"queue"`
   914  }
   915  
   916  // AWSLambdaSQSSpanTags contains fields within the `data.lambda.sqs` section of an OT span document
   917  type AWSLambdaSQSSpanTags struct {
   918  	// Messages are message tags for an SQS event
   919  	Messages []AWSSQSMessageTags `json:"messages"`
   920  }
   921  
   922  // newAWSLambdaSQSSpanTags extracts SQS event tags for an AWS Lambda entry span. It truncates
   923  // the events list to the first 3 items to reduce the payload.
   924  func newAWSLambdaSQSSpanTags(span *spanS) AWSLambdaSQSSpanTags {
   925  	var tags AWSLambdaSQSSpanTags
   926  
   927  	if msgs, ok := span.Tags["sqs.messages"]; ok {
   928  		msgs, ok := msgs.([]AWSSQSMessageTags)
   929  		if ok {
   930  			tags.Messages = msgs
   931  		}
   932  	}
   933  
   934  	if len(tags.Messages) > 3 {
   935  		tags.Messages = tags.Messages[:3]
   936  	}
   937  
   938  	return tags
   939  }
   940  
   941  // IsZero returns true if an AWSLambdaSQSSpanTags struct was populated with messages data
   942  func (tags AWSLambdaSQSSpanTags) IsZero() bool {
   943  	return len(tags.Messages) == 0
   944  }
   945  
   946  // AWSLambdaSpanTags contains fields within the `data.lambda` section of an OT span document
   947  type AWSLambdaSpanTags struct {
   948  	// ARN is the ARN of invoked AWS Lambda function with the version attached
   949  	ARN string `json:"arn"`
   950  	// Runtime is an Instana constant for this AWS lambda runtime (always "go")
   951  	Runtime string `json:"runtime"`
   952  	// Name is the name of invoked function
   953  	Name string `json:"functionName,omitempty"`
   954  	// Version is either the numeric version or $LATEST
   955  	Version string `json:"functionVersion,omitempty"`
   956  	// Trigger is the trigger event type (if any)
   957  	Trigger string `json:"trigger,omitempty"`
   958  	// ColdStart is true if this is the first time current instance of the function was invoked
   959  	ColdStart bool `json:"coldStart,omitempty"`
   960  	// MillisecondsLeft is a number of milliseconds until timeout
   961  	MillisecondsLeft int `json:"msleft,omitempty"`
   962  	// Error is an AWS Lambda specific error
   963  	Error string `json:"error,omitempty"`
   964  	// CloudWatch holds the details of a CloudWatch event associated with this lambda
   965  	CloudWatch *AWSLambdaCloudWatchSpanTags `json:"cw,omitempty"`
   966  	// S3 holds the details of a S3 events associated with this lambda
   967  	S3 *AWSLambdaS3SpanTags
   968  	// SQS holds the details of a SQS events associated with this lambda
   969  	SQS *AWSLambdaSQSSpanTags
   970  }
   971  
   972  // newAWSLambdaSpanTags extracts AWS Lambda entry span tags from a tracer span
   973  func newAWSLambdaSpanTags(span *spanS) AWSLambdaSpanTags {
   974  	tags := AWSLambdaSpanTags{Runtime: "go"}
   975  
   976  	if v, ok := span.Tags["lambda.arn"]; ok {
   977  		readStringTag(&tags.ARN, v)
   978  	}
   979  
   980  	if v, ok := span.Tags["lambda.name"]; ok {
   981  		readStringTag(&tags.Name, v)
   982  	}
   983  
   984  	if v, ok := span.Tags["lambda.version"]; ok {
   985  		readStringTag(&tags.Version, v)
   986  	}
   987  
   988  	if v, ok := span.Tags["lambda.trigger"]; ok {
   989  		readStringTag(&tags.Trigger, v)
   990  	}
   991  
   992  	if v, ok := span.Tags["lambda.coldStart"]; ok {
   993  		readBoolTag(&tags.ColdStart, v)
   994  	}
   995  
   996  	if v, ok := span.Tags["lambda.msleft"]; ok {
   997  		readIntTag(&tags.MillisecondsLeft, v)
   998  	}
   999  
  1000  	if v, ok := span.Tags["lambda.error"]; ok {
  1001  		readStringTag(&tags.Error, v)
  1002  	}
  1003  
  1004  	if cw := newAWSLambdaCloudWatchSpanTags(span); !cw.IsZero() {
  1005  		tags.CloudWatch = &cw
  1006  	}
  1007  
  1008  	if st := newAWSLambdaS3SpanTags(span); !st.IsZero() {
  1009  		tags.S3 = &st
  1010  	}
  1011  
  1012  	if sqs := newAWSLambdaSQSSpanTags(span); !sqs.IsZero() {
  1013  		tags.SQS = &sqs
  1014  	}
  1015  
  1016  	return tags
  1017  }
  1018  
  1019  // AWSLambdaSpanData is the base span data type for AWS Lambda entry spans
  1020  type AWSLambdaSpanData struct {
  1021  	Snapshot AWSLambdaSpanTags `json:"lambda"`
  1022  	HTTP     *HTTPSpanTags     `json:"http,omitempty"`
  1023  }
  1024  
  1025  // newAWSLambdaSpanData initializes a new AWSLambdaSpanData from span
  1026  func newAWSLambdaSpanData(span *spanS) AWSLambdaSpanData {
  1027  	d := AWSLambdaSpanData{
  1028  		Snapshot: newAWSLambdaSpanTags(span),
  1029  	}
  1030  
  1031  	switch span.Tags["lambda.trigger"] {
  1032  	case "aws:api.gateway", "aws:application.load.balancer":
  1033  		tags := newHTTPSpanTags(span)
  1034  		d.HTTP = &tags
  1035  	}
  1036  
  1037  	return d
  1038  }
  1039  
  1040  // Type returns the span type for an AWS Lambda span
  1041  func (d AWSLambdaSpanData) Type() RegisteredSpanType {
  1042  	return AWSLambdaEntrySpanType
  1043  }
  1044  
  1045  // Kind returns the span kind for an AWS Lambda span
  1046  func (d AWSLambdaSpanData) Kind() SpanKind {
  1047  	return EntrySpanKind
  1048  }
  1049  
  1050  // AWSS3SpanData represents the `data` section of a AWS S3 span sent within an OT span document
  1051  type AWSS3SpanData struct {
  1052  	SpanData
  1053  	Tags AWSS3SpanTags `json:"s3"`
  1054  }
  1055  
  1056  // newAWSS3SpanData initializes a new AWS S3 span data from tracer span
  1057  func newAWSS3SpanData(span *spanS) AWSS3SpanData {
  1058  	data := AWSS3SpanData{
  1059  		SpanData: NewSpanData(span, AWSS3SpanType),
  1060  		Tags:     newAWSS3SpanTags(span),
  1061  	}
  1062  
  1063  	return data
  1064  }
  1065  
  1066  // Kind returns the span kind for a AWS S3 span
  1067  func (d AWSS3SpanData) Kind() SpanKind {
  1068  	return ExitSpanKind
  1069  }
  1070  
  1071  // AWSS3SpanTags contains fields within the `data.s3` section of an OT span document
  1072  type AWSS3SpanTags struct {
  1073  	// Region is the AWS region used to access S3
  1074  	Region string `json:"region,omitempty"`
  1075  	// Operation is the operation name, as defined by AWS S3 API
  1076  	Operation string `json:"op,omitempty"`
  1077  	// Bucket is the bucket name
  1078  	Bucket string `json:"bucket,omitempty"`
  1079  	// Key is the object key
  1080  	Key string `json:"key,omitempty"`
  1081  	// Error is an optional error returned by AWS API
  1082  	Error string `json:"error,omitempty"`
  1083  }
  1084  
  1085  // newAWSS3SpanTags extracts AWS S3 span tags from a tracer span
  1086  func newAWSS3SpanTags(span *spanS) AWSS3SpanTags {
  1087  	var tags AWSS3SpanTags
  1088  	for k, v := range span.Tags {
  1089  		switch k {
  1090  		case "s3.region":
  1091  			readStringTag(&tags.Region, v)
  1092  		case "s3.op":
  1093  			readStringTag(&tags.Operation, v)
  1094  		case "s3.bucket":
  1095  			readStringTag(&tags.Bucket, v)
  1096  		case "s3.key":
  1097  			readStringTag(&tags.Key, v)
  1098  		case "s3.error":
  1099  			readStringTag(&tags.Error, v)
  1100  		}
  1101  	}
  1102  
  1103  	return tags
  1104  }
  1105  
  1106  // AWSSQSSpanData represents the `data` section of a AWS SQS span sent within an OT span document
  1107  type AWSSQSSpanData struct {
  1108  	SpanData
  1109  	Tags AWSSQSSpanTags `json:"sqs"`
  1110  }
  1111  
  1112  // newAWSSQSSpanData initializes a new AWS SQS span data from tracer span
  1113  func newAWSSQSSpanData(span *spanS) AWSSQSSpanData {
  1114  	data := AWSSQSSpanData{
  1115  		SpanData: NewSpanData(span, AWSSQSSpanType),
  1116  		Tags:     newAWSSQSSpanTags(span),
  1117  	}
  1118  
  1119  	return data
  1120  }
  1121  
  1122  // Kind returns the span kind for a AWS SQS span
  1123  func (d AWSSQSSpanData) Kind() SpanKind {
  1124  	switch d.Tags.Sort {
  1125  	case "entry":
  1126  		return EntrySpanKind
  1127  	case "exit":
  1128  		return ExitSpanKind
  1129  	default:
  1130  		return IntermediateSpanKind
  1131  	}
  1132  }
  1133  
  1134  // AWSSQSSpanTags contains fields within the `data.sqs` section of an OT span document
  1135  type AWSSQSSpanTags struct {
  1136  	// Sort is the direction of the call, wither "entry" or "exit"
  1137  	Sort string `json:"sort,omitempty"`
  1138  	// Queue is the queue name
  1139  	Queue string `json:"queue,omitempty"`
  1140  	// Type is the operation name
  1141  	Type string `json:"type,omitempty"`
  1142  	// MessageGroupID is the message group ID specified while sending messages
  1143  	MessageGroupID string `json:"group,omitempty"`
  1144  	// Size is the optional batch size
  1145  	Size int `json:"size,omitempty"`
  1146  	// Error is an optional error returned by AWS API
  1147  	Error string `json:"error,omitempty"`
  1148  }
  1149  
  1150  // newAWSSQSSpanTags extracts AWS SQS span tags from a tracer span
  1151  func newAWSSQSSpanTags(span *spanS) AWSSQSSpanTags {
  1152  	var tags AWSSQSSpanTags
  1153  	for k, v := range span.Tags {
  1154  		switch k {
  1155  		case "sqs.sort":
  1156  			readStringTag(&tags.Sort, v)
  1157  		case "sqs.queue":
  1158  			readStringTag(&tags.Queue, v)
  1159  		case "sqs.type":
  1160  			readStringTag(&tags.Type, v)
  1161  		case "sqs.group":
  1162  			readStringTag(&tags.MessageGroupID, v)
  1163  		case "sqs.size":
  1164  			readIntTag(&tags.Size, v)
  1165  		case "sqs.error":
  1166  			readStringTag(&tags.Error, v)
  1167  		}
  1168  	}
  1169  
  1170  	return tags
  1171  }
  1172  
  1173  // AWSSNSSpanData represents the `data` section of a AWS SNS span sent within an OT span document
  1174  type AWSSNSSpanData struct {
  1175  	SpanData
  1176  	Tags AWSSNSSpanTags `json:"sns"`
  1177  }
  1178  
  1179  // newAWSSNSSpanData initializes a new AWS SNS span data from tracer span
  1180  func newAWSSNSSpanData(span *spanS) AWSSNSSpanData {
  1181  	data := AWSSNSSpanData{
  1182  		SpanData: NewSpanData(span, AWSSNSSpanType),
  1183  		Tags:     newAWSSNSSpanTags(span),
  1184  	}
  1185  
  1186  	return data
  1187  }
  1188  
  1189  // Kind returns the span kind for a AWS SNS span
  1190  func (d AWSSNSSpanData) Kind() SpanKind {
  1191  	return ExitSpanKind
  1192  }
  1193  
  1194  // AWSSNSSpanTags contains fields within the `data.sns` section of an OT span document
  1195  type AWSSNSSpanTags struct {
  1196  	// TopicARN is the topic ARN of an SNS message
  1197  	TopicARN string `json:"topic,omitempty"`
  1198  	// TargetARN is the target ARN of an SNS message
  1199  	TargetARN string `json:"target,omitempty"`
  1200  	// Phone is the phone no. of an SNS message
  1201  	Phone string `json:"phone,omitempty"`
  1202  	// Subject is the subject of an SNS message
  1203  	Subject string `json:"subject,omitempty"`
  1204  	// Error is an optional error returned by AWS API
  1205  	Error string `json:"error,omitempty"`
  1206  }
  1207  
  1208  // newAWSSNSSpanTags extracts AWS SNS span tags from a tracer span
  1209  func newAWSSNSSpanTags(span *spanS) AWSSNSSpanTags {
  1210  	var tags AWSSNSSpanTags
  1211  	for k, v := range span.Tags {
  1212  		switch k {
  1213  		case "sns.topic":
  1214  			readStringTag(&tags.TopicARN, v)
  1215  		case "sns.target":
  1216  			readStringTag(&tags.TargetARN, v)
  1217  		case "sns.phone":
  1218  			readStringTag(&tags.Phone, v)
  1219  		case "sns.subject":
  1220  			readStringTag(&tags.Subject, v)
  1221  		case "sns.error":
  1222  			readStringTag(&tags.Error, v)
  1223  		}
  1224  	}
  1225  
  1226  	return tags
  1227  }
  1228  
  1229  // AWSDynamoDBSpanData represents the `data` section of a AWS DynamoDB span sent within an OT span document
  1230  type AWSDynamoDBSpanData struct {
  1231  	SpanData
  1232  	Tags AWSDynamoDBSpanTags `json:"dynamodb"`
  1233  }
  1234  
  1235  // newAWSDynamoDBSpanData initializes a new AWS DynamoDB span data from tracer span
  1236  func newAWSDynamoDBSpanData(span *spanS) AWSDynamoDBSpanData {
  1237  	data := AWSDynamoDBSpanData{
  1238  		SpanData: NewSpanData(span, AWSDynamoDBSpanType),
  1239  		Tags:     newAWSDynamoDBSpanTags(span),
  1240  	}
  1241  
  1242  	return data
  1243  }
  1244  
  1245  // Kind returns the span kind for a AWS DynamoDB span
  1246  func (d AWSDynamoDBSpanData) Kind() SpanKind {
  1247  	return ExitSpanKind
  1248  }
  1249  
  1250  // AWSDynamoDBSpanTags contains fields within the `data.sns` section of an OT span document
  1251  type AWSDynamoDBSpanTags struct {
  1252  	// Table is the name of DynamoDB table
  1253  	Table string `json:"table,omitempty"`
  1254  	// Operation is the operation name
  1255  	Operation string `json:"op,omitempty"`
  1256  	// Error is an optional name returned by AWS API
  1257  	Error string `json:"error,omitempty"`
  1258  	// Region is a region from the AWS session config
  1259  	Region string `json:"region,omitempty"`
  1260  }
  1261  
  1262  // newAWSDynamoDBSpanTags extracts AWS DynamoDB span tags from a tracer span
  1263  func newAWSDynamoDBSpanTags(span *spanS) AWSDynamoDBSpanTags {
  1264  	var tags AWSDynamoDBSpanTags
  1265  	for k, v := range span.Tags {
  1266  		switch k {
  1267  		case "dynamodb.table":
  1268  			readStringTag(&tags.Table, v)
  1269  		case "dynamodb.op":
  1270  			readStringTag(&tags.Operation, v)
  1271  		case "dynamodb.error":
  1272  			readStringTag(&tags.Error, v)
  1273  		case "dynamodb.region":
  1274  			readStringTag(&tags.Region, v)
  1275  		}
  1276  	}
  1277  
  1278  	return tags
  1279  }
  1280  
  1281  // AWSInvokeSpanTags contains fields within the `aws.lambda.invoke` section of an OT span document
  1282  type AWSInvokeSpanTags struct {
  1283  	// FunctionName is a name of the function which is invoked
  1284  	FunctionName string `json:"function"`
  1285  	// InvocationType if equal to `Event`, means it is an async invocation
  1286  	InvocationType string `json:"type"`
  1287  	// Error is an optional error returned by AWS API
  1288  	Error string `json:"error,omitempty"`
  1289  }
  1290  
  1291  func newAWSDInvokeSpanTags(span *spanS) AWSInvokeSpanTags {
  1292  	var tags AWSInvokeSpanTags
  1293  	for k, v := range span.Tags {
  1294  		switch k {
  1295  		case "function":
  1296  			readStringTag(&tags.FunctionName, v)
  1297  		case "type":
  1298  			readStringTag(&tags.InvocationType, v)
  1299  		case "error":
  1300  			readStringTag(&tags.Error, v)
  1301  		}
  1302  	}
  1303  
  1304  	return tags
  1305  }
  1306  
  1307  // AWSLambdaInvokeSpanData represents the `data` section of a AWS Invoke span sent within an OT span document
  1308  type AWSLambdaInvokeSpanData struct {
  1309  	SpanData
  1310  	Tags AWSInvokeSpanTags `json:"aws.lambda.invoke"`
  1311  }
  1312  
  1313  // Kind returns the span kind for a AWS SDK Invoke span
  1314  func (d AWSLambdaInvokeSpanData) Kind() SpanKind {
  1315  	return ExitSpanKind
  1316  }
  1317  
  1318  // Type returns the span type for an AWS SDK Invoke span
  1319  func (d AWSLambdaInvokeSpanData) Type() RegisteredSpanType {
  1320  	return AWSLambdaInvokeSpanType
  1321  }
  1322  
  1323  // newAWSLambdaInvokeSpanData initializes a new AWS Invoke span data from tracer span
  1324  func newAWSLambdaInvokeSpanData(span *spanS) AWSLambdaInvokeSpanData {
  1325  	data := AWSLambdaInvokeSpanData{
  1326  		SpanData: NewSpanData(span, AWSLambdaInvokeSpanType),
  1327  		Tags:     newAWSDInvokeSpanTags(span),
  1328  	}
  1329  
  1330  	return data
  1331  }
  1332  
  1333  // LogSpanData represents the `data` section of a logging span
  1334  type LogSpanData struct {
  1335  	SpanData
  1336  	Tags LogSpanTags `json:"log"`
  1337  }
  1338  
  1339  // newLogSpanData initializes a new logging span data from tracer span
  1340  func newLogSpanData(span *spanS) LogSpanData {
  1341  	return LogSpanData{
  1342  		SpanData: NewSpanData(span, LogSpanType),
  1343  		Tags:     newLogSpanTags(span),
  1344  	}
  1345  }
  1346  
  1347  // Kind returns the span kind for a logging span
  1348  func (d LogSpanData) Kind() SpanKind {
  1349  	return ExitSpanKind
  1350  }
  1351  
  1352  // LogSpanTags contains fields within the `data.log` section of an OT span document
  1353  type LogSpanTags struct {
  1354  	// Message is a string to log
  1355  	Message string `json:"message"`
  1356  	// Level is an optional log level for this record, e.g. INFO
  1357  	Level string `json:"level,omitempty"`
  1358  	// Logger is an optional logger name
  1359  	Logger string `json:"logger,omitempty"`
  1360  	// Error is an optional error string (if any)
  1361  	Error string `json:"parameters,omitempty"`
  1362  }
  1363  
  1364  func newLogSpanTags(span *spanS) LogSpanTags {
  1365  	var tags LogSpanTags
  1366  	for k, v := range span.Tags {
  1367  		switch k {
  1368  		case "log.message":
  1369  			readStringTag(&tags.Message, v)
  1370  		case "log.level":
  1371  			readStringTag(&tags.Level, v)
  1372  		case "log.parameters":
  1373  			readStringTag(&tags.Error, v)
  1374  		case "log.logger":
  1375  			readStringTag(&tags.Logger, v)
  1376  		}
  1377  	}
  1378  
  1379  	return tags
  1380  }
  1381  
  1382  // MongoDBSpanData represents the `data` section of a MongoDB client span
  1383  type MongoDBSpanData struct {
  1384  	SpanData
  1385  	Tags MongoDBSpanTags `json:"mongo"`
  1386  }
  1387  
  1388  // newMongoDBSpanData initializes a new MongoDB clientspan data from tracer span
  1389  func newMongoDBSpanData(span *spanS) MongoDBSpanData {
  1390  	return MongoDBSpanData{
  1391  		SpanData: NewSpanData(span, MongoDBSpanType),
  1392  		Tags:     newMongoDBSpanTags(span),
  1393  	}
  1394  }
  1395  
  1396  // RedisSpanData represents the `data` section of a Redis client span
  1397  type RedisSpanData struct {
  1398  	SpanData
  1399  	Tags RedisSpanTags `json:"redis"`
  1400  }
  1401  
  1402  // newRedisSpanData initializes a new Redis clientspan data from tracer span
  1403  func newRedisSpanData(span *spanS) RedisSpanData {
  1404  	return RedisSpanData{
  1405  		SpanData: NewSpanData(span, RedisSpanType),
  1406  		Tags:     newRedisSpanTags(span),
  1407  	}
  1408  }
  1409  
  1410  // Kind returns the span kind for a Redis client span
  1411  func (d RedisSpanData) Kind() SpanKind {
  1412  	return ExitSpanKind
  1413  }
  1414  
  1415  // Kind returns the span kind for a MongoDB client span
  1416  func (d MongoDBSpanData) Kind() SpanKind {
  1417  	return ExitSpanKind
  1418  }
  1419  
  1420  // MongoDBSpanTags contains fields within the `data.mongo` section of an OT span document
  1421  type MongoDBSpanTags struct {
  1422  	// Service is the MongoDB server address in form of host:port
  1423  	Service string `json:"service"`
  1424  	// Namespace is the namespace name
  1425  	Namespace string `json:"namespace"`
  1426  	// Command is the name of the command initiated the span
  1427  	Command string `json:"command"`
  1428  	// Query is an optional query passed with command
  1429  	Query string `json:"query,omitempty"`
  1430  	// JSON is an optional JSON aggregation provided with command
  1431  	JSON string `json:"json,omitempty"`
  1432  	// Filter is an optional filter passed with command
  1433  	Filter string `json:"filter,omitempty"`
  1434  	// Error is an optional error message
  1435  	Error string `json:"error,omitempty"`
  1436  }
  1437  
  1438  func newMongoDBSpanTags(span *spanS) MongoDBSpanTags {
  1439  	var tags MongoDBSpanTags
  1440  	for k, v := range span.Tags {
  1441  		switch k {
  1442  		case "mongo.service":
  1443  			readStringTag(&tags.Service, v)
  1444  		case "mongo.namespace":
  1445  			readStringTag(&tags.Namespace, v)
  1446  		case "mongo.command":
  1447  			readStringTag(&tags.Command, v)
  1448  		case "mongo.query":
  1449  			readStringTag(&tags.Query, v)
  1450  		case "mongo.json":
  1451  			readStringTag(&tags.JSON, v)
  1452  		case "mongo.filter":
  1453  			readStringTag(&tags.Filter, v)
  1454  		case "mongo.error":
  1455  			readStringTag(&tags.Error, v)
  1456  		}
  1457  	}
  1458  
  1459  	return tags
  1460  }
  1461  
  1462  // PostgreSQLSpanData represents the `data` section of a PostgreSQL client span
  1463  type PostgreSQLSpanData struct {
  1464  	SpanData
  1465  	Tags PostgreSQLSpanTags `json:"pg"`
  1466  }
  1467  
  1468  // newPostgreSQLSpanData initializes a new PostgreSQL client span data from tracer span
  1469  func newPostgreSQLSpanData(span *spanS) PostgreSQLSpanData {
  1470  	return PostgreSQLSpanData{
  1471  		SpanData: NewSpanData(span, PostgreSQLSpanType),
  1472  		Tags:     newPostgreSQLSpanTags(span),
  1473  	}
  1474  }
  1475  
  1476  // Kind returns the span kind for a PostgreSQL client span
  1477  func (d PostgreSQLSpanData) Kind() SpanKind {
  1478  	return ExitSpanKind
  1479  }
  1480  
  1481  // PostgreSQLSpanTags contains fields within the `data.pg` section of an OT span document
  1482  type PostgreSQLSpanTags struct {
  1483  	Host string `json:"host"`
  1484  	Port string `json:"port"`
  1485  	DB   string `json:"db"`
  1486  	User string `json:"user"`
  1487  	Stmt string `json:"stmt"`
  1488  
  1489  	Error string `json:"error,omitempty"`
  1490  }
  1491  
  1492  func newPostgreSQLSpanTags(span *spanS) PostgreSQLSpanTags {
  1493  	var tags PostgreSQLSpanTags
  1494  	for k, v := range span.Tags {
  1495  		switch k {
  1496  		case "pg.host":
  1497  			readStringTag(&tags.Host, v)
  1498  		case "pg.port":
  1499  			readStringTag(&tags.Port, v)
  1500  		case "pg.db":
  1501  			readStringTag(&tags.DB, v)
  1502  		case "pg.stmt":
  1503  			readStringTag(&tags.Stmt, v)
  1504  		case "pg.user":
  1505  			readStringTag(&tags.User, v)
  1506  		case "pg.error":
  1507  			readStringTag(&tags.Error, v)
  1508  		}
  1509  	}
  1510  	return tags
  1511  }
  1512  
  1513  // CouchbaseSpanData represents the `data` section of a Couchbase client span
  1514  type CouchbaseSpanData struct {
  1515  	SpanData
  1516  	Tags CouchbaseSpanTags `json:"couchbase"`
  1517  }
  1518  
  1519  // newCouchbaseSpanData initializes a new Couchbase client span data from tracer span
  1520  func newCouchbaseSpanData(span *spanS) CouchbaseSpanData {
  1521  	return CouchbaseSpanData{
  1522  		SpanData: NewSpanData(span, CouchbaseSpanType),
  1523  		Tags:     newCouchbaseSpanTags(span),
  1524  	}
  1525  }
  1526  
  1527  // Kind returns the span kind for a Couchbase client span
  1528  func (c CouchbaseSpanData) Kind() SpanKind {
  1529  	return ExitSpanKind
  1530  }
  1531  
  1532  // CouchbaseSpanTags contains fields within the `data.couchbase` section of an OT span document
  1533  type CouchbaseSpanTags struct {
  1534  	Bucket string `json:"bucket"`
  1535  	Host   string `json:"hostname"`
  1536  	Type   string `json:"type"`
  1537  	SQL    string `json:"sql"`
  1538  
  1539  	Error string `json:"error,omitempty"`
  1540  }
  1541  
  1542  // CosmosSpanData represents the `data` section of a Cosmos client span
  1543  type CosmosSpanData struct {
  1544  	SpanData
  1545  	Tags CosmosSpanTags `json:"cosmos"`
  1546  }
  1547  
  1548  // newCosmosSpanData initializes a new Cosmos client span data from tracer span
  1549  func newCosmosSpanData(span *spanS) CosmosSpanData {
  1550  	return CosmosSpanData{
  1551  		SpanData: NewSpanData(span, CosmosSpanType),
  1552  		Tags:     newCosmosSpanTags(span),
  1553  	}
  1554  }
  1555  
  1556  // Kind returns the span kind for a Cosmos client span
  1557  func (c CosmosSpanData) Kind() SpanKind {
  1558  	return ExitSpanKind
  1559  }
  1560  
  1561  // CosmosSpanTags contains fields within the `data.cosmos` section of an OT span document
  1562  type CosmosSpanTags struct {
  1563  	ConnectionURL string `json:"con"`
  1564  	Database      string `json:"db"`
  1565  	Type          string `json:"type"`
  1566  	Sql           string `json:"cmd"`
  1567  	Object        string `json:"obj"`
  1568  	PartitionKey  string `json:"pk"`
  1569  	ReturnCode    string `json:"rt"`
  1570  	Error         string `json:"error,omitempty"`
  1571  }
  1572  
  1573  func newCosmosSpanTags(span *spanS) CosmosSpanTags {
  1574  	var tags CosmosSpanTags
  1575  	for k, v := range span.Tags {
  1576  		switch k {
  1577  		case "cosmos.con":
  1578  			readStringTag(&tags.ConnectionURL, v)
  1579  		case "cosmos.db":
  1580  			readStringTag(&tags.Database, v)
  1581  		case "cosmos.type":
  1582  			readStringTag(&tags.Type, v)
  1583  		case "cosmos.rt":
  1584  			readStringTag(&tags.ReturnCode, v)
  1585  		case "cosmos.cmd":
  1586  			readStringTag(&tags.Sql, v)
  1587  		case "cosmos.obj":
  1588  			readStringTag(&tags.Object, v)
  1589  		case "cosmos.pk":
  1590  			readStringTag(&tags.PartitionKey, v)
  1591  		case "cosmos.error":
  1592  			readStringTag(&tags.Error, v)
  1593  		}
  1594  	}
  1595  	return tags
  1596  }
  1597  
  1598  func newCouchbaseSpanTags(span *spanS) CouchbaseSpanTags {
  1599  	var tags CouchbaseSpanTags
  1600  	for k, v := range span.Tags {
  1601  		switch k {
  1602  		case "couchbase.bucket":
  1603  			readStringTag(&tags.Bucket, v)
  1604  		case "couchbase.hostname":
  1605  			readStringTag(&tags.Host, v)
  1606  		case "couchbase.type":
  1607  			readStringTag(&tags.Type, v)
  1608  		case "couchbase.sql":
  1609  			readStringTag(&tags.SQL, v)
  1610  		case "couchbase.error":
  1611  			readStringTag(&tags.Error, v)
  1612  		}
  1613  	}
  1614  	return tags
  1615  }
  1616  
  1617  // MySQLSpanData represents the `data` section of a MySQL client span
  1618  type MySQLSpanData struct {
  1619  	SpanData
  1620  	Tags MySQLSpanTags `json:"mysql"`
  1621  }
  1622  
  1623  // newMySQLSpanData initializes a new MySQL client span data from tracer span
  1624  func newMySQLSpanData(span *spanS) MySQLSpanData {
  1625  	return MySQLSpanData{
  1626  		SpanData: NewSpanData(span, MySQLSpanType),
  1627  		Tags:     newMySQLSpanTags(span),
  1628  	}
  1629  }
  1630  
  1631  // Kind returns the span kind for a MySQL client span
  1632  func (d MySQLSpanData) Kind() SpanKind {
  1633  	return ExitSpanKind
  1634  }
  1635  
  1636  // MySQLSpanTags contains fields within the `data.mysql` section of an OT span document
  1637  type MySQLSpanTags struct {
  1638  	Host string `json:"host"`
  1639  	Port string `json:"port"`
  1640  	DB   string `json:"db"`
  1641  	User string `json:"user"`
  1642  	Stmt string `json:"stmt"`
  1643  
  1644  	Error string `json:"error,omitempty"`
  1645  }
  1646  
  1647  func newMySQLSpanTags(span *spanS) MySQLSpanTags {
  1648  	var tags MySQLSpanTags
  1649  	for k, v := range span.Tags {
  1650  		switch k {
  1651  		case "mysql.host":
  1652  			readStringTag(&tags.Host, v)
  1653  		case "mysql.port":
  1654  			readStringTag(&tags.Port, v)
  1655  		case "mysql.db":
  1656  			readStringTag(&tags.DB, v)
  1657  		case "mysql.stmt":
  1658  			readStringTag(&tags.Stmt, v)
  1659  		case "mysql.user":
  1660  			readStringTag(&tags.User, v)
  1661  		case "mysql.error":
  1662  			readStringTag(&tags.Error, v)
  1663  		}
  1664  	}
  1665  	return tags
  1666  }
  1667  
  1668  // RedisSpanTags contains fields within the `data.redis` section of an OT span document
  1669  type RedisSpanTags struct {
  1670  	// Connection is the host and port where the Redis server is running
  1671  	Connection string `json:"connection"`
  1672  	// Command is the Redis command being executed
  1673  	Command string `json:"command"`
  1674  	// Subcommands is the list of commands queued when a transaction starts, eg: by using the MULTI command
  1675  	Subcommands []string `json:"subCommands,omitempty"`
  1676  	// Error is the optional error that can be thrown by Redis when executing a command
  1677  	Error string `json:"error,omitempty"`
  1678  }
  1679  
  1680  func newRedisSpanTags(span *spanS) RedisSpanTags {
  1681  	var tags RedisSpanTags
  1682  	for k, v := range span.Tags {
  1683  		switch k {
  1684  		case "redis.connection":
  1685  			readStringTag(&tags.Connection, v)
  1686  		case "redis.command":
  1687  			readStringTag(&tags.Command, v)
  1688  		case "redis.subCommands":
  1689  			readArrayStringTag(&tags.Subcommands, v)
  1690  		case "redis.error":
  1691  			readStringTag(&tags.Error, v)
  1692  		}
  1693  	}
  1694  
  1695  	return tags
  1696  }
  1697  
  1698  type AZFSpanTags struct {
  1699  	Name         string `json:"name,omitempty"`
  1700  	FunctionName string `json:"functionname,omitempty"`
  1701  	MethodName   string `json:"methodname,omitempty"`
  1702  	Trigger      string `json:"triggername,omitempty"`
  1703  	Runtime      string `json:"runtime,omitempty"`
  1704  	Error        string `json:"error,omitempty"`
  1705  }
  1706  
  1707  func newAZFSpanTags(span *spanS) AZFSpanTags {
  1708  	var tags AZFSpanTags
  1709  	for k, v := range span.Tags {
  1710  		switch k {
  1711  		case "azf.name":
  1712  			readStringTag(&tags.Name, v)
  1713  		case "azf.functionname":
  1714  			readStringTag(&tags.FunctionName, v)
  1715  		case "azf.methodname":
  1716  			readStringTag(&tags.MethodName, v)
  1717  		case "azf.triggername":
  1718  			readStringTag(&tags.Trigger, v)
  1719  		case "azf.runtime":
  1720  			readStringTag(&tags.Runtime, v)
  1721  		}
  1722  	}
  1723  
  1724  	return tags
  1725  }
  1726  
  1727  type AZFSpanData struct {
  1728  	SpanData
  1729  	Tags AZFSpanTags `json:"azf"`
  1730  }
  1731  
  1732  func newAZFSpanData(span *spanS) AZFSpanData {
  1733  	return AZFSpanData{
  1734  		SpanData: NewSpanData(span, AzureFunctionType),
  1735  		Tags:     newAZFSpanTags(span),
  1736  	}
  1737  }
  1738  
  1739  // Kind returns instana.EntrySpanKind for server spans and instana.ExitSpanKind otherwise
  1740  func (d AZFSpanData) Kind() SpanKind {
  1741  	return EntrySpanKind
  1742  }
  1743  
  1744  // GraphQLSpanData represents the `data` section of a GraphQL span sent within an OT span document
  1745  type GraphQLSpanData struct {
  1746  	SpanData
  1747  	Tags GraphQLSpanTags `json:"graphql"`
  1748  
  1749  	clientSpan bool
  1750  }
  1751  
  1752  // newGraphQLSpanData initializes a new GraphQL span data from tracer span
  1753  func newGraphQLSpanData(span *spanS) GraphQLSpanData {
  1754  	data := GraphQLSpanData{
  1755  		SpanData: NewSpanData(span, RegisteredSpanType(span.Operation)),
  1756  		Tags:     newGraphQLSpanTags(span),
  1757  	}
  1758  
  1759  	kindTag := span.Tags[string(ext.SpanKind)]
  1760  	data.clientSpan = kindTag == ext.SpanKindRPCClientEnum || kindTag == string(ext.SpanKindRPCClientEnum)
  1761  
  1762  	return data
  1763  }
  1764  
  1765  // Kind returns instana.EntrySpanKind for server spans and instana.ExitSpanKind otherwise
  1766  func (d GraphQLSpanData) Kind() SpanKind {
  1767  	if d.clientSpan {
  1768  		return ExitSpanKind
  1769  	}
  1770  
  1771  	return EntrySpanKind
  1772  }
  1773  
  1774  // GraphQLSpanTags contains fields within the `data.graphql` section of an OT span document
  1775  type GraphQLSpanTags struct {
  1776  	OperationName string              `json:"operationName,omitempty"`
  1777  	OperationType string              `json:"operationType,omitempty"`
  1778  	Fields        map[string][]string `json:"fields,omitempty"`
  1779  	Args          map[string][]string `json:"args,omitempty"`
  1780  	Error         string              `json:"error,omitempty"`
  1781  }
  1782  
  1783  // newGraphQLSpanTags extracts GraphQL-specific span tags from a tracer span
  1784  func newGraphQLSpanTags(span *spanS) GraphQLSpanTags {
  1785  	var tags GraphQLSpanTags
  1786  	for k, v := range span.Tags {
  1787  		switch k {
  1788  		case "graphql.operationName":
  1789  			readStringTag(&tags.OperationName, v)
  1790  		case "graphql.operationType":
  1791  			readStringTag(&tags.OperationType, v)
  1792  		case "graphql.fields":
  1793  			readMapOfStringSlicesTag(&tags.Fields, v)
  1794  		case "graphql.args":
  1795  			readMapOfStringSlicesTag(&tags.Args, v)
  1796  		case "graphql.error":
  1797  			readStringTag(&tags.Error, v)
  1798  		}
  1799  	}
  1800  
  1801  	return tags
  1802  }