diff --git a/contrib/opencensus-ext-ocagent/tests/test_trace_exporter_utils.py b/contrib/opencensus-ext-ocagent/tests/test_trace_exporter_utils.py index 52de1aa27..5e0231177 100644 --- a/contrib/opencensus-ext-ocagent/tests/test_trace_exporter_utils.py +++ b/contrib/opencensus-ext-ocagent/tests/test_trace_exporter_utils.py @@ -16,6 +16,7 @@ from datetime import datetime, timedelta import unittest +from opencensus.common import utils as common_utils from opencensus.ext.ocagent.trace_exporter import utils from opencensus.ext.ocagent.trace_exporter.gen.opencensus.trace.v1 \ import trace_pb2 @@ -532,7 +533,8 @@ def test_datetime_str_to_proto_ts_conversion(self): expected_seconds = int(delta.total_seconds()) expected_nanos = delta.microseconds * 1000 - proto_ts = utils.proto_ts_from_datetime_str(now.isoformat() + 'Z') + proto_ts = utils.proto_ts_from_datetime_str( + common_utils.to_iso_str(now)) self.assertEqual(proto_ts.seconds, int(expected_seconds)) self.assertEqual(proto_ts.nanos, expected_nanos) diff --git a/opencensus/common/utils/__init__.py b/opencensus/common/utils/__init__.py index d8646ecd8..9ed6b0afe 100644 --- a/opencensus/common/utils/__init__.py +++ b/opencensus/common/utils/__init__.py @@ -69,6 +69,13 @@ def check_str_length(str_to_check, limit=MAX_LENGTH): return (result, truncated_byte_count) +def to_iso_str(ts=None): + """Get an ISO 8601 string for a UTC datetime.""" + if ts is None: + ts = datetime.datetime.utcnow() + return ts.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + + def timestamp_to_microseconds(timestamp): """Convert a timestamp string into a microseconds value :param timestamp diff --git a/opencensus/stats/measurement_map.py b/opencensus/stats/measurement_map.py index 2c1dab23a..8cdf2fb4d 100644 --- a/opencensus/stats/measurement_map.py +++ b/opencensus/stats/measurement_map.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from datetime import datetime import logging +from opencensus.common import utils from opencensus.tags import execution_context @@ -114,6 +114,6 @@ def record(self, tag_map_tags=None): self.measure_to_view_map.record( tags=tag_map_tags, measurement_map=self.measurement_map, - timestamp=datetime.utcnow().isoformat() + 'Z', + timestamp=utils.to_iso_str(), attachments=self.attachments ) diff --git a/opencensus/stats/view_data.py b/opencensus/stats/view_data.py index 52b43d2ab..47adaa33d 100644 --- a/opencensus/stats/view_data.py +++ b/opencensus/stats/view_data.py @@ -12,9 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from datetime import datetime import copy +from opencensus.common import utils + class ViewData(object): """View Data is the aggregated data for a particular view @@ -62,11 +63,11 @@ def tag_value_aggregation_data_map(self): def start(self): """sets the start time for the view data""" - self._start_time = datetime.utcnow().isoformat() + 'Z' + self._start_time = utils.to_iso_str() def end(self): """sets the end time for the view data""" - self._end_time = datetime.utcnow().isoformat() + 'Z' + self._end_time = utils.to_iso_str() def get_tag_values(self, tags, columns): """function to get the tag values from tags and columns""" diff --git a/opencensus/stats/view_manager.py b/opencensus/stats/view_manager.py index c7c32d0f2..2afaea22c 100644 --- a/opencensus/stats/view_manager.py +++ b/opencensus/stats/view_manager.py @@ -12,16 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +from opencensus.common import utils from opencensus.stats.measure_to_view_map import MeasureToViewMap from opencensus.stats import execution_context -from datetime import datetime class ViewManager(object): """View Manager allows the registering of Views for collecting stats and receiving stats data as View Data""" def __init__(self): - self.time = datetime.utcnow().isoformat() + 'Z' + self.time = utils.to_iso_str() if execution_context.get_measure_to_view_map() == {}: execution_context.set_measure_to_view_map(MeasureToViewMap()) diff --git a/opencensus/trace/span.py b/opencensus/trace/span.py index 43130d509..041b597f1 100644 --- a/opencensus/trace/span.py +++ b/opencensus/trace/span.py @@ -15,7 +15,7 @@ from datetime import datetime from itertools import chain -from opencensus.common.utils import get_truncatable_str +from opencensus.common import utils from opencensus.trace import attributes from opencensus.trace import base_span from opencensus.trace import link as link_module @@ -225,11 +225,11 @@ def add_link(self, link): def start(self): """Set the start time for a span.""" - self.start_time = datetime.utcnow().isoformat() + 'Z' + self.start_time = utils.to_iso_str() def finish(self): """Set the end time for a span.""" - self.end_time = datetime.utcnow().isoformat() + 'Z' + self.end_time = utils.to_iso_str() def __iter__(self): """Iterate through the span tree.""" @@ -265,7 +265,7 @@ def format_span_json(span): :returns: Formatted Span. """ span_json = { - 'displayName': get_truncatable_str(span.name), + 'displayName': utils.get_truncatable_str(span.name), 'spanId': span.span_id, 'startTime': span.start_time, 'endTime': span.end_time, diff --git a/opencensus/trace/time_event.py b/opencensus/trace/time_event.py index aaa6ee49d..2c315fa37 100644 --- a/opencensus/trace/time_event.py +++ b/opencensus/trace/time_event.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from opencensus.common.utils import get_truncatable_str +from opencensus.common import utils class Type(object): @@ -46,7 +46,8 @@ def __init__(self, description, attributes=None): def format_annotation_json(self): annotation_json = {} - annotation_json['description'] = get_truncatable_str(self.description) + annotation_json['description'] = utils.get_truncatable_str( + self.description) if self.attributes is not None: annotation_json['attributes'] = self.attributes.\ @@ -126,7 +127,7 @@ class TimeEvent(object): spans. """ def __init__(self, timestamp, annotation=None, message_event=None): - self.timestamp = timestamp.isoformat() + 'Z' + self.timestamp = utils.to_iso_str(timestamp) if annotation is not None and message_event is not None: raise ValueError("A TimeEvent can contain either an Annotation" diff --git a/tests/unit/stats/exporters/test_stackdriver_stats.py b/tests/unit/stats/exporters/test_stackdriver_stats.py index 7b52744cd..fc3d920e2 100644 --- a/tests/unit/stats/exporters/test_stackdriver_stats.py +++ b/tests/unit/stats/exporters/test_stackdriver_stats.py @@ -18,6 +18,7 @@ from google.cloud import monitoring_v3 +from opencensus.common import utils from opencensus.common.version import __version__ from opencensus.stats import aggregation as aggregation_module from opencensus.stats import aggregation_data as aggregation_data_module @@ -58,7 +59,7 @@ VIDEO_SIZE_VIEW_NAME, "processed video size over time", [FRONTEND_KEY], VIDEO_SIZE_MEASURE, VIDEO_SIZE_DISTRIBUTION) -TEST_TIME = datetime(2018, 12, 25, 1, 2, 3, 4).isoformat() + 'Z' +TEST_TIME = utils.to_iso_str(datetime(2018, 12, 25, 1, 2, 3, 4)) class _Client(object): diff --git a/tests/unit/stats/test_view_data.py b/tests/unit/stats/test_view_data.py index 41c9aaf15..1edf3008c 100644 --- a/tests/unit/stats/test_view_data.py +++ b/tests/unit/stats/test_view_data.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest -import mock from datetime import datetime +import mock +import unittest + +from opencensus.common import utils from opencensus.stats import aggregation as aggregation_module from opencensus.stats import measure as measure_module from opencensus.stats import view_data as view_data_module @@ -82,7 +84,7 @@ def test_record(self): context = mock.Mock() context.map = {'key1': 'val1', 'key2': 'val2'} - time = datetime.utcnow().isoformat() + 'Z' + time = utils.to_iso_str() value = 1 self.assertEqual({}, view_data.tag_value_aggregation_data_map) @@ -143,7 +145,7 @@ def test_record_with_attachment(self): view=view, start_time=start_time, end_time=end_time) context = mock.Mock context.map = {'key1': 'val1', 'key2': 'val2'} - time = datetime.utcnow().isoformat() + 'Z' + time = utils.to_iso_str() value = 1 view_data.record( @@ -193,7 +195,7 @@ def test_record_with_attachment_no_histogram(self): view=view, start_time=start_time, end_time=end_time) context = mock.Mock context.map = {'key1': 'val1', 'key2': 'val2'} - time = datetime.utcnow().isoformat() + 'Z' + time = utils.to_iso_str() value = 1 view_data.record( context=context, @@ -223,7 +225,7 @@ def test_record_with_multi_keys(self): view=view, start_time=start_time, end_time=end_time) context = mock.Mock() context.map = {'key1': 'val1', 'key2': 'val2'} - time = datetime.utcnow().isoformat() + 'Z' + time = utils.to_iso_str() value = 1 self.assertEqual({}, view_data.tag_value_aggregation_data_map) @@ -242,7 +244,7 @@ def test_record_with_multi_keys(self): context_2 = mock.Mock() context_2.map = {'key1': 'val3', 'key2': 'val2'} - time_2 = datetime.utcnow().isoformat() + 'Z' + time_2 = utils.to_iso_str() value_2 = 2 view_data.record( context=context_2, @@ -258,7 +260,7 @@ def test_record_with_multi_keys(self): sum_data_2 = view_data.tag_value_aggregation_data_map.get(tuple_vals_2) self.assertEqual(2, sum_data_2.sum_data) - time_3 = datetime.utcnow().isoformat() + 'Z' + time_3 = utils.to_iso_str() value_3 = 3 # Use the same context {'key1': 'val1', 'key2': 'val2'}. # Record to entry [(val1, val2), sum=1]. @@ -282,7 +284,7 @@ def test_record_with_missing_key_in_context(self): 'key1': 'val1', 'key3': 'val3' } # key2 is not in the context. - time = datetime.utcnow().isoformat() + 'Z' + time = utils.to_iso_str() value = 4 view_data.record( context=context, value=value, timestamp=time, attachments=None) @@ -303,7 +305,7 @@ def test_record_with_none_context(self): end_time = datetime.utcnow() view_data = view_data_module.ViewData( view=view, start_time=start_time, end_time=end_time) - time = datetime.utcnow().isoformat() + 'Z' + time = utils.to_iso_str() value = 4 view_data.record( context=None, value=value, timestamp=time, attachments=None) diff --git a/tests/unit/trace/test_blank_span.py b/tests/unit/trace/test_blank_span.py index da0a2593e..cf8126233 100644 --- a/tests/unit/trace/test_blank_span.py +++ b/tests/unit/trace/test_blank_span.py @@ -15,6 +15,8 @@ import datetime import mock import unittest + +from opencensus.common import utils from opencensus.trace.link import Link from opencensus.trace.span import format_span_json from opencensus.trace.time_event import TimeEvent @@ -85,13 +87,12 @@ def test_do_not_crash(self): span.finish() def test_constructor_explicit(self): - from datetime import datetime span_id = 'test_span_id' span_name = 'test_span_name' parent_span = mock.Mock() - start_time = datetime.utcnow().isoformat() + 'Z' - end_time = datetime.utcnow().isoformat() + 'Z' + start_time = utils.to_iso_str() + end_time = utils.to_iso_str() attributes = { 'http.status_code': '200', 'component': 'HTTP load balancer', diff --git a/tests/unit/trace/test_span.py b/tests/unit/trace/test_span.py index 90d52684b..9565bfb34 100644 --- a/tests/unit/trace/test_span.py +++ b/tests/unit/trace/test_span.py @@ -12,12 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +import datetime import unittest import mock from google.rpc import code_pb2 +from opencensus.common import utils from opencensus.trace.stack_trace import StackTrace from opencensus.trace.status import Status from opencensus.trace.time_event import TimeEvent @@ -56,13 +58,12 @@ def test_constructor_defaults(self): self.assertIsNone(span.context_tracer) def test_constructor_explicit(self): - from datetime import datetime span_id = 'test_span_id' span_name = 'test_span_name' parent_span = mock.Mock() - start_time = datetime.utcnow().isoformat() + 'Z' - end_time = datetime.utcnow().isoformat() + 'Z' + start_time = utils.to_iso_str() + end_time = utils.to_iso_str() attributes = { 'http.status_code': '200', 'component': 'HTTP load balancer', @@ -136,7 +137,6 @@ def test_add_attribute(self): def test_add_time_event(self): from opencensus.trace.time_event import TimeEvent - import datetime span_name = 'test_span_name' span = self._make_one(span_name) @@ -320,7 +320,6 @@ def test_format_span_json_no_parent_span(self): @mock.patch.object(TimeEvent, 'format_time_event_json') def test_format_span_json_with_parent_span(self, time_event_mock, status_mock, stack_trace_mock): - import datetime from opencensus.trace.link import Link from opencensus.trace.span import format_span_json diff --git a/tests/unit/trace/test_span_data.py b/tests/unit/trace/test_span_data.py index 8d50843e4..7966f84fc 100644 --- a/tests/unit/trace/test_span_data.py +++ b/tests/unit/trace/test_span_data.py @@ -15,6 +15,7 @@ import datetime import unittest +from opencensus.common import utils from opencensus.trace import link from opencensus.trace import span_context from opencensus.trace import span_data as span_data_module @@ -31,8 +32,8 @@ def test_create_span_data(self): span_id='6e0c63257de34c92', parent_span_id='6e0c63257de34c93', attributes={'key1': 'value1'}, - start_time=datetime.datetime.utcnow().isoformat() + 'Z', - end_time=datetime.datetime.utcnow().isoformat() + 'Z', + start_time=utils.to_iso_str(), + end_time=utils.to_iso_str(), stack_trace=None, links=None, status=None, @@ -49,8 +50,8 @@ def test_span_data_immutable(self): span_id='6e0c63257de34c92', parent_span_id='6e0c63257de34c93', attributes={'key1': 'value1'}, - start_time=datetime.datetime.utcnow().isoformat() + 'Z', - end_time=datetime.datetime.utcnow().isoformat() + 'Z', + start_time=utils.to_iso_str(), + end_time=utils.to_iso_str(), stack_trace=None, links=None, status=None, @@ -76,8 +77,8 @@ def test_format_legacy_trace_json(self): span_id='6e0c63257de34c92', parent_span_id='6e0c63257de34c93', attributes={'key1': 'value1'}, - start_time=datetime.datetime.utcnow().isoformat() + 'Z', - end_time=datetime.datetime.utcnow().isoformat() + 'Z', + start_time=utils.to_iso_str(), + end_time=utils.to_iso_str(), stack_trace=stack_trace.StackTrace(stack_trace_hash_id='111'), links=[link.Link('1111', span_id='6e0c63257de34c92')], status=status.Status(code=0, message='pok'), diff --git a/tests/unit/trace/test_time_event.py b/tests/unit/trace/test_time_event.py index b38cad87e..b6c616142 100644 --- a/tests/unit/trace/test_time_event.py +++ b/tests/unit/trace/test_time_event.py @@ -17,6 +17,7 @@ import mock +from opencensus.common import utils from opencensus.trace import time_event as time_event_module @@ -137,7 +138,7 @@ def test_constructor(self): time_event = time_event_module.TimeEvent( timestamp=timestamp, message_event=message_event) - self.assertEqual(time_event.timestamp, timestamp.isoformat() + 'Z') + self.assertEqual(time_event.timestamp, utils.to_iso_str(timestamp)) self.assertEqual(time_event.message_event, message_event) def test_constructor_value_error(self): @@ -163,7 +164,7 @@ def test_format_time_event_json_annotation(self): time_event_json = time_event.format_time_event_json() expected_time_event_json = { - 'time': timestamp.isoformat() + 'Z', + 'time': utils.to_iso_str(timestamp), 'annotation': mock_annotation } @@ -181,7 +182,7 @@ def test_format_time_event_json_message_event(self): time_event_json = time_event.format_time_event_json() expected_time_event_json = { - 'time': timestamp.isoformat() + 'Z', + 'time': utils.to_iso_str(timestamp), 'message_event': mock_message_event } diff --git a/tests/unit/trace/test_tracer.py b/tests/unit/trace/test_tracer.py index 58b28bbe1..21a590eb4 100644 --- a/tests/unit/trace/test_tracer.py +++ b/tests/unit/trace/test_tracer.py @@ -221,10 +221,7 @@ def test_end_span_sampled(self): span.__iter__ = mock.Mock(return_value=iter([span])) execution_context.set_current_span(span) - patch = mock.patch('opencensus.trace.span.get_truncatable_str', - mock.Mock()) - - with patch: + with mock.patch('opencensus.trace.span.utils.get_truncatable_str'): tracer.end_span() self.assertTrue(span.finish.called)