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
15from opencensus.trace import execution_context, print_exporter, samplers
16from opencensus.trace.propagation import trace_context_http_header_format
17from opencensus.trace.span_context import SpanContext
18from opencensus.trace.tracers import context_tracer, noop_tracer
19
20
21class Tracer(object):
22    """The Tracer is for tracing a request for web applications.
23
24    :type span_context: :class:`~opencensus.trace.span_context.SpanContext`
25    :param span_context: SpanContext encapsulates the current context within
26                         the request's trace.
27
28    :type sampler: :class:`~opencensus.trace.samplers.base.Sampler`
29    :param sampler: Instances of Sampler objects. Defaults to
30                    :class:`.ProbabilitySampler`. Other options include
31                    :class:`.AlwaysOnSampler` and :class:`.AlwaysOffSampler`.
32
33    :type exporter: :class:`~opencensus.trace.base_exporter.exporter`
34    :param exporter: Instances of exporter objects. Default to
35                     :class:`.Printexporter`. The rest options are
36                     :class:`.Fileexporter`, :class:`.Printexporter`,
37                     :class:`.Loggingexporter`, :class:`.Zipkinexporter`,
38                     :class:`.GoogleCloudexporter`
39    """
40    def __init__(
41            self,
42            span_context=None,
43            sampler=None,
44            exporter=None,
45            propagator=None):
46        if span_context is None:
47            span_context = SpanContext()
48
49        if sampler is None:
50            sampler = samplers.ProbabilitySampler()
51
52        if exporter is None:
53            exporter = print_exporter.PrintExporter()
54
55        if propagator is None:
56            propagator = \
57                trace_context_http_header_format.TraceContextPropagator()
58
59        self.span_context = span_context
60        self.sampler = sampler
61        self.exporter = exporter
62        self.propagator = propagator
63        self.tracer = self.get_tracer()
64        self.store_tracer()
65
66    def should_sample(self):
67        """Determine whether to sample this request or not.
68        If the context enables tracing, return True.
69        Else follow the decision of the sampler.
70
71        :rtype: bool
72        :returns: Whether to trace the request or not.
73        """
74        return self.sampler.should_sample(self.span_context)
75
76    def get_tracer(self):
77        """Return a tracer according to the sampling decision."""
78        sampled = self.should_sample()
79
80        if sampled:
81            self.span_context.trace_options.set_enabled(True)
82            return context_tracer.ContextTracer(
83                exporter=self.exporter,
84                span_context=self.span_context)
85        return noop_tracer.NoopTracer()
86
87    def store_tracer(self):
88        """Add the current tracer to thread_local"""
89        execution_context.set_opencensus_tracer(self)
90
91    def finish(self):
92        """End all spans."""
93        self.tracer.finish()
94
95    def span(self, name='span'):
96        """Create a new span with the trace using the context information.
97
98        :type name: str
99        :param name: The name of the span.
100
101        :rtype: :class:`~opencensus.trace.span.Span`
102        :returns: The Span object.
103        """
104        return self.tracer.span(name)
105
106    def start_span(self, name='span'):
107        return self.tracer.start_span(name)
108
109    def end_span(self):
110        """End a span. Update the span_id in SpanContext to the current span's
111        parent span id; Update the current span; Send the span to exporter.
112        """
113        self.tracer.end_span()
114
115    def current_span(self):
116        """Return the current span."""
117        return self.tracer.current_span()
118
119    def add_attribute_to_current_span(self, attribute_key, attribute_value):
120        """Add attribute to current span.
121
122        :type attribute_key: str
123        :param attribute_key: Attribute key.
124
125        :type attribute_value:str
126        :param attribute_value: Attribute value.
127        """
128        self.tracer.add_attribute_to_current_span(
129            attribute_key, attribute_value)
130
131    def trace_decorator(self):
132        """Decorator to trace a function."""
133
134        def decorator(func):
135
136            def wrapper(*args, **kwargs):
137                self.tracer.start_span(name=func.__name__)
138                return_value = func(*args, **kwargs)
139                self.tracer.end_span()
140                return return_value
141
142            return wrapper
143
144        return decorator
145