Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

187 строки
5.3KB

  1. import os
  2. import sys
  3. import time
  4. import struct
  5. import socket
  6. import random
  7. from eventlet.green import threading
  8. from eventlet.zipkin._thrift.zipkinCore import ttypes
  9. from eventlet.zipkin._thrift.zipkinCore.constants import SERVER_SEND
  10. client = None
  11. _tls = threading.local() # thread local storage
  12. def put_annotation(msg, endpoint=None):
  13. """ This is annotation API.
  14. You can add your own annotation from in your code.
  15. Annotation is recorded with timestamp automatically.
  16. e.g.) put_annotation('cache hit for %s' % request)
  17. :param msg: String message
  18. :param endpoint: host info
  19. """
  20. if is_sample():
  21. a = ZipkinDataBuilder.build_annotation(msg, endpoint)
  22. trace_data = get_trace_data()
  23. trace_data.add_annotation(a)
  24. def put_key_value(key, value, endpoint=None):
  25. """ This is binary annotation API.
  26. You can add your own key-value extra information from in your code.
  27. Key-value doesn't have a time component.
  28. e.g.) put_key_value('http.uri', '/hoge/index.html')
  29. :param key: String
  30. :param value: String
  31. :param endpoint: host info
  32. """
  33. if is_sample():
  34. b = ZipkinDataBuilder.build_binary_annotation(key, value, endpoint)
  35. trace_data = get_trace_data()
  36. trace_data.add_binary_annotation(b)
  37. def is_tracing():
  38. """ Return whether the current thread is tracking or not """
  39. return hasattr(_tls, 'trace_data')
  40. def is_sample():
  41. """ Return whether it should record trace information
  42. for the request or not
  43. """
  44. return is_tracing() and _tls.trace_data.sampled
  45. def get_trace_data():
  46. if is_tracing():
  47. return _tls.trace_data
  48. def set_trace_data(trace_data):
  49. _tls.trace_data = trace_data
  50. def init_trace_data():
  51. if is_tracing():
  52. del _tls.trace_data
  53. def _uniq_id():
  54. """
  55. Create a random 64-bit signed integer appropriate
  56. for use as trace and span IDs.
  57. XXX: By experimentation zipkin has trouble recording traces with ids
  58. larger than (2 ** 56) - 1
  59. """
  60. return random.randint(0, (2 ** 56) - 1)
  61. def generate_trace_id():
  62. return _uniq_id()
  63. def generate_span_id():
  64. return _uniq_id()
  65. class TraceData(object):
  66. END_ANNOTATION = SERVER_SEND
  67. def __init__(self, name, trace_id, span_id, parent_id, sampled, endpoint):
  68. """
  69. :param name: RPC name (String)
  70. :param trace_id: int
  71. :param span_id: int
  72. :param parent_id: int or None
  73. :param sampled: lets the downstream servers know
  74. if I should record trace data for the request (bool)
  75. :param endpoint: zipkin._thrift.zipkinCore.ttypes.EndPoint
  76. """
  77. self.name = name
  78. self.trace_id = trace_id
  79. self.span_id = span_id
  80. self.parent_id = parent_id
  81. self.sampled = sampled
  82. self.endpoint = endpoint
  83. self.annotations = []
  84. self.bannotations = []
  85. self._done = False
  86. def add_annotation(self, annotation):
  87. if annotation.host is None:
  88. annotation.host = self.endpoint
  89. if not self._done:
  90. self.annotations.append(annotation)
  91. if annotation.value == self.END_ANNOTATION:
  92. self.flush()
  93. def add_binary_annotation(self, bannotation):
  94. if bannotation.host is None:
  95. bannotation.host = self.endpoint
  96. if not self._done:
  97. self.bannotations.append(bannotation)
  98. def flush(self):
  99. span = ZipkinDataBuilder.build_span(name=self.name,
  100. trace_id=self.trace_id,
  101. span_id=self.span_id,
  102. parent_id=self.parent_id,
  103. annotations=self.annotations,
  104. bannotations=self.bannotations)
  105. client.send_to_collector(span)
  106. self.annotations = []
  107. self.bannotations = []
  108. self._done = True
  109. class ZipkinDataBuilder:
  110. @staticmethod
  111. def build_span(name, trace_id, span_id, parent_id,
  112. annotations, bannotations):
  113. return ttypes.Span(
  114. name=name,
  115. trace_id=trace_id,
  116. id=span_id,
  117. parent_id=parent_id,
  118. annotations=annotations,
  119. binary_annotations=bannotations
  120. )
  121. @staticmethod
  122. def build_annotation(value, endpoint=None):
  123. if isinstance(value, unicode):
  124. value = value.encode('utf-8')
  125. return ttypes.Annotation(time.time() * 1000 * 1000,
  126. str(value), endpoint)
  127. @staticmethod
  128. def build_binary_annotation(key, value, endpoint=None):
  129. annotation_type = ttypes.AnnotationType.STRING
  130. return ttypes.BinaryAnnotation(key, value, annotation_type, endpoint)
  131. @staticmethod
  132. def build_endpoint(ipv4=None, port=None, service_name=None):
  133. if ipv4 is not None:
  134. ipv4 = ZipkinDataBuilder._ipv4_to_int(ipv4)
  135. if service_name is None:
  136. service_name = ZipkinDataBuilder._get_script_name()
  137. return ttypes.Endpoint(
  138. ipv4=ipv4,
  139. port=port,
  140. service_name=service_name
  141. )
  142. @staticmethod
  143. def _ipv4_to_int(ipv4):
  144. return struct.unpack('!i', socket.inet_aton(ipv4))[0]
  145. @staticmethod
  146. def _get_script_name():
  147. return os.path.basename(sys.argv[0])