Skip to content

Commit ddae8b5

Browse files
fix checks
# Conflicts: # .github/workflows/test_1.yml # .github/workflows/test_2.yml # .github/workflows/test_3.yml
1 parent 361e5e6 commit ddae8b5

File tree

10 files changed

+129
-41
lines changed

10 files changed

+129
-41
lines changed

.github/workflows/lint_0.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,10 +590,10 @@ jobs:
590590
- name: Checkout repo @ SHA - ${{ github.sha }}
591591
uses: actions/checkout@v4
592592

593-
- name: Set up Python 3.13
593+
- name: Set up Python 3.14
594594
uses: actions/setup-python@v5
595595
with:
596-
python-version: "3.13"
596+
python-version: "3.14"
597597

598598
- name: Install tox
599599
run: pip install tox-uv

docs-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ redis>=2.6
4747
remoulade>=0.50
4848
sqlalchemy>=1.0
4949
starlette~=0.50
50+
structlog~=21.1
5051
tornado>=5.1.1
5152
tortoise-orm>=0.17.0
5253

instrumentation/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy >= 1.0.0, < 2.1.0 | Yes | development
4545
| [opentelemetry-instrumentation-sqlite3](./opentelemetry-instrumentation-sqlite3) | sqlite3 | No | development
4646
| [opentelemetry-instrumentation-starlette](./opentelemetry-instrumentation-starlette) | starlette >= 0.13 | Yes | development
47-
| [opentelemetry-instrumentation-structlog](./opentelemetry-instrumentation-structlog) | structlog ~= 21.1 | No | development
47+
| [opentelemetry-instrumentation-structlog](./opentelemetry-instrumentation-structlog) | structlog >= 21.1 | No | development
4848
| [opentelemetry-instrumentation-system-metrics](./opentelemetry-instrumentation-system-metrics) | psutil >= 5 | No | development
4949
| [opentelemetry-instrumentation-threading](./opentelemetry-instrumentation-threading) | threading | No | development
5050
| [opentelemetry-instrumentation-tornado](./opentelemetry-instrumentation-tornado) | tornado >= 5.1.1 | Yes | migration

instrumentation/opentelemetry-instrumentation-structlog/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ classifiers = [
2626
]
2727
dependencies = [
2828
"opentelemetry-api ~= 1.12",
29-
"opentelemetry-instrumentation == 0.59b0.dev",
29+
"opentelemetry-instrumentation == 0.62b0.dev",
3030
]
3131

3232
[project.optional-dependencies]

instrumentation/opentelemetry-instrumentation-structlog/src/opentelemetry/instrumentation/structlog/__init__.py

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,18 @@
5353
)
5454

5555
# Reserved keys that structlog uses internally and should not be passed as attributes
56-
_STRUCTLOG_RESERVED_KEYS = frozenset({
57-
"event",
58-
"level",
59-
"timestamp",
60-
"exc_info",
61-
"exception",
62-
"_record",
63-
"_logger",
64-
"_name",
65-
})
56+
_STRUCTLOG_RESERVED_KEYS = frozenset(
57+
{
58+
"event",
59+
"level",
60+
"timestamp",
61+
"exc_info",
62+
"exception",
63+
"_record",
64+
"_logger",
65+
"_name",
66+
}
67+
)
6668

