Skip to content

Commit d3bed64

Browse files
committed
add span_compression_enabled setting
closes elastic#1428
1 parent 1677f7b commit d3bed64

File tree

5 files changed

+117
-9
lines changed

5 files changed

+117
-9
lines changed

docs/configuration.asciidoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,24 @@ To disable stack trace collection for spans completely, set the value to `0`.
691691
Except for the special values `-1` and `0`,
692692
this setting has to be provided in *<<config-format-duration, duration format>>*.
693693

694+
[float]
695+
[[config-span-compression-enabled]]
696+
==== `span_compression_enabled`
697+
698+
<<dynamic-configuration, image:./images/dynamic-config.svg[] >>
699+
700+
[options="header"]
701+
|============
702+
| Environment | Django/Flask | Default
703+
| `ELASTIC_APM_SPAN_COMPRESSION_ENABLED` | `SPAN_COMPRESSION_ENABLED` | `False`
704+
|============
705+
706+
Enable/disable span compression.
707+
708+
If enabled, the agent will compress very short, repeated spans into a single span,
709+
which is beneficial for storage and processing requirements.
710+
Some information is lost in this process, e.g. exact durations of each compressed span.
711+
694712
[float]
695713
[[config-span-compression-exact-match-max_duration]]
696714
==== `span_compression_exact_match_max_duration`

