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

Commit 4d1d617

Browse files
authored
Add Fixed-rate sampling logic for Azure Log Exporter (#848)
1 parent 3b48294 commit 4d1d617

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ def __init__(self, *args, **kwargs):
9292
export_interval=15.0,
9393
grace_period=5.0,
9494
instrumentation_key=None,
95+
logging_sampling_rate=1.0,
9596
max_batch_size=100,
9697
minimum_retry_interval=60, # minimum retry interval in seconds
9798
proxy=None,

contrib/opencensus-ext-azure/opencensus/ext/azure/log_exporter/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import logging
16+
import random
1617
import threading
1718
import time
1819
import traceback
@@ -107,6 +108,16 @@ def stop(self, timeout=None): # pragma: NO COVER
107108
return time.time() - start_time # time taken to stop
108109

109110

111+
class SamplingFilter(logging.Filter):
112+
113+
def __init__(self, probability=1.0):
114+
super(SamplingFilter, self).__init__()
115+
self.probability = probability
116+
117+
def filter(self, record):
118+
return random.random() < self.probability
119+
120+
110121
class AzureLogHandler(TransportMixin, BaseLogHandler):
111122
"""Handler for logging to Microsoft Azure Monitor.
112123
@@ -116,6 +127,8 @@ class AzureLogHandler(TransportMixin, BaseLogHandler):
116127
def __init__(self, **options):
117128
self.options = Options(**options)
118129
utils.validate_instrumentation_key(self.options.instrumentation_key)
130+
if not 0 <= self.options.logging_sampling_rate <= 1:
131+
raise ValueError('Sampling must be in the range: [0,1]')
119132
self.export_interval = self.options.export_interval
120133
self.max_batch_size = self.options.max_batch_size
121134
self.storage = LocalFileStorage(
@@ -125,6 +138,7 @@ def __init__(self, **options):
125138
retention_period=self.options.storage_retention_period,
126139
)
127140
super(AzureLogHandler, self).__init__()
141+
self.addFilter(SamplingFilter(self.options.logging_sampling_rate))
128142

129143
def close(self):
130144
self.storage.close()

contrib/opencensus-ext-azure/tests/test_azure_log_exporter.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ def test_ctor(self):
7878
self.assertRaises(ValueError, lambda: log_exporter.AzureLogHandler())
7979
Options._default.instrumentation_key = instrumentation_key
8080

81+
def test_invalid_sampling_rate(self):
82+
with self.assertRaises(ValueError):
83+
log_exporter.AzureLogHandler(
84+
instrumentation_key='12345678-1234-5678-abcd-12345678abcd',
85+
logging_sampling_rate=4.0,
86+
)
87+
8188
@mock.patch('requests.post', return_value=mock.Mock())
8289
def test_exception(self, requests_mock):
8390
logger = logging.getLogger(self.id())
@@ -207,3 +214,37 @@ def test_log_with_invalid_custom_properties(self, requests_mock):
207214

208215
self.assertFalse('not_a_dict' in post_body)
209216
self.assertFalse('key_1' in post_body)
217+
218+
@mock.patch('requests.post', return_value=mock.Mock())
219+
def test_log_record_sampled(self, requests_mock):
220+
logger = logging.getLogger(self.id())
221+
handler = log_exporter.AzureLogHandler(
222+
instrumentation_key='12345678-1234-5678-abcd-12345678abcd',
223+
logging_sampling_rate=1.0,
224+
)
225+
logger.addHandler(handler)
226+
logger.warning('Hello_World')
227+
logger.warning('Hello_World2')
228+
logger.warning('Hello_World3')
229+
logger.warning('Hello_World4')
230+
handler.close()
231+
post_body = requests_mock.call_args_list[0][1]['data']
232+
self.assertTrue('Hello_World' in post_body)
233+
self.assertTrue('Hello_World2' in post_body)
234+
self.assertTrue('Hello_World3' in post_body)
235+
self.assertTrue('Hello_World4' in post_body)
236+
237+
@mock.patch('requests.post', return_value=mock.Mock())
238+
def test_log_record_not_sampled(self, requests_mock):
239+
logger = logging.getLogger(self.id())
240+
handler = log_exporter.AzureLogHandler(
241+
instrumentation_key='12345678-1234-5678-abcd-12345678abcd',
242+
logging_sampling_rate=0.0,
243+
)
244+
logger.addHandler(handler)
245+
logger.warning('Hello_World')
246+
logger.warning('Hello_World2')
247+
logger.warning('Hello_World3')
248+
logger.warning('Hello_World4')
249+
handler.close()
250+
self.assertFalse(requests_mock.called)

0 commit comments

Comments
 (0)