6769
# Map structlog level names to standard library log level numbers
6870
_STRUCTLOG_LEVEL_TO_LEVELNO = {
@@ -175,7 +177,8 @@ def __call__(self, logger, name: str, event_dict: dict) -> dict:
175177

176178
return event_dict
177179

178-
def _get_attributes(self, event_dict: dict) -> dict[str, Any]:
180+
@staticmethod
181+
def _get_attributes(event_dict: dict) -> dict[str, Any]:
179182
"""
180183
Extract attributes from the structlog event dictionary.
181184
@@ -190,7 +193,8 @@ def _get_attributes(self, event_dict: dict) -> dict[str, Any]:
190193
"""
191194
# Start with all non-reserved keys
192195
attributes = {
193-
k: v for k, v in event_dict.items()
196+
k: v
197+
for k, v in event_dict.items()
194198
if k not in _STRUCTLOG_RESERVED_KEYS
195199
}
196200

@@ -200,13 +204,20 @@ def _get_attributes(self, event_dict: dict) -> dict[str, Any]:
200204
if exc_info is True:
201205
# exc_info=True means "get current exception"
202206
exc_info = sys.exc_info()
207+
elif isinstance(exc_info, BaseException):
208+
# exc_info can also be passed as an exception instance directly
209+
exc_info = (type(exc_info), exc_info, exc_info.__traceback__)
203210

204211
if isinstance(exc_info, tuple) and len(exc_info) == 3:
205212
exctype, value, tb = exc_info
206213
if exctype is not None:
207-
attributes[exception_attributes.EXCEPTION_TYPE] = exctype.__name__
214+
attributes[exception_attributes.EXCEPTION_TYPE] = (
215+
exctype.__name__
216+
)
208217
if value is not None and value.args:
209-
attributes[exception_attributes.EXCEPTION_MESSAGE] = str(value.args[0])
218+
attributes[exception_attributes.EXCEPTION_MESSAGE] = str(
219+
value.args[0]
220+
)
210221
if tb is not None:
211222
attributes[exception_attributes.EXCEPTION_STACKTRACE] = (
212223
"".join(traceback.format_exception(*exc_info))
@@ -217,7 +228,9 @@ def _get_attributes(self, event_dict: dict) -> dict[str, Any]:
217228
if isinstance(exception_str, str):
218229
# If we don't already have a stacktrace from exc_info, use this
219230
if exception_attributes.EXCEPTION_STACKTRACE not in attributes:
220-
attributes[exception_attributes.EXCEPTION_STACKTRACE] = exception_str
231+
attributes[exception_attributes.EXCEPTION_STACKTRACE] = (
232+
exception_str
233+
)
221234

222235
return attributes
223236

@@ -357,7 +370,8 @@ def _uninstrument(self, **kwargs):
357370

358371
# Remove all StructlogHandler instances
359372
new_processors = [
360-
p for p in current_processors
373+
p
374+
for p in current_processors
361375
if not isinstance(p, StructlogHandler)
362376
]
363377

instrumentation/opentelemetry-instrumentation-structlog/src/opentelemetry/instrumentation/structlog/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
__version__ = "0.59b0.dev"
15+
__version__ = "0.62b0.dev"

instrumentation/opentelemetry-instrumentation-structlog/tests/test_structlog.py

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

1515
import sys
16+
import time
17+
from unittest.mock import Mock
1618

1719
import structlog
1820

@@ -22,7 +24,10 @@
2224
StructlogInstrumentor,
2325
)
2426
from opentelemetry.sdk._logs import LoggerProvider
25-
from opentelemetry.sdk._logs.export import InMemoryLogRecordExporter, SimpleLogRecordProcessor
27+
from opentelemetry.sdk._logs.export import (
28+
InMemoryLogRecordExporter,
29+
SimpleLogRecordProcessor,
30+
)
2631
from opentelemetry.semconv._incubating.attributes import exception_attributes
2732
from opentelemetry.test.test_base import TestBase
2833
from opentelemetry.trace import TraceFlags
@@ -41,16 +46,15 @@ def setUp(self):
4146
)
4247

4348
# Configure structlog with OTel processor
44-
self.processor = StructlogHandler(
45-
logger_provider=self.logger_provider
46-
)
49+
self.processor = StructlogHandler(logger_provider=self.logger_provider)
4750
structlog.configure(
4851
processors=[
4952
self.processor,
5053
structlog.dev.ConsoleRenderer(),
5154
]
5255
)
5356
self.logger = structlog.get_logger()
57+
self.tracer = self.tracer_provider.get_tracer(__name__)
5458

5559
def tearDown(self):
5660
super().tearDown()
@@ -145,10 +149,14 @@ def test_exception_from_exc_info_tuple(self):
145149
attrs = log.log_record.attributes
146150

147151
self.assertIn(exception_attributes.EXCEPTION_TYPE, attrs)
148-
self.assertEqual(attrs[exception_attributes.EXCEPTION_TYPE], "ValueError")
152+
self.assertEqual(
153+
attrs[exception_attributes.EXCEPTION_TYPE], "ValueError"
154+
)
149155

150156
self.assertIn(exception_attributes.EXCEPTION_MESSAGE, attrs)
151-
self.assertEqual(attrs[exception_attributes.EXCEPTION_MESSAGE], "test error")
157+
self.assertEqual(
158+
attrs[exception_attributes.EXCEPTION_MESSAGE], "test error"
159+
)
152160

153161
self.assertIn(exception_attributes.EXCEPTION_STACKTRACE, attrs)
154162
stacktrace = attrs[exception_attributes.EXCEPTION_STACKTRACE]
@@ -169,10 +177,36 @@ def test_exception_from_exc_info_true(self):
169177
attrs = log.log_record.attributes
170178

171179
self.assertIn(exception_attributes.EXCEPTION_TYPE, attrs)
172-
self.assertEqual(attrs[exception_attributes.EXCEPTION_TYPE], "RuntimeError")
180+
self.assertEqual(
181+
attrs[exception_attributes.EXCEPTION_TYPE], "RuntimeError"
182+
)
183+
184+
self.assertIn(exception_attributes.EXCEPTION_MESSAGE, attrs)
185+
self.assertEqual(
186+
attrs[exception_attributes.EXCEPTION_MESSAGE], "runtime error"
187+
)
188+
189+
self.assertIn(exception_attributes.EXCEPTION_STACKTRACE, attrs)
190+
191+
def test_exception_from_exception_instance(self):
192+
"""Test exception extraction when exc_info is an Exception instance."""
193+
try:
194+
raise KeyError("missing key")
195+
except KeyError as exc:
196+
self.logger.error("error occurred", exc_info=exc)
197+
198+
logs = self.exporter.get_finished_logs()
199+
self.assertEqual(len(logs), 1)
200+
201+
log = logs[0]
202+
attrs = log.log_record.attributes
203+
204+
self.assertIn(exception_attributes.EXCEPTION_TYPE, attrs)
205+
self.assertEqual(
206+
attrs[exception_attributes.EXCEPTION_TYPE], "KeyError"
207+
)
173208