elasticapm/conf/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,14 +578,15 @@ class Config(_ConfigBase):
578578
],
579579
type=int,
580580
)
581+
span_compression_enabled = _BoolConfigValue("SPAN_COMPRESSION_ENABLED", default=False)
581582
span_compression_exact_match_max_duration = _ConfigValue(
582-
"span_compression_exact_match_max_duration",
583+
"SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION",
583584
default=50,
584585
validators=[duration_validator],
585586
type=int,
586587
)
587588
span_compression_same_kind_max_duration = _ConfigValue(
588-
"span_compression_exact_match_max_duration",
589+
"SPAN_COMPRESSION_SAME_KIND_MAX_DURATION",
589590
default=5,
590591
validators=[duration_validator],
591592
type=int,

elasticapm/traces.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,12 @@ def is_exact_match(self, other_span: SpanType) -> bool:
583583
return bool(self.name == other_span.name and self.is_same_kind(other_span))
584584

585585
def is_compression_eligible(self) -> bool:
586-
return self.leaf and not self.dist_tracing_propagated and self.outcome in (None, constants.OUTCOME.SUCCESS)
586+
"""
587+
Determine if this span is eligible for compression.
588+
"""
589+
if self.tracer.config.span_compression_enabled:
590+
return self.leaf and not self.dist_tracing_propagated and self.outcome in (None, constants.OUTCOME.SUCCESS)
591+
return False
587592

588593
def end(self, skip_frames: int = 0, duration: Optional[float] = None):
589594
"""

tests/client/span_compression_tests.py

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@
3535

3636
@pytest.mark.parametrize(
3737
"elasticapm_client",
38-
[{"span_compression_same_kind_max_duration": "5ms", "span_compression_exact_match_max_duration": "5ms"}],
38+
[
39+
{
40+
"span_compression_enabled": True,
41+
"span_compression_same_kind_max_duration": "5ms",
42+
"span_compression_exact_match_max_duration": "5ms",
43+
}
44+
],
3945
indirect=True,
4046
)
4147
def test_exact_match(elasticapm_client):
@@ -73,7 +79,13 @@ def test_exact_match(elasticapm_client):
7379

7480
@pytest.mark.parametrize(
7581
"elasticapm_client",
76-
[{"span_compression_same_kind_max_duration": "5ms", "span_compression_exact_match_max_duration": "5ms"}],
82+
[
83+
{
84+
"span_compression_enabled": True,
85+
"span_compression_same_kind_max_duration": "5ms",
86+
"span_compression_exact_match_max_duration": "5ms",
87+
}
88+
],
7789
indirect=True,
7890
)
7991
def test_same_kind(elasticapm_client):
@@ -114,7 +126,13 @@ def test_same_kind(elasticapm_client):
114126

115127
@pytest.mark.parametrize(
116128
"elasticapm_client",
117-
[{"span_compression_same_kind_max_duration": "5ms", "span_compression_exact_match_max_duration": "5ms"}],
129+
[
130+
{
131+
"span_compression_enabled": True,
132+
"span_compression_same_kind_max_duration": "5ms",
133+
"span_compression_exact_match_max_duration": "5ms",
134+
}
135+
],
118136
indirect=True,
119137
)
120138
def test_exact_match_after_same_kind(elasticapm_client):
@@ -162,7 +180,13 @@ def test_exact_match_after_same_kind(elasticapm_client):
162180

163181
@pytest.mark.parametrize(
164182
"elasticapm_client",
165-
[{"span_compression_same_kind_max_duration": "5ms", "span_compression_exact_match_max_duration": "5ms"}],
183+
[
184+
{
185+
"span_compression_enabled": True,
186+
"span_compression_same_kind_max_duration": "5ms",
187+
"span_compression_exact_match_max_duration": "5ms",
188+
}
189+
],
166190
indirect=True,
167191
)
168192
def test_nested_spans(elasticapm_client):
@@ -199,7 +223,13 @@ def test_nested_spans(elasticapm_client):
199223

200224
@pytest.mark.parametrize(
201225
"elasticapm_client",
202-
[{"span_compression_same_kind_max_duration": "5ms", "span_compression_exact_match_max_duration": "5ms"}],
226+
[
227+
{
228+
"span_compression_enabled": True,
229+
"span_compression_same_kind_max_duration": "5ms",
230+
"span_compression_exact_match_max_duration": "5ms",
231+
}
232+
],
203233
indirect=True,
204234
)
205235
def test_buffer_is_reported_if_next_child_ineligible(elasticapm_client):
@@ -224,7 +254,13 @@ def test_buffer_is_reported_if_next_child_ineligible(elasticapm_client):
224254

225255
@pytest.mark.parametrize(
226256
"elasticapm_client",
227-
[{"span_compression_same_kind_max_duration": "5ms", "span_compression_exact_match_max_duration": "5ms"}],
257+
[
258+
{
259+
"span_compression_enabled": True,
260+
"span_compression_same_kind_max_duration": "5ms",
261+
"span_compression_exact_match_max_duration": "5ms",
262+
}
263+
],
228264
indirect=True,
229265
)
230266
def test_compressed_spans_not_counted(elasticapm_client):
@@ -254,3 +290,43 @@ def test_compressed_spans_not_counted(elasticapm_client):
254290
spans = elasticapm_client.events[SPAN]
255291
assert len(spans) == transaction["span_count"]["started"] == 1
256292
assert transaction["span_count"]["dropped"] == 0
293+
294+
295+
@pytest.mark.parametrize(
296+
"elasticapm_client",
297+
[
298+
{
299+
"span_compression_enabled": False,
300+
"span_compression_same_kind_max_duration": "5ms",
301+
"span_compression_exact_match_max_duration": "5ms",
302+
}
303+
],
304+
indirect=True,
305+
)
306+
def test_span_compression_disabled(elasticapm_client):
307+
transaction = elasticapm_client.begin_transaction("test")
308+
with elasticapm.capture_span(
309+
"test",
310+
span_type="a",
311+
span_subtype="b",
312+
span_action="c",
313+
leaf=True,
314+
duration=2,
315+
extra={"destination": {"service": {"resource": "x"}}},
316+
) as span1:
317+
assert not span1.is_compression_eligible()
318+
with elasticapm.capture_span(
319+
"test",
320+
span_type="a",
321+
span_subtype="b",
322+
span_action="c",
323+
leaf=True,
324+
duration=3,
325+
extra={"destination": {"service": {"resource": "x"}}},
326+
) as span2:
327+
assert not span2.is_compression_eligible()
328+
elasticapm_client.end_transaction("test")
329+
spans = elasticapm_client.events[SPAN]
330+
assert len(spans) == 2
331+
span = spans[0]
332+
assert "composite" not in span

tests/config/tests.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,3 +438,11 @@ def test_versioned_config_attribute_access(elasticapm_client):
438438
elasticapm_client.config.update("2", capture_body=True)
439439
val = elasticapm_client.config.start_stop_order
440440
assert isinstance(val, int)
441+
442+
443+
def test_config_all_upper_case():
444+
c = Config.__class__.__dict__.items()
445+
for field, config_value in Config.__dict__.items():
446+
if not isinstance(config_value, _ConfigValue):
447+
continue
448+
assert config_value.env_key == config_value.env_key.upper()

0 commit comments

Comments
 (0)