Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit 40af615

Browse files
authored
Add Stackdriver Trace V2 models (#87)
1 parent 7b56070 commit 40af615

28 files changed

Lines changed: 1617 additions & 192 deletions

opencensus/trace/attributes.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright 2017, OpenCensus Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from opencensus.trace import utils
16+
17+
18+
def _format_attribute_value(value):
19+
if isinstance(value, bool):
20+
value_type = 'bool_value'
21+
elif isinstance(value, int):
22+
value_type = 'int_value'
23+
elif isinstance(value, str):
24+
value_type = 'string_value'
25+
value = utils._get_truncatable_str(value)
26+
else:
27+
return None
28+
29+
return {value_type: value}
30+
31+
32+
class Attributes(object):
33+
"""A set of attributes, each in the format [KEY]:[VALUE].
34+
35+
:type attributes: dict
36+
:param attributes: The set of attributes. Each attribute's key can be up
37+
to 128 bytes long. The value can be a string up to 256
38+
bytes, an integer, or the Boolean values true and false.
39+
"""
40+
def __init__(self, attributes=None):
41+
self.attributes = dict(attributes or {})
42+
43+
def set_attribute(self, key, value):
44+
"""Set a key value pair."""
45+
self.attributes[key] = value
46+
47+
def delete_attribute(self, key):
48+
"""Delete an attribute given a key if existed."""
49+
self.attributes.pop(key, None)
50+
51+
def get_attribute(self, key):
52+
"""Get a attribute value."""
53+
return self.attributes.get(key, None)
54+
55+
def format_attributes_json(self):
56+
"""Convert the Attributes object to json format."""
57+
attributes_json = {
58+
utils.check_str_length(key)[0]: _format_attribute_value(value)
59+
for key, value in self.attributes.items()
60+
}
61+
62+
attributes_json = {}
63+
64+
for key, value in self.attributes.items():
65+
key = utils.check_str_length(key)[0]
66+
value = _format_attribute_value(value)
67+
68+
if value is not None:
69+
attributes_json[key] = value
70+
71+
result = {
72+
'attributeMap': attributes_json
73+
}
74+
75+
return result

opencensus/trace/enums.py

Lines changed: 0 additions & 41 deletions
This file was deleted.

opencensus/trace/exporters/stackdriver_exporter.py

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from google.cloud.trace.client import Client
2121

22+
2223
# Environment variable set in App Engine when vm:true is set.
2324
_APPENGINE_FLEXIBLE_ENV_VM = 'GAE_APPENGINE_HOSTNAME'
2425

@@ -90,54 +91,67 @@ class StackdriverExporter(base.Exporter):
9091
"""
9192
def __init__(self, client=None, project_id=None,
9293
transport=sync.SyncTransport):
93-
# The client will handler the case when project_id is None
94+
# The client will handle the case when project_id is None
9495
if client is None:
9596
client = Client(project=project_id)
9697

9798
self.client = client
9899
self.project_id = client.project
99100
self.transport = transport(self)
100101

101-
def emit(self, trace):
102+
def emit(self, spans):
102103
"""
103-
:type trace: dict
104-
:param trace: Trace collected.
104+
:type spans: dict
105+
:param spans: Spans collected.
105106
"""
106-
stackdriver_traces = self.translate_to_stackdriver(trace)
107-
self.client.patch_traces(stackdriver_traces)
107+
name = 'projects/{}'.format(self.project_id)
108+
stackdriver_spans = self.translate_to_stackdriver(spans)
109+
self.client.batch_write_spans(name, stackdriver_spans)
108110

109111
def export(self, trace):
110112
self.transport.export(trace)
111113

112-
def translate_to_stackdriver(self, trace):
113-
"""
114-
:type trace: dict
115-
:param trace: Trace collected.
114+
def translate_to_stackdriver(self, spans):
115+
"""Translate the spans json to Stackdriver format.
116+
117+
See: https://cloud.google.com/trace/docs/reference/v2/rest/v2/
118+
projects.traces/batchWrite
119+
120+
:type spans: dict
121+
:param spans: Spans collected.
116122
117123
:rtype: dict
118-
:returns: Traces in Google Cloud StackDriver Trace format.
124+
:returns: Spans in Google Cloud StackDriver Trace format.
119125
"""
120-
set_attributes(trace)
121-
spans = trace.get('spans')
122-
trace_id = trace.get('traceId')
123-
spans_json = []
126+
set_attributes(spans)
127+
spans_json = spans.get('spans')
128+
trace_id = spans.get('traceId')
129+
spans_list = []
130+
131+
for span in spans_json:
132+
span_name = 'projects/{}/traces/{}/spans/{}'.format(
133+
self.project_id, trace_id, span.get('spanId'))
124134

125-
for span in spans:
126135
span_json = {
127-
'name': span.get('name'),
136+
'name': span_name,
137+
'displayName': span.get('displayName'),
128138
'startTime': span.get('startTime'),
129139
'endTime': span.get('endTime'),
130-
'spanId': span.get('spanId'),
131-
'parentSpanId': span.get('parentSpanId'),
132-
'labels': span.get('attributes')
140+
'spanId': str(span.get('spanId')),
141+
'attributes': span.get('attributes'),
142+
'links': span.get('links'),
143+
'status': span.get('status'),
144+
'stackTrace': span.get('stackTrace'),
145+
'timeEvents': span.get('timeEvents'),
146+
'sameProcessAsParentSpan': span.get('sameProcessAsParentSpan'),
147+
'childSpanCount': span.get('childSpanCount')
133148
}
134-
spans_json.append(span_json)
135149

136-
trace_json = {
137-
'projectId': self.project_id,
138-
'traceId': trace_id,
139-
'spans': spans_json
140-
}
150+
if span.get('parentSpanId') is not None:
151+
parent_span_id = str(span.get('parentSpanId'))
152+
span_json['parentSpanId'] = parent_span_id
153+
154+
spans_list.append(span_json)
141155

142-
traces = {'traces': [trace_json]}
143-
return traces
156+
spans = {'spans': spans_list}
157+
return spans

opencensus/trace/ext/sqlalchemy/trace.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def _before_cursor_execute(conn, cursor, statement, parameters,
6262

6363
_tracer = execution_context.get_opencensus_tracer()
6464
_span = _tracer.start_span()
65-
_span.name = '[{}.query]{}'.format(MODULE_NAME, statement)
65+
_span.name = '{}.query'.format(MODULE_NAME)
6666

6767
# Set query statement attribute
6868
_tracer.add_attribute_to_current_span(

opencensus/trace/link.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright 2017, OpenCensus Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
class Type(object):
17+
"""The relationship of the current span relative to the linked span: child,
18+
parent, or unspecified.
19+
20+
Attributes:
21+
TYPE_UNSPECIFIED (int): The relationship of the two spans is unknown.
22+
CHILD_LINKED_SPAN (int): The linked span is a child of the current span.
23+
PARENT_LINKED_SPAN (int): The linked span is a parent of the current
24+
span.
25+
"""
26+
TYPE_UNSPECIFIED = 0
27+
CHILD_LINKED_SPAN = 1
28+
PARENT_LINKED_SPAN = 2
29+
30+
31+
class Link(object):
32+
"""A pointer from the current span to another span in the same trace or in
33+
a different trace. For example, this can be used in batching operations,
34+
where a single batch handler processes multiple requests from different
35+
traces or when the handler receives a request from a different project.
36+
37+
:type trace_id: str
38+
:param trace_id: The [TRACE_ID] for a trace within a project.
39+
40+
:type span_id: str
41+
:param span_id: The [SPAN_ID] for a span within a trace.
42+
43+
:type type: Enum of :class:`~opencensus.trace.link.Type`
44+
:param type: The relationship of the current span relative to the linked
45+
span.
46+
47+
:type attributes: :class:`~opencensus.trace.attributes.Attributes`
48+
:param attributes: A set of attributes on the link. You have have up to 32
49+
attributes per link.
50+
"""
51+
def __init__(self, trace_id, span_id, type=None, attributes=None):
52+
self.trace_id = trace_id
53+
self.span_id = span_id
54+
55+
if type is None:
56+
type = Type.TYPE_UNSPECIFIED
57+
58+
self.type = type
59+
self.attributes = attributes
60+
61+
def format_link_json(self):
62+
"""Convert a Link object to json format."""
63+
link_json = {}
64+
link_json['trace_id'] = self.trace_id
65+
link_json['span_id'] = self.span_id
66+
link_json['type'] = self.type
67+
68+
if self.attributes is not None:
69+
link_json['attributes'] = self.attributes
70+
71+
return link_json

0 commit comments

Comments
 (0)