174209
self.assertIn(exception_attributes.EXCEPTION_MESSAGE, attrs)
175-
self.assertEqual(attrs[exception_attributes.EXCEPTION_MESSAGE], "runtime error")
176210

177211
self.assertIn(exception_attributes.EXCEPTION_STACKTRACE, attrs)
178212

@@ -189,8 +223,7 @@ def test_exception_from_string(self):
189223

190224
self.assertIn(exception_attributes.EXCEPTION_STACKTRACE, attrs)
191225
self.assertEqual(
192-
attrs[exception_attributes.EXCEPTION_STACKTRACE],
193-
exception_string
226+
attrs[exception_attributes.EXCEPTION_STACKTRACE], exception_string
194227
)
195228

196229
def test_trace_context_with_active_span(self):
@@ -212,7 +245,7 @@ def test_trace_context_with_active_span(self):
212245
self.assertEqual(log.log_record.span_id, span_context.span_id)
213246
self.assertEqual(
214247
log.log_record.trace_flags,
215-
TraceFlags(span_context.trace_flags)
248+
TraceFlags(span_context.trace_flags),
216249
)
217250

218251
def test_without_active_span(self):
@@ -278,19 +311,17 @@ def test_custom_attributes_pass_through(self):
278311
self.assertEqual(attrs["duration"], 1.5)
279312
self.assertEqual(attrs["success"], True)
280313

281-
def test_flush(self):
314+
@staticmethod
315+
def test_flush():
282316
"""Test that flush calls force_flush on the provider."""
283317
# Create a mock provider to verify flush is called
284-
from unittest.mock import Mock
285-
286318
mock_provider = Mock()
287319
mock_provider.force_flush = Mock()
288320
processor = StructlogHandler(logger_provider=mock_provider)
289321

290322
processor.flush()
291323

292324
# Give the thread a moment to execute
293-
import time
294325
time.sleep(0.1)
295326

296327
# Verify force_flush was called
@@ -334,7 +365,9 @@ def test_instrument_adds_processor(self):
334365
initial_count = len(initial_processors)
335366

336367
# Instrument
337-
StructlogInstrumentor().instrument(logger_provider=self.logger_provider)
368+
StructlogInstrumentor().instrument(
369+
logger_provider=self.logger_provider
370+
)
338371

339372
# Check that processor was added
340373
new_processors = structlog.get_config()["processors"]
@@ -356,7 +389,9 @@ def test_uninstrument_removes_processor(self):
356389
)
357390

358391
# Instrument
359-
StructlogInstrumentor().instrument(logger_provider=self.logger_provider)
392+
StructlogInstrumentor().instrument(
393+
logger_provider=self.logger_provider
394+
)
360395

361396
# Verify processor was added
362397
config_after_instrument = structlog.get_config()["processors"]
@@ -384,12 +419,16 @@ def test_double_instrument_prevented(self):
384419
)
385420

386421
# First instrumentation
387-
StructlogInstrumentor().instrument(logger_provider=self.logger_provider)
422+
StructlogInstrumentor().instrument(
423+
logger_provider=self.logger_provider
424+
)
388425
config_after_first = structlog.get_config()["processors"]
389426
count_after_first = len(config_after_first)
390427

391428
# Second instrumentation (should be no-op)
392-
StructlogInstrumentor().instrument(logger_provider=self.logger_provider)
429+
StructlogInstrumentor().instrument(
430+
logger_provider=self.logger_provider
431+
)
393432
config_after_second = structlog.get_config()["processors"]
394433
count_after_second = len(config_after_second)
395434

opentelemetry-contrib-instrumentations/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ dependencies = [
7373
"opentelemetry-instrumentation-sqlalchemy==0.62b0.dev",
7474
"opentelemetry-instrumentation-sqlite3==0.62b0.dev",
7575
"opentelemetry-instrumentation-starlette==0.62b0.dev",
76+
"opentelemetry-instrumentation-structlog==0.62b0.dev",
7677
"opentelemetry-instrumentation-system-metrics==0.62b0.dev",
7778
"opentelemetry-instrumentation-threading==0.62b0.dev",
7879
"opentelemetry-instrumentation-tornado==0.62b0.dev",

opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@
194194
},
195195
{
196196
"library": "structlog ~= 21.1",
197-
"instrumentation": "opentelemetry-instrumentation-structlog==0.59b0.dev",
197+
"instrumentation": "opentelemetry-instrumentation-structlog==0.62b0.dev",
198198
},
199199
{
200200
"library": "psutil >= 5",

0 commit comments

Comments
 (0)