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

Commit 456ee2c

Browse files
kcoopersteinliyanhui1228
authored andcommitted
Tag Propagation (#209)
1 parent 0dee5c8 commit 456ee2c

5 files changed

Lines changed: 194 additions & 2 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2018, 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.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Copyright 2018, 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+
# -*- coding: utf-8 -*-
16+
17+
import logging
18+
import six
19+
20+
from google.protobuf.internal.encoder import _VarintBytes
21+
22+
from opencensus.tags import tag_map as tag_map_module
23+
24+
# Used for decoding hex bytes to hex string.
25+
UTF8 = 'utf-8'
26+
27+
VERSION_ID = 0
28+
TAG_FIELD_ID = 0
29+
TAG_MAP_SERIALIZED_SIZE_LIMIT = 8192
30+
31+
32+
class BinarySerializer(object):
33+
def from_byte_array(self, binary):
34+
if len(binary) <= 0:
35+
logging.warning("Input byte[] cannot be empty/")
36+
return tag_map_module.TagMap()
37+
else:
38+
buffer = memoryview(binary)
39+
version_id = buffer[0]
40+
if six.PY2:
41+
version_id = ord(version_id)
42+
if version_id != VERSION_ID:
43+
raise ValueError("Invalid version id.")
44+
return self._parse_tags(buffer)
45+
46+
def to_byte_array(self, tag_context):
47+
encoded_bytes = b''
48+
encoded_bytes += _VarintBytes(VERSION_ID)
49+
total_chars = 0
50+
for tag in tag_context.tags:
51+
for tag_key, tag_value in tag.items():
52+
total_chars += len(tag_key)
53+
total_chars += len(tag_value)
54+
encoded_bytes = self._encode_tag(
55+
tag_key, tag_value, encoded_bytes)
56+
if total_chars <= TAG_MAP_SERIALIZED_SIZE_LIMIT:
57+
return encoded_bytes
58+
else: # pragma: NO COVER
59+
logging.warning("Size of the tag context exceeds the maximum size")
60+
61+
def _parse_tags(self, buffer):
62+
tag_context = tag_map_module.TagMap()
63+
limit = len(buffer)
64+
total_chars = 0
65+
i = 1
66+
while i < limit:
67+
field_id = buffer[i] if six.PY3 else ord(buffer[i])
68+
if field_id == TAG_FIELD_ID:
69+
i += 1
70+
key = self._decode_string(buffer, i)
71+
i += len(key)
72+
total_chars += len(key)
73+
i += 1
74+
val = self._decode_string(buffer, i)
75+
i += len(val)
76+
total_chars += len(val)
77+
i += 1
78+
if total_chars > \
79+
TAG_MAP_SERIALIZED_SIZE_LIMIT: # pragma: NO COVER
80+
logging.warning("Size of the tag context exceeds maximum")
81+
break
82+
else:
83+
tag_context.insert(str(key), str(val))
84+
else:
85+
break
86+
return tag_context
87+
88+
def _encode_tag(self, tag_key, tag_value, encoded_bytes):
89+
encoded_bytes += _VarintBytes(TAG_FIELD_ID)
90+
encoded_bytes = self._encode_string(tag_key, encoded_bytes)
91+
encoded_bytes = self._encode_string(tag_value, encoded_bytes)
92+
return encoded_bytes
93+
94+
def _encode_string(self, input_str, encoded_bytes):
95+
encoded_bytes += _VarintBytes(len(input_str))
96+
encoded_bytes += input_str.encode(UTF8)
97+
return encoded_bytes
98+
99+
def _decode_string(self, buffer, pos):
100+
length = buffer[pos] if six.PY3 else ord(buffer[pos])
101+
builder = ""
102+
i = 1
103+
while i <= length:
104+
bytes_to_decode = buffer[pos + i] if six.PY3 \
105+
else ord(buffer[pos + i])
106+
builder += _VarintBytes(bytes_to_decode).decode()
107+
i += 1
108+
return builder

opencensus/tags/tag_map.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(self, tags=None):
2929
self._map[tag_key] = tag_value
3030

3131
else:
32-
self.tags = {}
32+
self._map = {}
3333

3434
@property
3535
def map(self):
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Copyright 2018, 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+
import unittest
16+
17+
from opencensus.tags.propagation import binary_serializer
18+
19+
20+
class TestBinarySerializer(unittest.TestCase):
21+
def test_from_byte_array_input_empty(self):
22+
binary = bytearray(b'')
23+
propagator = binary_serializer.BinarySerializer()
24+
tag_context = propagator.from_byte_array(binary=binary)
25+
expected_tags = {}
26+
27+
self.assertEqual(expected_tags, tag_context.map)
28+
29+
def test_from_byte_array_invalid_version_id(self):
30+
binary = bytearray(b'\x04key1\x04val1')
31+
propagator = binary_serializer.BinarySerializer()
32+
with self.assertRaises(ValueError):
33+
propagator.from_byte_array(binary=binary)
34+
35+
def test_from_byte_array(self):
36+
binary = bytearray(b'\x00\x00\x04key1\x04val1\x00\x04key2\x04val2'
37+
b'\x00\x04key3\x04val3')
38+
propagator = binary_serializer.BinarySerializer()
39+
tag_context = propagator.from_byte_array(binary=binary)
40+
expected_tags = {'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}
41+
42+
self.assertEqual(expected_tags, tag_context.map)
43+
44+
def test_to_byte_array(self):
45+
from opencensus.tags.tag_map import TagMap
46+
from collections import OrderedDict
47+
48+
tag_map = OrderedDict(
49+
[('key1', 'val1'), ('key2', 'val2'),
50+
('key3', 'val3'), ('key4', 'val4')])
51+
tags = [tag_map]
52+
tag_context = TagMap(tags=tags)
53+
propagator = binary_serializer.BinarySerializer()
54+
binary = propagator.to_byte_array(tag_context)
55+
56+
expected_binary = b'\x00\x00\x04key1\x04val1\x00\x04key2\x04val2\x00' \
57+
b'\x04key3\x04val3\x00\x04key4\x04val4'
58+
59+
self.assertEqual(binary, expected_binary)
60+
61+
def test__parse_tags_invalid_field_id(self):
62+
from collections import OrderedDict
63+
64+
propagator = binary_serializer.BinarySerializer()
65+
binary = bytearray(b'\x00\x00\x04key1\x04val1\x04key2\x04val2'
66+
b'\x00\x04key3\x04val3')
67+
buffer = memoryview(binary)
68+
tag_context = propagator._parse_tags(buffer)
69+
expected_dict = OrderedDict(
70+
[('key1', 'val1')])
71+
72+
self.assertEqual(frozenset(tag_context.map), frozenset(expected_dict))

tests/unit/tags/test_tag_map.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ class TestTagMap(unittest.TestCase):
2121

2222
def test_constructor_defaults(self):
2323
tag_map = tag_map_module.TagMap()
24-
self.assertEqual(tag_map.tags, {})
2524
self.assertEqual(tag_map.map, {})
2625

2726
def test_constructor_explicit(self):

0 commit comments

Comments
 (0)