1+ """
2+ This module contains the classes that represents spans.
3+
4+ InstanaSpan - the OpenTracing based span used during tracing
5+
6+ When an InstanaSpan is finished, it is converted into either an SDKSpan
7+ or RegisteredSpan depending on type.
8+
9+ BaseSpan: Base class containing the commonalities for the two descendants
10+ - SDKSpan: Class that represents an SDK type span
11+ - RegisteredSpan: Class that represents a Registered type span
12+ """
113import six
2- import sys
3- from .log import logger
4- from .util import DictionaryOfStan
14+
515from basictracer .span import BasicSpan
616import opentracing .ext .tags as ot_tags
717
8-
9- class SpanContext ():
10- def __init__ (
11- self ,
12- trace_id = None ,
13- span_id = None ,
14- baggage = None ,
15- sampled = True ,
16- level = 1 ,
17- synthetic = False ):
18-
19- self .level = level
20- self .trace_id = trace_id
21- self .span_id = span_id
22- self .sampled = sampled
23- self .synthetic = synthetic
24- self ._baggage = baggage or {}
25-
26- @property
27- def baggage (self ):
28- return self ._baggage
29-
30- def with_baggage_item (self , key , value ):
31- new_baggage = self ._baggage .copy ()
32- new_baggage [key ] = value
33- return SpanContext (
34- trace_id = self .trace_id ,
35- span_id = self .span_id ,
36- sampled = self .sampled ,
37- baggage = new_baggage )
18+ from .log import logger
19+ from .util import DictionaryOfStan
3820
3921
4022class InstanaSpan (BasicSpan ):
4123 stack = None
4224 synthetic = False
4325
44- def finish (self , finish_time = None ):
45- super (InstanaSpan , self ).finish (finish_time )
26+ def __init__ (self , tracer , operation_name = None , context = None , parent_id = None , tags = None , start_time = None ):
27+ # Tag validation
28+ filtered_tags = {}
29+ if tags is not None :
30+ for key in tags .keys ():
31+ validated_key , validated_value = self ._validate_tag (key , tags [key ])
32+ if validated_key is not None :
33+ filtered_tags [validated_key ] = validated_value
34+
35+ super (InstanaSpan , self ).__init__ (tracer , operation_name , context , parent_id , filtered_tags , start_time )
36+
37+ def _validate_tag (self , key , value ):
38+ """
39+ This method will assure that <key> and <value> are valid to set as a tag.
40+ If <value> fails the check, an attempt will be made to convert it into
41+ something useful.
42+
43+ On check failure, this method will return None values indicating that the tag is
44+ not valid and could not be converted into something useful
45+
46+ :param key: The tag key
47+ :param value: The tag value
48+ :return: Tuple (key, value)
49+ """
50+ validated_key = None
51+ validated_value = None
52+
53+ try :
54+ # Tag keys must be some type of text or string type
55+ if isinstance (key , (six .text_type , six .string_types )):
56+ validated_key = key [0 :1024 ] # Max key length of 1024 characters
57+
58+ if isinstance (value , (bool , float , int , list , dict , six .text_type , six .string_types )):
59+ validated_value = value
60+ else :
61+ validated_value = self ._convert_tag_value (value )
62+ else :
63+ logger .debug ("(non-fatal) tag names must be strings. tag discarded for %s" , type (key ))
64+ except Exception :
65+ logger .debug ("instana.span._validate_tag: " , exc_info = True )
66+
67+ return (validated_key , validated_value )
68+
69+ def _convert_tag_value (self , value ):
70+ final_value = None
71+
72+ try :
73+ final_value = repr (value )
74+ except Exception :
75+ final_value = "(non-fatal) span.set_tag: values must be one of these types: bool, float, int, list, " \
76+ "set, str or alternatively support 'repr'. tag discarded"
77+ logger .debug (final_value , exc_info = True )
78+ return None
79+ return final_value
4680
4781 def set_tag (self , key , value ):
48- # Key validation
49- if not isinstance (key , six .text_type ) and not isinstance (key , six .string_types ) :
50- logger .debug ("(non-fatal) span.set_tag: tag names must be strings. tag discarded for %s" , type (key ))
51- return self
82+ validated_key , validated_value = self ._validate_tag (key , value )
5283
53- final_value = value
54- value_type = type ( value )
84+ if validated_key is not None and validated_value is not None :
85+ return super ( InstanaSpan , self ). set_tag ( validated_key , validated_value )
5586
56- # Value validation
57- if value_type in [bool , float , int , list , str ]:
58- return super (InstanaSpan , self ).set_tag (key , final_value )
87+ return self
5988
60- elif isinstance (value , six .text_type ):
61- final_value = str (value )
89+ def log_kv (self , key_values , timestamp = None ):
90+ validated_key = None
91+ validated_value = None
6292
63- else :
64- try :
65- final_value = repr (value )
66- except :
67- final_value = "(non-fatal) span.set_tag: values must be one of these types: bool, float, int, list, " \
68- "set, str or alternatively support 'repr'. tag discarded"
69- logger .debug (final_value , exc_info = True )
70- return self
93+ for key in key_values .keys ():
94+ validated_key , validated_value = self ._validate_tag (key , key_values [key ])
95+
96+ if validated_key is not None and validated_value is not None :
97+ return super (InstanaSpan , self ).log_kv ({validated_key : validated_value }, timestamp )
7198
72- return super ( InstanaSpan , self ). set_tag ( key , final_value )
99+ return self
73100
74101 def mark_as_errored (self , tags = None ):
75102 """
@@ -81,7 +108,7 @@ def mark_as_errored(self, tags = None):
81108 ec = self .tags .get ('ec' , 0 )
82109 self .set_tag ('ec' , ec + 1 )
83110
84- if tags is not None and type (tags ) is dict :
111+ if tags is not None and isinstance (tags , dict ) :
85112 for key in tags :
86113 self .set_tag (key , tags [key ])
87114 except Exception :
@@ -99,7 +126,7 @@ def assure_errored(self):
99126 except Exception :
100127 logger .debug ('span.assure_errored' , exc_info = True )
101128
102- def log_exception (self , e ):
129+ def log_exception (self , exc ):
103130 """
104131 Log an exception onto this span. This will log pertinent info from the exception and
105132 assure that this span is marked as errored.
@@ -110,12 +137,12 @@ def log_exception(self, e):
110137 message = ""
111138 self .mark_as_errored ()
112139
113- if hasattr (e , '__str__' ) and len (str (e )) > 0 :
114- message = str (e )
115- elif hasattr (e , 'message' ) and e .message is not None :
116- message = e .message
140+ if hasattr (exc , '__str__' ) and len (str (exc )) > 0 :
141+ message = str (exc )
142+ elif hasattr (exc , 'message' ) and exc .message is not None :
143+ message = exc .message
117144 else :
118- message = repr (e )
145+ message = repr (exc )
119146
120147 if self .operation_name in ['rpc-server' , 'rpc-client' ]:
121148 self .set_tag ('rpc.error' , message )
@@ -133,39 +160,18 @@ def log_exception(self, e):
133160 logger .debug ("span.log_exception" , exc_info = True )
134161 raise
135162
136- def collect_logs (self ):
137- """
138- Collect up log data and feed it to the Instana brain.
139-
140- :param span: The span to search for logs in
141- :return: Logs ready for consumption by the Instana brain.
142- """
143- logs = {}
144- for log in self .logs :
145- ts = int (round (log .timestamp * 1000 ))
146- if ts not in logs :
147- logs [ts ] = {}
148-
149- if 'message' in log .key_values :
150- logs [ts ]['message' ] = log .key_values ['message' ]
151- if 'event' in log .key_values :
152- logs [ts ]['event' ] = log .key_values ['event' ]
153- if 'parameters' in log .key_values :
154- logs [ts ]['parameters' ] = log .key_values ['parameters' ]
155-
156- return logs
157-
158163
159164class BaseSpan (object ):
160165 sy = None
161-
166+
162167 def __str__ (self ):
163168 return "BaseSpan(%s)" % self .__dict__ .__str__ ()
164169
165170 def __repr__ (self ):
166171 return self .__dict__ .__str__ ()
167172
168173 def __init__ (self , span , source , service_name , ** kwargs ):
174+ # pylint: disable=invalid-name
169175 self .t = span .context .trace_id
170176 self .p = span .parent_id
171177 self .s = span .context .span_id
@@ -189,6 +195,7 @@ class SDKSpan(BaseSpan):
189195 EXIT_KIND = ["exit" , "client" , "producer" ]
190196
191197 def __init__ (self , span , source , service_name , ** kwargs ):
198+ # pylint: disable=invalid-name
192199 super (SDKSpan , self ).__init__ (span , source , service_name , ** kwargs )
193200
194201 span_kind = self .get_span_kind (span )
@@ -202,13 +209,18 @@ def __init__(self, span, source, service_name, **kwargs):
202209 self .data ["sdk" ]["name" ] = span .operation_name
203210 self .data ["sdk" ]["type" ] = span_kind [0 ]
204211 self .data ["sdk" ]["custom" ]["tags" ] = span .tags
205- self .data ["sdk" ]["custom" ]["logs" ] = span .logs
212+
213+ if span .logs is not None and len (span .logs ) > 0 :
214+ logs = DictionaryOfStan ()
215+ for log in span .logs :
216+ logs [repr (log .timestamp )] = log .key_values
217+ self .data ["sdk" ]["custom" ]["logs" ] = logs
206218
207219 if "arguments" in span .tags :
208- self .data . sdk . arguments = span .tags ["arguments" ]
220+ self .data [ ' sdk' ][ ' arguments' ] = span .tags ["arguments" ]
209221
210222 if "return" in span .tags :
211- self .data . sdk . Return = span .tags ["return" ]
223+ self .data [ ' sdk' ][ 'return' ] = span .tags ["return" ]
212224
213225 if len (span .context .baggage ) > 0 :
214226 self .data ["baggage" ] = span .context .baggage
@@ -244,6 +256,7 @@ class RegisteredSpan(BaseSpan):
244256 LOCAL_SPANS = ("render" )
245257
246258 def __init__ (self , span , source , service_name , ** kwargs ):
259+ # pylint: disable=invalid-name
247260 super (RegisteredSpan , self ).__init__ (span , source , service_name , ** kwargs )
248261 self .n = span .operation_name
249262
@@ -263,7 +276,7 @@ def __init__(self, span, source, service_name, **kwargs):
263276 self .k = 1 # entry
264277
265278 # Store any leftover tags in the custom section
266- if len (span .tags ):
279+ if len (span .tags ) > 0 :
267280 self .data ["custom" ]["tags" ] = span .tags
268281
269282 def _populate_entry_span_data (self , span ):
0 commit comments