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
16DEFAULT_SAMPLING_RATE = 1e-4
17
18
19class Sampler(object):
20    """Base class for opencensus trace request samplers.
21
22    Subclasses must override :meth:`should_sample`.
23    """
24
25    def should_sample(self, span_context):
26        """Whether to sample this request.
27
28        :type span_context: :class:`opencensus.trace.span_context.SpanContext`
29        :param span_context: The span context.
30
31        :rtype: bool
32        :returns: Whether to sample the request according to the context.
33        """
34        raise NotImplementedError
35
36
37class AlwaysOnSampler(Sampler):
38    """Sampler that samples every request, regardless of trace options."""
39
40    def should_sample(self, span_context):
41        return True
42
43
44class AlwaysOffSampler(Sampler):
45    """Sampler that doesn't sample any request, regardless of trace options."""
46
47    def should_sample(self, span_context):
48        return False
49
50
51class ProbabilitySampler(Sampler):
52    """Sample a request at a fixed rate.
53
54    :type rate: float
55    :param rate: The rate of sampling.
56    """
57    def __init__(self, rate=None):
58        if rate is None:
59            rate = DEFAULT_SAMPLING_RATE
60
61        if not 0 <= rate <= 1:
62            raise ValueError('Rate must between 0 and 1.')
63
64        self.rate = rate
65
66    def should_sample(self, span_context):
67        """Make the sampling decision based on the lower 8 bytes of the trace
68        ID. If the value is less than the bound, return True, else False.
69
70        :type span_context: :class:`opencensus.trace.span_context.SpanContext`
71        :param span_context: The span context.
72
73        :rtype: bool
74        :returns: Whether to sample the request according to the context.
75        """
76        if span_context.trace_options.get_enabled():
77            return True
78
79        lower_long = get_lower_long_from_trace_id(span_context.trace_id)
80        bound = self.rate * 0xffffffffffffffff
81        return lower_long <= bound
82
83
84def get_lower_long_from_trace_id(trace_id):
85    """Returns the lower 8 bytes of the trace ID as a long value, assuming
86    little endian order.
87
88    :rtype: long
89    :returns: Lower 8 bytes of trace ID
90    """
91    lower_bytes = trace_id[16:]
92    lower_long = int(lower_bytes, 16)
93
94    return lower_long
95