@@ -153,3 +153,129 @@ def dataclass_task(data: TestDataClass):
153
153
"field1" : "value1" ,
154
154
"field2" : 123 ,
155
155
}
156
+
157
+
158
+ def test_json_truncation_with_otel_limit (exporter , monkeypatch ):
159
+ """Test that JSON input/output is truncated when OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT is set"""
160
+ # Set environment variable to a small limit for testing
161
+ monkeypatch .setenv ("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" , "50" )
162
+
163
+ @task (name = "truncation_task" )
164
+ def truncation_task (long_input ):
165
+ # Return a long output that will also be truncated
166
+ return "This is a very long output string that should definitely exceed the 50 character limit"
167
+
168
+ # Call with a long input that will be truncated
169
+ long_input = "This is a very long input string that should definitely exceed the 50 character limit"
170
+ truncation_task (long_input )
171
+
172
+ spans = exporter .get_finished_spans ()
173
+ task_span = spans [0 ]
174
+
175
+ # Check that input was truncated
176
+ input_json = task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_INPUT ]
177
+ assert len (input_json ) == 50
178
+ assert input_json .startswith ('{"args": ["This is a very long input string that s' )
179
+
180
+ # Check that output was truncated
181
+ output_json = task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_OUTPUT ]
182
+ assert len (output_json ) == 50
183
+ assert output_json .startswith ('"This is a very long output string that should def' )
184
+
185
+
186
+ def test_json_no_truncation_without_otel_limit (exporter , monkeypatch ):
187
+ """Test that JSON input/output is not truncated when OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT is not set"""
188
+ # Ensure environment variable is not set
189
+ monkeypatch .delenv ("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" , raising = False )
190
+
191
+ @task (name = "no_truncation_task" )
192
+ def no_truncation_task (long_input ):
193
+ return "This is a very long output string that would be truncated if limits were set but should remain intact"
194
+
195
+ long_input = "This is a very long input string that would be truncated if limits were set but should remain intact"
196
+ result = no_truncation_task (long_input )
197
+
198
+ spans = exporter .get_finished_spans ()
199
+ task_span = spans [0 ]
200
+
201
+ # Check that input was not truncated
202
+ input_data = json .loads (task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_INPUT ])
203
+ assert input_data ["args" ][0 ] == long_input
204
+
205
+ # Check that output was not truncated
206
+ output_data = json .loads (task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_OUTPUT ])
207
+ assert output_data == result
208
+
209
+
210
+ def test_json_truncation_with_invalid_otel_limit (exporter , monkeypatch ):
211
+ """Test that invalid OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT values are ignored"""
212
+ # Set environment variable to invalid value
213
+ monkeypatch .setenv ("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" , "not_a_number" )
214
+
215
+ @task (name = "invalid_limit_task" )
216
+ def invalid_limit_task (test_input ):
217
+ return "This output should not be truncated because the limit is invalid"
218
+
219
+ test_input = "This input should not be truncated because the limit is invalid"
220
+ result = invalid_limit_task (test_input )
221
+
222
+ spans = exporter .get_finished_spans ()
223
+ task_span = spans [0 ]
224
+
225
+ # Check that input was not truncated (since invalid limit should be ignored)
226
+ input_data = json .loads (task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_INPUT ])
227
+ assert input_data ["args" ][0 ] == test_input
228
+
229
+ # Check that output was not truncated
230
+ output_data = json .loads (task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_OUTPUT ])
231
+ assert output_data == result
232
+
233
+
234
+ @pytest .mark .asyncio
235
+ async def test_async_json_truncation_with_otel_limit (exporter , monkeypatch ):
236
+ """Test that JSON truncation works with async tasks"""
237
+ # Set environment variable to a small limit for testing
238
+ monkeypatch .setenv ("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" , "40" )
239
+
240
+ @task (name = "async_truncation_task" )
241
+ async def async_truncation_task (long_input ):
242
+ await asyncio .sleep (0.1 ) # Simulate async work
243
+ return "This is a long async output that should be truncated"
244
+
245
+ long_input = "This is a long async input that should be truncated"
246
+ await async_truncation_task (long_input )
247
+
248
+ spans = exporter .get_finished_spans ()
249
+ task_span = spans [0 ]
250
+
251
+ # Check that input was truncated
252
+ input_json = task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_INPUT ]
253
+ assert len (input_json ) == 40
254
+
255
+ # Check that output was truncated
256
+ output_json = task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_OUTPUT ]
257
+ assert len (output_json ) == 40
258
+
259
+
260
+ def test_json_truncation_preserves_short_content (exporter , monkeypatch ):
261
+ """Test that short content is not affected by truncation limits"""
262
+ # Set environment variable to a limit larger than our content
263
+ monkeypatch .setenv ("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" , "1000" )
264
+
265
+ @task (name = "short_content_task" )
266
+ def short_content_task (short_input ):
267
+ return "short output"
268
+
269
+ short_input = "short input"
270
+ result = short_content_task (short_input )
271
+
272
+ spans = exporter .get_finished_spans ()
273
+ task_span = spans [0 ]
274
+
275
+ # Check that short input was preserved completely
276
+ input_data = json .loads (task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_INPUT ])
277
+ assert input_data ["args" ][0 ] == short_input
278
+
279
+ # Check that short output was preserved completely
280
+ output_data = json .loads (task_span .attributes [SpanAttributes .TRACELOOP_ENTITY_OUTPUT ])
281
+ assert output_data == result
0 commit comments