|
3 | 3 |
|
4 | 4 | import pytest |
5 | 5 |
|
| 6 | +from sentry_sdk import get_client |
| 7 | +from sentry_sdk.consts import VERSION |
6 | 8 | from sentry_sdk.integrations.logging import LoggingIntegration, ignore_logger |
| 9 | +from tests.test_logs import envelopes_to_logs |
7 | 10 |
|
8 | 11 | other_logger = logging.getLogger("testfoo") |
9 | 12 | logger = logging.getLogger(__name__) |
@@ -283,3 +286,200 @@ def test_logging_dictionary_args(sentry_init, capture_events): |
283 | 286 | == "the value of foo is bar, and the value of bar is baz" |
284 | 287 | ) |
285 | 288 | assert event["logentry"]["params"] == {"foo": "bar", "bar": "baz"} |
| 289 | + |
| 290 | + |
| 291 | +@minimum_python_37 |
| 292 | +def test_sentry_logs_warning(sentry_init, capture_envelopes): |
| 293 | + """ |
| 294 | + The python logger module should create 'warn' sentry logs if the flag is on. |
| 295 | + """ |
| 296 | + sentry_init(_experiments={"enable_logs": True}) |
| 297 | + envelopes = capture_envelopes() |
| 298 | + |
| 299 | + python_logger = logging.Logger("test-logger") |
| 300 | + python_logger.warning("this is %s a template %s", "1", "2") |
| 301 | + |
| 302 | + get_client().flush() |
| 303 | + logs = envelopes_to_logs(envelopes) |
| 304 | + attrs = logs[0]["attributes"] |
| 305 | + assert attrs["sentry.message.template"] == "this is %s a template %s" |
| 306 | + assert "code.file.path" in attrs |
| 307 | + assert "code.line.number" in attrs |
| 308 | + assert attrs["logger.name"] == "test-logger" |
| 309 | + assert attrs["sentry.environment"] == "production" |
| 310 | + assert attrs["sentry.message.parameter.0"] == "1" |
| 311 | + assert attrs["sentry.message.parameter.1"] == "2" |
| 312 | + assert attrs["sentry.origin"] == "auto.logger.log" |
| 313 | + assert logs[0]["severity_number"] == 13 |
| 314 | + assert logs[0]["severity_text"] == "warn" |
| 315 | + |
| 316 | + |
| 317 | +@minimum_python_37 |
| 318 | +def test_sentry_logs_debug(sentry_init, capture_envelopes): |
| 319 | + """ |
| 320 | + The python logger module should not create 'debug' sentry logs if the flag is on by default |
| 321 | + """ |
| 322 | + sentry_init(_experiments={"enable_logs": True}) |
| 323 | + envelopes = capture_envelopes() |
| 324 | + |
| 325 | + python_logger = logging.Logger("test-logger") |
| 326 | + python_logger.debug("this is %s a template %s", "1", "2") |
| 327 | + get_client().flush() |
| 328 | + |
| 329 | + assert len(envelopes) == 0 |
| 330 | + |
| 331 | + |
| 332 | +@minimum_python_37 |
| 333 | +def test_no_log_infinite_loop(sentry_init, capture_envelopes): |
| 334 | + """ |
| 335 | + If 'debug' mode is true, and you set a low log level in the logging integration, there should be no infinite loops. |
| 336 | + """ |
| 337 | + sentry_init( |
| 338 | + _experiments={"enable_logs": True}, |
| 339 | + integrations=[LoggingIntegration(sentry_logs_level=logging.DEBUG)], |
| 340 | + debug=True, |
| 341 | + ) |
| 342 | + envelopes = capture_envelopes() |
| 343 | + |
| 344 | + python_logger = logging.Logger("test-logger") |
| 345 | + python_logger.debug("this is %s a template %s", "1", "2") |
| 346 | + get_client().flush() |
| 347 | + |
| 348 | + assert len(envelopes) == 1 |
| 349 | + |
| 350 | + |
| 351 | +@minimum_python_37 |
| 352 | +def test_logging_errors(sentry_init, capture_envelopes): |
| 353 | + """ |
| 354 | + The python logger module should be able to log errors without erroring |
| 355 | + """ |
| 356 | + sentry_init(_experiments={"enable_logs": True}) |
| 357 | + envelopes = capture_envelopes() |
| 358 | + |
| 359 | + python_logger = logging.Logger("test-logger") |
| 360 | + python_logger.error(Exception("test exc 1")) |
| 361 | + python_logger.error("error is %s", Exception("test exc 2")) |
| 362 | + get_client().flush() |
| 363 | + |
| 364 | + error_event_1 = envelopes[0].items[0].payload.json |
| 365 | + assert error_event_1["level"] == "error" |
| 366 | + error_event_2 = envelopes[1].items[0].payload.json |
| 367 | + assert error_event_2["level"] == "error" |
| 368 | + |
| 369 | + logs = envelopes_to_logs(envelopes) |
| 370 | + assert logs[0]["severity_text"] == "error" |
| 371 | + assert "sentry.message.template" not in logs[0]["attributes"] |
| 372 | + assert "sentry.message.parameter.0" not in logs[0]["attributes"] |
| 373 | + assert "code.line.number" in logs[0]["attributes"] |
| 374 | + |
| 375 | + assert logs[1]["severity_text"] == "error" |
| 376 | + assert logs[1]["attributes"]["sentry.message.template"] == "error is %s" |
| 377 | + assert ( |
| 378 | + logs[1]["attributes"]["sentry.message.parameter.0"] == "Exception('test exc 2')" |
| 379 | + ) |
| 380 | + assert "code.line.number" in logs[1]["attributes"] |
| 381 | + |
| 382 | + assert len(logs) == 2 |
| 383 | + |
| 384 | + |
| 385 | +def test_log_strips_project_root(sentry_init, capture_envelopes): |
| 386 | + """ |
| 387 | + The python logger should strip project roots from the log record path |
| 388 | + """ |
| 389 | + sentry_init( |
| 390 | + _experiments={"enable_logs": True}, |
| 391 | + project_root="/custom/test", |
| 392 | + ) |
| 393 | + envelopes = capture_envelopes() |
| 394 | + |
| 395 | + python_logger = logging.Logger("test-logger") |
| 396 | + python_logger.handle( |
| 397 | + logging.LogRecord( |
| 398 | + name="test-logger", |
| 399 | + level=logging.WARN, |
| 400 | + pathname="/custom/test/blah/path.py", |
| 401 | + lineno=123, |
| 402 | + msg="This is a test log with a custom pathname", |
| 403 | + args=(), |
| 404 | + exc_info=None, |
| 405 | + ) |
| 406 | + ) |
| 407 | + get_client().flush() |
| 408 | + |
| 409 | + logs = envelopes_to_logs(envelopes) |
| 410 | + assert len(logs) == 1 |
| 411 | + attrs = logs[0]["attributes"] |
| 412 | + assert attrs["code.file.path"] == "blah/path.py" |
| 413 | + |
| 414 | + |
| 415 | +def test_logger_with_all_attributes(sentry_init, capture_envelopes): |
| 416 | + """ |
| 417 | + The python logger should be able to log all attributes, including extra data. |
| 418 | + """ |
| 419 | + sentry_init(_experiments={"enable_logs": True}) |
| 420 | + envelopes = capture_envelopes() |
| 421 | + |
| 422 | + python_logger = logging.Logger("test-logger") |
| 423 | + python_logger.warning( |
| 424 | + "log #%d", |
| 425 | + 1, |
| 426 | + extra={"foo": "bar", "numeric": 42, "more_complex": {"nested": "data"}}, |
| 427 | + ) |
| 428 | + get_client().flush() |
| 429 | + |
| 430 | + logs = envelopes_to_logs(envelopes) |
| 431 | + |
| 432 | + attributes = logs[0]["attributes"] |
| 433 | + |
| 434 | + assert "process.pid" in attributes |
| 435 | + assert isinstance(attributes["process.pid"], int) |
| 436 | + del attributes["process.pid"] |
| 437 | + |
| 438 | + assert "sentry.release" in attributes |
| 439 | + assert isinstance(attributes["sentry.release"], str) |
| 440 | + del attributes["sentry.release"] |
| 441 | + |
| 442 | + assert "server.address" in attributes |
| 443 | + assert isinstance(attributes["server.address"], str) |
| 444 | + del attributes["server.address"] |
| 445 | + |
| 446 | + assert "thread.id" in attributes |
| 447 | + assert isinstance(attributes["thread.id"], int) |
| 448 | + del attributes["thread.id"] |
| 449 | + |
| 450 | + assert "code.file.path" in attributes |
| 451 | + assert isinstance(attributes["code.file.path"], str) |
| 452 | + del attributes["code.file.path"] |
| 453 | + |
| 454 | + assert "code.function.name" in attributes |
| 455 | + assert isinstance(attributes["code.function.name"], str) |
| 456 | + del attributes["code.function.name"] |
| 457 | + |
| 458 | + assert "code.line.number" in attributes |
| 459 | + assert isinstance(attributes["code.line.number"], int) |
| 460 | + del attributes["code.line.number"] |
| 461 | + |
| 462 | + assert "process.executable.name" in attributes |
| 463 | + assert isinstance(attributes["process.executable.name"], str) |
| 464 | + del attributes["process.executable.name"] |
| 465 | + |
| 466 | + assert "thread.name" in attributes |
| 467 | + assert isinstance(attributes["thread.name"], str) |
| 468 | + del attributes["thread.name"] |
| 469 | + |
| 470 | + assert attributes.pop("sentry.sdk.name").startswith("sentry.python") |
| 471 | + |
| 472 | + # Assert on the remaining non-dynamic attributes. |
| 473 | + assert attributes == { |
| 474 | + "foo": "bar", |
| 475 | + "numeric": 42, |
| 476 | + "more_complex": "{'nested': 'data'}", |
| 477 | + "logger.name": "test-logger", |
| 478 | + "sentry.origin": "auto.logger.log", |
| 479 | + "sentry.message.template": "log #%d", |
| 480 | + "sentry.message.parameter.0": 1, |
| 481 | + "sentry.environment": "production", |
| 482 | + "sentry.sdk.version": VERSION, |
| 483 | + "sentry.severity_number": 13, |
| 484 | + "sentry.severity_text": "warn", |
| 485 | + } |
0 commit comments