diff --git a/README.md b/README.md index c315b4a876..97c41298f6 100644 --- a/README.md +++ b/README.md @@ -138,9 +138,10 @@ Emeritus Maintainers: 1. Go to your Contrib repo directory. `cd ~/git/opentelemetry-python-contrib`. 2. Create a virtual env in your Contrib repo directory. `python3 -m venv my_test_venv`. 3. Activate your virtual env. `source my_test_venv/bin/activate`. -4. Make sure you have `tox` installed. `pip install tox`. -5. Run tests for a package. (e.g. `tox -e test-instrumentation-flask`.) - +4. Make sure you have `tox` installed. `pip install tox==4.13.0`. +5. Change Python version to 3.11 +6. Run tests for a package. (e.g. `tox -e test-instrumentation-flask`.) +7. To install new packages when running Tox, add the package with the desired version to the test-requirements.txt file associated with the instrumentation being tested. ### Thanks to all the people who already contributed diff --git a/examples/handlers/opentelemetry_loguru/README.md b/examples/handlers/opentelemetry_loguru/README.md new file mode 100644 index 0000000000..ba6340b6a9 --- /dev/null +++ b/examples/handlers/opentelemetry_loguru/README.md @@ -0,0 +1,138 @@ +# OpenTelemetry Python `loguru` Handler Example with Docker +This is a demo for the custom loguru handler implemented for OpenTelemetry. Overall, this example runs a basic Flask application with Docker to demonstrate an example application that uses OpenTelemetry logging with Python's logging library loguru. This example is scalable to other software systems that require the use of the loguru library for logging. + +Note: This example is adapted from OpenTelemetry's [Getting Started Tutorial for Python](https://opentelemetry.io/docs/languages/python/getting-started/) guide and OpenTelemetry's [example for logs](https://github.com/open-telemetry/opentelemetry-python/blob/main/docs/examples/logs/README.rst) code. + +## Prerequisites +Python 3 + +## Installation +Prior to building the example application, set up the directory and virtual environment: +``` +mkdir otel-loguru-example +cd otel-loguru-example +python3 -m venv venv +source ./venv/bin/activate +``` + +After activating the virtual environment `venv`, install flask and loguru. +``` +pip install flask +pip install loguru +pip install opentelemetry-exporter-otlp +``` + +### Create and Launch HTTP Server +Now that the environment is set up, create an `app.py` flask application. This is a basic example that uses the loguru Python logging library for OpenTelemetry logging instead of the standard Python logging library. + +Notice the importance of the following imports for using the loguru handler: `import loguru` and `from handlers.opentelemetry_loguru.src.exporter import LoguruHandler`. + +``` +from random import randint +from flask import Flask, request +from loguru import logger as loguru_logger +import sys +sys.path.insert(0, '../../..') +from handlers.opentelemetry_loguru.src.exporter import LoguruHandler + +from opentelemetry._logs import set_logger_provider +from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( + OTLPLogExporter, +) +from opentelemetry.sdk._logs import LoggerProvider +from opentelemetry.sdk.resources import Resource + + + +logger_provider = LoggerProvider( + resource=Resource.create( + { + "service.name": "shoppingcart", + "service.instance.id": "instance-12", + } + ), +) +set_logger_provider(logger_provider) + +# Replace the standard logging configuration with Loguru +loguru_handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) +loguru_logger.add(loguru_handler.sink) # Add LoguruHandler to the logger + +app = Flask(__name__) + +@app.route("/rolldice") +def roll_dice(): + player = request.args.get('player', default=None, type=str) + result = str(roll()) + if player: + loguru_logger.warning("Player is rolling the dice: num") + else: + loguru_logger.warning("Anonymous player is rolling the dice: num") + return result + + +def roll(): + return randint(1, 6) + +``` + +Run the application on port 8080 with the following flask command and open [http://localhost:8080/rolldice](http://localhost:8080/rolldice) in your web browser to ensure it is working. + +``` +flask run -p 8080 +``` + +However, do not be alarmed if you receive these errors since Docker is not yet set up to export the logs: +``` +Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 1s. +Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 2s. +Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 4s. +... +``` + +## Run with Docker + +To serve the application on Docker, first create the `otel-collector-config.yaml` file locally in the application's repository. +``` +# otel-collector-config.yaml +receivers: + otlp: + protocols: + grpc: + +processors: + batch: + +exporters: + logging: + verbosity: detailed + +service: + pipelines: + logs: + receivers: [otlp] + processors: [batch] + exporters: [logging] +``` + +Next, start the Docker container: +``` +docker run \ + -p 4317:4317 \ + -v $(pwd)/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml \ + otel/opentelemetry-collector-contrib:latest +``` + +And lastly, run the basic application with flask: +``` +flask run -p 8080 +``` + +Here is some example output: +``` + +``` + + +## Contributors +Caroline Gilbert: [carolincgilbert](https://github.com/carolinecgilbert) diff --git a/examples/handlers/opentelemetry_loguru/app.py b/examples/handlers/opentelemetry_loguru/app.py new file mode 100644 index 0000000000..2a23dab402 --- /dev/null +++ b/examples/handlers/opentelemetry_loguru/app.py @@ -0,0 +1,37 @@ +from random import randint +from flask import Flask, request +from loguru import logger as loguru_logger +import sys +sys.path.insert(0, '../../..') +from handlers.opentelemetry_loguru.src.exporter import LoguruHandler + +from opentelemetry._logs import set_logger_provider +from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( + OTLPLogExporter, +) +from opentelemetry.sdk._logs import LoggerProvider +from opentelemetry.sdk.resources import Resource + + + + +# Replace the standard logging configuration with Loguru +loguru_handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) +loguru_logger.add(loguru_handler.sink) # Add LoguruHandler to the logger + + +app = Flask(__name__) + +@app.route("/rolldice") +def roll_dice(): + player = request.args.get('player', default=None, type=str) + result = str(roll()) + if player: + loguru_logger.warning(f"Player {player} is rolling the dice: {result}") + else: + loguru_logger.warning(f"Anonymous player is rolling the dice: {result}") + return result + + +def roll(): + return randint(1, 6) diff --git a/examples/handlers/opentelemetry_loguru/otel-collector-config.yaml b/examples/handlers/opentelemetry_loguru/otel-collector-config.yaml new file mode 100644 index 0000000000..5525cdd849 --- /dev/null +++ b/examples/handlers/opentelemetry_loguru/otel-collector-config.yaml @@ -0,0 +1,19 @@ +# otel-collector-config.yaml +receivers: + otlp: + protocols: + grpc: + +processors: + batch: + +exporters: + logging: + verbosity: detailed + +service: + pipelines: + logs: + receivers: [otlp] + processors: [batch] + exporters: [logging] \ No newline at end of file diff --git a/examples/handlers/opentelemetry_structlog/README.md b/examples/handlers/opentelemetry_structlog/README.md new file mode 100644 index 0000000000..214fd32210 --- /dev/null +++ b/examples/handlers/opentelemetry_structlog/README.md @@ -0,0 +1,156 @@ +# OpenTelemetry Python `structlog` Handler Example with Docker +This is a demo for the custom structlog handler implemented for OpenTelemetry. Overall, this example runs a basic Flask application with Docker to demonstrate an example application that uses OpenTelemetry logging with Python's logging library structlog. This example is scalable to other software systems that require the use of the structlog library for logging. + +Note: This example is adapted from OpenTelemetry's [Getting Started Tutorial for Python](https://opentelemetry.io/docs/languages/python/getting-started/) guide and OpenTelemetry's [example for logs](https://github.com/open-telemetry/opentelemetry-python/blob/main/docs/examples/logs/README.rst) code. + +## Prerequisites +Python 3 + +## Installation +Prior to building the example application, set up the directory and virtual environment: +``` +mkdir otel-structlog-example +cd otel-structlog-example +python3 -m venv venv +source ./venv/bin/activate +``` + +After activating the virtual environment `venv`, install flask and structlog. +``` +pip install flask +pip install structlog +pip install opentelemetry-exporter-otlp +``` + +### Create and Launch HTTP Server +Now that the environment is set up, create an `app.py` flask application. This is a basic example that uses the structlog Python logging library for OpenTelemetry logging instead of the standard Python logging library. + +Notice the importance of the following imports for using the structlog handler: `import structlog` and `from handlers.opentelemetry_structlog.src.exporter import StructlogHandler`. + +``` +from random import randint +from flask import Flask, request +import structlog +import sys +sys.path.insert(0, '../../..') +from handlers.opentelemetry_structlog.src.exporter import StructlogHandler +from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( + OTLPLogExporter, +) +from opentelemetry.sdk._logs import LoggerProvider +from opentelemetry.sdk.resources import Resource +from opentelemetry._logs import set_logger_provider +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.sdk.resources import Resource + +logger_provider = LoggerProvider( + resource=Resource.create( + { + "service.name": "shoppingcart", + "service.instance.id": "instance-12", + } + ), +) +set_logger_provider(logger_provider) + +# Replace the standard logging configuration with Loguru +structlog_handler = StructlogHandler(service_name="flask-structlog-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) +structlog_handler._logger_provider = logger_provider +structlog_logger = structlog.wrap_logger(structlog.get_logger(), processors=[structlog_handler]) # Add StructlogHandler to the logger + +app = Flask(__name__) + +@app.route("/rolldice") +def roll_dice(): + player = request.args.get('player', default=None, type=str) + result = str(roll()) + if player: + structlog_logger.warning("Player %s is rolling the dice: %s", player, result, level="warning") + else: + structlog_logger.warning("Anonymous player is rolling the dice: %s", result, level="warning") + return result + + +def roll(): + return randint(1, 6) +``` + +Run the application on port 8080 with the following flask command and open [http://localhost:8080/rolldice](http://localhost:8080/rolldice) in your web browser to ensure it is working. + +``` +flask run -p 8080 +``` + +However, do not be alarmed if you receive these errors since Docker is not yet set up to export the logs: +``` +Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 1s. +Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 2s. +Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 4s. +... +``` + +## Run with Docker + +To serve the application on Docker, first create the `otel-collector-config.yaml` file locally in the application's repository. +``` +# otel-collector-config.yaml +receivers: + otlp: + protocols: + grpc: + +processors: + batch: + +exporters: + logging: + verbosity: detailed + +service: + pipelines: + logs: + receivers: [otlp] + processors: [batch] + exporters: [logging] +``` + +Next, start the Docker container: +``` +docker run \ + -p 4317:4317 \ + -v $(pwd)/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml \ + otel/opentelemetry-collector-contrib:latest +``` + +And lastly, run the basic application with flask: +``` +flask run -p 8080 +``` + +Here is some example output: +``` + * Debug mode: off +WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:8080 +Press CTRL+C to quit +2024-04-28 23:15:22 [warning ] Anonymous player is rolling the dice: 1 +127.0.0.1 - - [28/Apr/2024 23:15:22] "GET /rolldice HTTP/1.1" 200 - +2024-04-28 23:15:27 [warning ] Anonymous player is rolling the dice: 6 +127.0.0.1 - - [28/Apr/2024 23:15:27] "GET /rolldice HTTP/1.1" 200 - +2024-04-28 23:15:28 [warning ] Anonymous player is rolling the dice: 3 +127.0.0.1 - - [28/Apr/2024 23:15:28] "GET /rolldice HTTP/1.1" 200 - +2024-04-28 23:15:29 [warning ] Anonymous player is rolling the dice: 4 +127.0.0.1 - - [28/Apr/2024 23:15:29] "GET /rolldice HTTP/1.1" 200 - +2024-04-28 23:15:29 [warning ] Anonymous player is rolling the dice: 1 +127.0.0.1 - - [28/Apr/2024 23:15:29] "GET /rolldice HTTP/1.1" 200 - +2024-04-28 23:15:30 [warning ] Anonymous player is rolling the dice: 2 +127.0.0.1 - - [28/Apr/2024 23:15:30] "GET /rolldice HTTP/1.1" 200 - +2024-04-28 23:15:31 [warning ] Anonymous player is rolling the dice: 3 +127.0.0.1 - - [28/Apr/2024 23:15:31] "GET /rolldice HTTP/1.1" 200 - +2024-04-28 23:16:14 [warning ] Anonymous player is rolling the dice: 4 +127.0.0.1 - - [28/Apr/2024 23:16:14] "GET /rolldice HTTP/1.1" 200 - +``` + + +## Contributors +Caroline Gilbert: [carolincgilbert](https://github.com/carolinecgilbert) diff --git a/examples/handlers/opentelemetry_structlog/app.py b/examples/handlers/opentelemetry_structlog/app.py new file mode 100644 index 0000000000..82745b48bc --- /dev/null +++ b/examples/handlers/opentelemetry_structlog/app.py @@ -0,0 +1,47 @@ +from random import randint +from flask import Flask, request +import structlog +import sys +sys.path.insert(0, '../../..') +from handlers.opentelemetry_structlog.src.exporter import StructlogHandler +from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( + OTLPLogExporter, +) +from opentelemetry.sdk._logs import LoggerProvider +from opentelemetry.sdk.resources import Resource +from opentelemetry._logs import set_logger_provider +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.sdk.resources import Resource + +logger_provider = LoggerProvider( + resource=Resource.create( + { + "service.name": "shoppingcart", + "service.instance.id": "instance-12", + } + ), +) +set_logger_provider(logger_provider) + +# Replace the standard logging configuration with Loguru +structlog_handler = StructlogHandler(service_name="flask-structlog-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) +structlog_handler._logger_provider = logger_provider +structlog_logger = structlog.wrap_logger(structlog.get_logger(), processors=[structlog_handler]) # Add StructlogHandler to the logger + + + +app = Flask(__name__) + +@app.route("/rolldice") +def roll_dice(): + player = request.args.get('player', default=None, type=str) + result = str(roll()) + if player: + structlog_logger.warning("Player %s is rolling the dice: %s", player, result, level="warning") + else: + structlog_logger.warning("Anonymous player is rolling the dice: %s", result, level="warning") + return result + + +def roll(): + return randint(1, 6) diff --git a/examples/handlers/opentelemetry_structlog/otel-collector-config.yaml b/examples/handlers/opentelemetry_structlog/otel-collector-config.yaml new file mode 100644 index 0000000000..5525cdd849 --- /dev/null +++ b/examples/handlers/opentelemetry_structlog/otel-collector-config.yaml @@ -0,0 +1,19 @@ +# otel-collector-config.yaml +receivers: + otlp: + protocols: + grpc: + +processors: + batch: + +exporters: + logging: + verbosity: detailed + +service: + pipelines: + logs: + receivers: [otlp] + processors: [batch] + exporters: [logging] \ No newline at end of file diff --git a/handlers/__init__.py b/handlers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/handlers/opentelemetry_loguru/__init__.py b/handlers/opentelemetry_loguru/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/handlers/opentelemetry_loguru/src/README.md b/handlers/opentelemetry_loguru/src/README.md new file mode 100644 index 0000000000..e06194f51a --- /dev/null +++ b/handlers/opentelemetry_loguru/src/README.md @@ -0,0 +1,53 @@ +# Loguru Handler for OpenTelemetry + +This project provides a Loguru handler for OpenTelemetry applications. The handler converts Loguru logs into the OpenTelemetry Logs Protocol (OTLP) format for export to a collector. + +## Usage + +To use the Loguru handler in your OpenTelemetry application, follow these steps: + +1. Import the necessary modules: + +```python +import loguru +from handlers.opentelemetry_loguru.src.exporter import LoguruHandler +from opentelemetry.sdk._logs._internal.export import LogExporter +from opentelemetry.sdk.resources import Resource +``` + +2. Initialize the LoguruHandler with your service name, server hostname, and LogExporter instance: + +```python +service_name = "my_service" +server_hostname = "my_server" +exporter = LogExporter() # Initialize your LogExporter instance +handler = LoguruHandler(service_name, server_hostname, exporter) +``` + +3. Add the handler to your Loguru logger: + +```python +logger = loguru.logger +logger.add(handler.sink) +``` + +4. Use the logger as usual with Loguru: + +```python +logger.warning("This is a test log message.") +``` +## OpenTelemetry Application Example with Handler +See the loguru handler demo in the examples directory of this repository for a step-by-step guide on using the handler in an OpenTelemetry application. + +## Customization + +The LoguruHandler supports customization through its constructor parameters: + +- `service_name`: The name of your service. +- `server_hostname`: The hostname of the server where the logs originate. +- `exporter`: An instance of your LogExporter for exporting logs to a collector. + +## Notes + +- This handler automatically converts Loguru logs into the OTLP format for compatibility with OpenTelemetry. +- It extracts attributes from Loguru logs and maps them to OpenTelemetry attributes for log records. diff --git a/handlers/opentelemetry_loguru/src/__init__.py b/handlers/opentelemetry_loguru/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/handlers/opentelemetry_loguru/src/exporter.py b/handlers/opentelemetry_loguru/src/exporter.py new file mode 100644 index 0000000000..4b3e475ab5 --- /dev/null +++ b/handlers/opentelemetry_loguru/src/exporter.py @@ -0,0 +1,203 @@ +import traceback +from datetime import datetime, timezone +from typing import Dict + +import loguru +import traceback +from os import environ +from time import time_ns +from typing import Any, Callable, Optional, Tuple, Union # noqa +from opentelemetry._logs import ( + NoOpLogger, + SeverityNumber, + get_logger, + get_logger_provider, + std_to_otel, +) +from opentelemetry.attributes import BoundedAttributes +from opentelemetry.sdk.environment_variables import ( + OTEL_ATTRIBUTE_COUNT_LIMIT, + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, +) +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.util import ns_to_iso_str +from opentelemetry.sdk.util.instrumentation import InstrumentationScope +from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.trace import ( + format_span_id, + format_trace_id, + get_current_span, +) +from opentelemetry.trace.span import TraceFlags +from opentelemetry.util.types import Attributes + +from opentelemetry._logs import Logger as APILogger +from opentelemetry._logs import LoggerProvider as APILoggerProvider +from opentelemetry._logs import LogRecord as APILogRecord + +from opentelemetry._logs import std_to_otel +from opentelemetry.sdk._logs._internal import LoggerProvider, LogRecord +from opentelemetry.sdk._logs._internal.export import BatchLogRecordProcessor, LogExporter +from opentelemetry.sdk.resources import Resource +from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.trace import get_current_span + +from opentelemetry._logs.severity import SeverityNumber + +import sys +import json + +_STD_TO_OTEL = { + 10: SeverityNumber.DEBUG, + 11: SeverityNumber.DEBUG2, + 12: SeverityNumber.DEBUG3, + 13: SeverityNumber.DEBUG4, + 14: SeverityNumber.DEBUG4, + 15: SeverityNumber.DEBUG4, + 16: SeverityNumber.DEBUG4, + 17: SeverityNumber.DEBUG4, + 18: SeverityNumber.DEBUG4, + 19: SeverityNumber.DEBUG4, + 20: SeverityNumber.INFO, + 21: SeverityNumber.INFO2, + 22: SeverityNumber.INFO3, + 23: SeverityNumber.INFO4, + 24: SeverityNumber.INFO4, + 25: SeverityNumber.INFO4, + 26: SeverityNumber.INFO4, + 27: SeverityNumber.INFO4, + 28: SeverityNumber.INFO4, + 29: SeverityNumber.INFO4, + 30: SeverityNumber.WARN, + 31: SeverityNumber.WARN2, + 32: SeverityNumber.WARN3, + 33: SeverityNumber.WARN4, + 34: SeverityNumber.WARN4, + 35: SeverityNumber.WARN4, + 36: SeverityNumber.WARN4, + 37: SeverityNumber.WARN4, + 38: SeverityNumber.WARN4, + 39: SeverityNumber.WARN4, + 40: SeverityNumber.ERROR, + 41: SeverityNumber.ERROR2, + 42: SeverityNumber.ERROR3, + 43: SeverityNumber.ERROR4, + 44: SeverityNumber.ERROR4, + 45: SeverityNumber.ERROR4, + 46: SeverityNumber.ERROR4, + 47: SeverityNumber.ERROR4, + 48: SeverityNumber.ERROR4, + 49: SeverityNumber.ERROR4, + 50: SeverityNumber.FATAL, + 51: SeverityNumber.FATAL2, + 52: SeverityNumber.FATAL3, + 53: SeverityNumber.FATAL4, +} + +EXCLUDE_ATTR = ("elapsed", "exception", "extra", "file", "level", "process", "thread", "time") +class LoguruHandler: + + # this was largely inspired by the OpenTelemetry handler for stdlib `logging`: + # https://github.com/open-telemetry/opentelemetry-python/blob/8f312c49a5c140c14d1829c66abfe4e859ad8fd7/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py#L318 + + def __init__( + self, + service_name: str, + server_hostname: str, + exporter: LogExporter, + ) -> None: + logger_provider = LoggerProvider( + resource=Resource.create( + { + "service.name": service_name, + "service.instance.id": server_hostname, + } + ), + ) + + logger_provider.add_log_record_processor( + BatchLogRecordProcessor(exporter, max_export_batch_size=1) + ) + + self._logger_provider = logger_provider + self._logger = logger_provider.get_logger(__name__) + + + def _get_attributes(self, record) -> Attributes: + attributes = {key:value for key, value in record.items() if key not in EXCLUDE_ATTR} + + # Add standard code attributes for logs. + attributes[SpanAttributes.CODE_FILEPATH] = record['file'].path #This includes file and path -> (file, path) + attributes[SpanAttributes.CODE_FUNCTION] = record['function'] + attributes[SpanAttributes.CODE_LINENO] = record['line'] + + attributes['process_name'] = (record['process']).name + attributes['process_id'] = (record['process']).id + attributes['thread_name'] = (record['thread']).name + attributes['thread_id'] = (record['thread']).id + attributes['file'] = record['file'].name + + if record['exception'] is not None: + + attributes[SpanAttributes.EXCEPTION_TYPE] = record['exception'].type + + attributes[SpanAttributes.EXCEPTION_MESSAGE] = record['exception'].value + + attributes[SpanAttributes.EXCEPTION_STACKTRACE] = record['exception'].traceback + + return attributes + + def _loguru_to_otel(self, levelno: int) -> SeverityNumber: + if levelno < 10 or levelno == 25: + return SeverityNumber.UNSPECIFIED + + elif levelno > 53: + return SeverityNumber.FATAL4 + + return _STD_TO_OTEL[levelno] + + + + + def _translate(self, record) -> LogRecord: + + #Timestamp + timestamp = int((record["time"].timestamp()) * 1e9) + + #Observed timestamp + observedTimestamp = time_ns() + + #Span context + spanContext = get_current_span().get_span_context() + + #Setting the level name + if record['level'].name == 'WARNING': + levelName = 'WARN' + elif record['level'].name == 'TRACE' or record['level'].name == 'SUCCESS': + levelName = 'NOTSET' + else: + levelName = record['level'].name + + #Severity number + severityNumber = self._loguru_to_otel(int(record["level"].no)) + + #Getting attributes + attributes = self._get_attributes(record) + + + return LogRecord( + timestamp = timestamp, + observed_timestamp = observedTimestamp, + trace_id = spanContext.trace_id, + span_id = spanContext.span_id, + trace_flags = spanContext.trace_flags, + severity_text = levelName, + severity_number = severityNumber, + body=record['message'], + resource = self._logger.resource, + attributes=attributes + ) + + def sink(self, record) -> None: + self._logger.emit(self._translate(record.record)) + \ No newline at end of file diff --git a/handlers/opentelemetry_structlog/README.md b/handlers/opentelemetry_structlog/README.md new file mode 100644 index 0000000000..ac5652f1d3 --- /dev/null +++ b/handlers/opentelemetry_structlog/README.md @@ -0,0 +1 @@ +# Structlog handler for OpenTelemetry diff --git a/handlers/opentelemetry_structlog/__init__.py b/handlers/opentelemetry_structlog/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/handlers/opentelemetry_structlog/pyproject.toml b/handlers/opentelemetry_structlog/pyproject.toml new file mode 100644 index 0000000000..241c46dfed --- /dev/null +++ b/handlers/opentelemetry_structlog/pyproject.toml @@ -0,0 +1,45 @@ +[build-system] +requires = [ + "hatchling", +] +build-backend = "hatchling.build" + +[project] +name = "opentelemetry-structlog" +dynamic = [ + "version", +] +description = "Structlog handler for emitting logs to OpenTelemetry" +readme = "README.md" +license = "Apache-2.0" +requires-python = ">=3.7" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dependencies = [ + "opentelemetry-sdk ~= 1.22", + "structlog ~= 24.1", +] + +[tool.hatch.version] +path = "src/opentelemetry-structlog/version.py" + +[tool.hatch.build.targets.sdist] +include = [ + "/src", +] + +[tool.hatch.build.targets.wheel] +packages = [ + "src/opentelemetry-structlog", +] diff --git a/handlers/opentelemetry_structlog/src/README.md b/handlers/opentelemetry_structlog/src/README.md new file mode 100644 index 0000000000..a61fef5e81 --- /dev/null +++ b/handlers/opentelemetry_structlog/src/README.md @@ -0,0 +1,60 @@ +# Structlog Handler for OpenTelemetry +This project provides a Structlog handler for OpenTelemetry applications. The handler converts Structlog logs into the OpenTelemetry Logs Protocol (OTLP) format for export to a collector. + +## Usage + +To use the Structlog handler in your OpenTelemetry application, follow these steps: + +1. Import the necessary modules: + +```python +import structlog +from opentelemetry.sdk._logs._internal.export import LogExporter +from opentelemetry.sdk.resources import Resource +from handlers.opentelemetry_structlog.src.exporter import StructlogHandler +``` + +2. Initialize the StructlogHandler with your service name, server hostname, and LogExporter instance: + +```python +service_name = "my_service" +server_hostname = "my_server" +exporter = LogExporter() # Initialize your LogExporter instance +handler = StructlogHandler(service_name, server_hostname, exporter) +``` + +3. Add the handler to your Structlog logger: + +```python +structlog.configure( + processors=[structlog.processors.JSONRenderer()], + logger_factory=structlog.stdlib.LoggerFactory(), + wrapper_class=structlog.stdlib.BoundLogger, + cache_logger_on_first_use=True, + context_class=dict, + **handler.wrap_for_structlog(), +) +``` + +4. Use the logger as usual with Structlog: + +```python +logger = structlog.get_logger() +logger.info("This is a test log message.") +``` +## OpenTelemetry Application Example with Handler +See the structlog handler demo in the examples directory of this repository for a step-by-step guide on using the handler in an OpenTelemetry application. + +## Customization + +The StructlogHandler supports customization through its constructor parameters: + +- `service_name`: The name of your service. +- `server_hostname`: The hostname of the server where the logs originate. +- `exporter`: An instance of your LogExporter for exporting logs to a collector. + +## Notes + +- This handler automatically converts the `timestamp` key in the `event_dict` to ISO 8601 format for better compatibility. +- It performs operations similar to `structlog.processors.ExceptionRenderer`, so avoid using `ExceptionRenderer` in the same pipeline. +``` diff --git a/handlers/opentelemetry_structlog/src/__init__.py b/handlers/opentelemetry_structlog/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/handlers/opentelemetry_structlog/src/exporter.py b/handlers/opentelemetry_structlog/src/exporter.py new file mode 100644 index 0000000000..9300391184 --- /dev/null +++ b/handlers/opentelemetry_structlog/src/exporter.py @@ -0,0 +1,161 @@ +"""OpenTelemetry processor for structlog.""" + +import traceback +from datetime import datetime, timezone +from typing import Dict + +import structlog +from opentelemetry._logs import std_to_otel +from opentelemetry.sdk._logs._internal import LoggerProvider, LogRecord +from opentelemetry.sdk._logs._internal.export import BatchLogRecordProcessor, LogExporter +from opentelemetry.sdk.resources import Resource +from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.trace import get_current_span +from structlog._frames import _format_exception +from structlog._log_levels import NAME_TO_LEVEL +from structlog.processors import _figure_out_exc_info + +_EXCLUDE_ATTRS = {"exception", "timestamp"} + + +class StructlogHandler: + """A structlog processor that writes logs in OTLP format to a collector. + + Note: this will replace (or insert if not present) the `timestamp` key in the + `event_dict` to be in an ISO 8601 format that is more widely recognized. This + means that `structlog.processors.TimeStamper` is not required to be added to the + processors list if this processor is used. + + Note: this also performs the operations done by + `structlog.processors.ExceptionRenderer`. DO NOT use `ExceptionRenderer` in the + same processor pipeline as this processor. + """ + + # this was largely inspired by the OpenTelemetry handler for stdlib `logging`: + # https://github.com/open-telemetry/opentelemetry-python/blob/8f312c49a5c140c14d1829c66abfe4e859ad8fd7/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py#L318 + + def __init__( + self, + service_name: str, + server_hostname: str, + exporter: LogExporter, + ) -> None: + logger_provider = LoggerProvider( + resource=Resource.create( + { + "service.name": service_name, + "service.instance.id": server_hostname, + } + ), + ) + + logger_provider.add_log_record_processor( + BatchLogRecordProcessor(exporter, max_export_batch_size=1) + ) + + self._logger_provider = logger_provider + self._logger = logger_provider.get_logger(__name__) + + def _pre_process( + self, event_dict: structlog.typing.EventDict + ) -> structlog.typing.EventDict: + event_dict["timestamp"] = datetime.now(timezone.utc) + + self._pre_process_exc_info(event_dict) + + return event_dict + + def _post_process( + self, event_dict: structlog.typing.EventDict + ) -> structlog.typing.EventDict: + event_dict["timestamp"] = event_dict["timestamp"].isoformat() + + self._post_process_exc_info(event_dict) + + return event_dict + + def _pre_process_exc_info( + self, event_dict: structlog.typing.EventDict + ) -> structlog.typing.EventDict: + exc_info = event_dict.pop("exc_info", None) + if exc_info is not None: + event_dict["exception"] = _figure_out_exc_info(exc_info) + + return event_dict + + def _post_process_exc_info( + self, event_dict: structlog.typing.EventDict + ) -> structlog.typing.EventDict: + exception = event_dict.pop("exception", None) + if exception is not None: + event_dict["exception"] = _format_exception(exception) + + return event_dict + + def _translate( + self, + timestamp: int, + extra_attrs: Dict[str, str], + event_dict: structlog.typing.EventDict, + ) -> LogRecord: + span_context = get_current_span().get_span_context() + # attributes = self._get_attributes(record) + severity_number = std_to_otel(NAME_TO_LEVEL[event_dict["level"]]) + + return LogRecord( + timestamp=timestamp, + trace_id=span_context.trace_id, + span_id=span_context.span_id, + trace_flags=span_context.trace_flags, + severity_text=event_dict["level"], + severity_number=severity_number, + body=event_dict["event"], + resource=self._logger.resource, + attributes={ + **{k: v for k, v in event_dict.items() if k not in _EXCLUDE_ATTRS}, + **extra_attrs, + }, + ) + + @staticmethod + def _parse_timestamp(event_dict: structlog.typing.EventDict) -> int: + return int(event_dict["timestamp"].timestamp() * 1e9) + + @staticmethod + def _parse_exception(event_dict: structlog.typing.EventDict) -> Dict[str, str]: + # taken from: https://github.com/open-telemetry/opentelemetry-python/blob/c4d17e9f14f3cafb6757b96eefabdc7ed4891306/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py#L458-L475 + attributes: Dict[str, str] = {} + exception = event_dict.get("exception", None) + if exception is not None: + exc_type = "" + message = "" + stack_trace = "" + exctype, value, tb = exception + if exctype is not None: + exc_type = exctype.__name__ + if value is not None and value.args: + message = value.args[0] + if tb is not None: + # https://github.com/open-telemetry/opentelemetry-specification/blob/9fa7c656b26647b27e485a6af7e38dc716eba98a/specification/trace/semantic_conventions/exceptions.md#stacktrace-representation + stack_trace = "".join(traceback.format_exception(*exception)) + attributes[SpanAttributes.EXCEPTION_TYPE] = exc_type + attributes[SpanAttributes.EXCEPTION_MESSAGE] = message + attributes[SpanAttributes.EXCEPTION_STACKTRACE] = stack_trace + + return attributes + + def __call__( + self, + logger: structlog.typing.WrappedLogger, + name: str, + event_dict: structlog.typing.EventDict, + ): + """Emit a record.""" + event_dict = self._pre_process(event_dict) + timestamp = self._parse_timestamp(event_dict) + extra_attrs = self._parse_exception(event_dict) + event_dict = self._post_process(event_dict) + + self._logger.emit(self._translate(timestamp, extra_attrs, event_dict)) + + return event_dict diff --git a/handlers/opentelemetry_structlog/src/version.py b/handlers/opentelemetry_structlog/src/version.py new file mode 100644 index 0000000000..3dc1f76bc6 --- /dev/null +++ b/handlers/opentelemetry_structlog/src/version.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt index f376796169..ad7b238d7c 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt @@ -13,5 +13,8 @@ tomli==2.0.1 typing_extensions==4.9.0 wrapt==1.16.0 zipp==3.17.0 +structlog==24.1.0 +loguru==0.7.2 +opentelemetry-exporter-otlp==1.24.0 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-logging diff --git a/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py b/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py index a5a0d5adff..0f5c6f23ec 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py +++ b/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py @@ -15,16 +15,37 @@ import logging from typing import Optional from unittest import mock +import unittest +from collections import namedtuple import pytest +# Imports for StructlogHandler tests +from unittest.mock import Mock +from handlers.opentelemetry_structlog.src.exporter import LogExporter + +from datetime import datetime, timezone + +from unittest.mock import MagicMock, patch +from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( + OTLPLogExporter, +) + + +from opentelemetry.semconv.trace import SpanAttributes + from opentelemetry.instrumentation.logging import ( # pylint: disable=no-name-in-module DEFAULT_LOGGING_FORMAT, LoggingInstrumentor, ) from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import ProxyTracer, get_tracer +from opentelemetry.trace import ProxyTracer, get_tracer, get_current_span, SpanContext, TraceFlags + +from handlers.opentelemetry_structlog.src.exporter import StructlogHandler +from handlers.opentelemetry_loguru.src.exporter import LoguruHandler, _STD_TO_OTEL +from opentelemetry._logs import get_logger_provider, get_logger +import time class FakeTracerProvider: def get_tracer( # pylint: disable=no-self-use @@ -207,3 +228,289 @@ def test_uninstrumented(self): self.assertFalse(hasattr(record, "otelTraceID")) self.assertFalse(hasattr(record, "otelServiceName")) self.assertFalse(hasattr(record, "otelTraceSampled")) + +# StructlogHandler Tests +# Test Initialization +class TestStructlogHandler(TestBase): + @pytest.fixture(autouse=True) + def inject_fixtures(self, caplog): + self.caplog = caplog # pylint: disable=attribute-defined-outside-init + + def mocker(self): + return MagicMock() + + def setUp(self): + super().setUp() + LoggingInstrumentor().instrument() + self.tracer = get_tracer(__name__) + + def tearDown(self): + super().tearDown() + LoggingInstrumentor().uninstrument() + + def structlog_exporter(self): + with self.caplog.at_level(level=logging.INFO): + # Mock the LogExporter dependency + mock_exporter = Mock(spec=LogExporter) + # Instantiate the StructlogHandler with mock dependencies + exporter = StructlogHandler("test_service", "test_host", mock_exporter) + return exporter + + def test_initialization(self): + exporter = self.structlog_exporter() + assert exporter._logger_provider is not None, "LoggerProvider should be initialized" + assert exporter._logger is not None, "Logger should be initialized" + + def test_pre_process_adds_timestamp(self): + event_dict = {"event": "test_event"} + processed_event = self.structlog_exporter()._pre_process(event_dict) + assert "timestamp" in processed_event, "Timestamp should be added in pre-processing" + + def test_post_process_formats_timestamp(self): + # Assuming the pre_process method has added a datetime object + event_dict = {"timestamp": datetime.now(timezone.utc)} + processed_event = self.structlog_exporter()._post_process(event_dict) + assert isinstance(processed_event["timestamp"], str), "Timestamp should be formatted to string in ISO format" + + def test_parse_exception(self): + # Mocking an exception event + exception = (ValueError, ValueError("mock error"), None) + event_dict = {"exception": exception} + parsed_exception = self.structlog_exporter()._parse_exception(event_dict) + assert parsed_exception["exception.type"] == "ValueError", "Exception type should be parsed" + assert parsed_exception["exception.message"] == "mock error", "Exception message should be parsed" + + def test_parse_timestamp(self): + # Assuming a specific datetime for consistency + fixed_datetime = datetime(2020, 1, 1, tzinfo=timezone.utc) + event_dict = {"timestamp": fixed_datetime} + timestamp = self.structlog_exporter()._parse_timestamp(event_dict) + expected_timestamp = 1577836800000000000 # Expected nanoseconds since epoch + assert timestamp == expected_timestamp, "Timestamp should be correctly parsed to nanoseconds" + + def test_call_method_processes_log_correctly(self): + # Mock the logger and exporter + exporter = MagicMock() + logger = MagicMock() + exporter_instance = StructlogHandler("test_service", "test_host", exporter) + exporter_instance._logger = logger + + # Define an event dictionary + event_dict = {"level": "info", "event": "test event", "timestamp": datetime.now(timezone.utc)} + + # Call the __call__ method of StructlogHandler + processed_event = exporter_instance(logger=None, name=None, event_dict=event_dict) + + # Assert that the logger's emit method was called with the processed event + logger.emit.assert_called_once() + + def test_log_record_translation_attributes(self): + """Verify that event_dict translates correctly into a LogRecord with the correct attributes.""" + exporter = MagicMock() + logger = MagicMock() + exporter_instance = StructlogHandler("test_service", "test_host", exporter) + exporter_instance._logger = logger + + timestamp = datetime.now(timezone.utc).isoformat() + event_dict = { + "level": "info", + "event": "test event", + "timestamp": timestamp + } + # Get the StructlogHandler instance + + # Assuming StructlogHandler has a method to process and possibly log the event_dict directly. + # Use the instance to process the event_dict. + # Mocking the internal logger's emit method to capture the log record + with patch.object(exporter_instance._logger, 'emit') as mock_emit: + exporter_instance(event_dict=event_dict, logger=logger, name=None) + calls = mock_emit.call_args_list + assert len(calls) > 0, "Emit should be called" + log_record = calls[0][0][0] # First call, first arg + + # Assuming log_record is the structured log that would have been emitted, + # and you need to verify its contents. + # Need to adjust the assertion depending on how log records are structured. + # I am assuming log_record is a dictionary that was passed to logger.emit. + assert log_record.body == event_dict["event"], "LogRecord body should match event" + + assert log_record.attributes["level"] == event_dict["level"], "LogRecord level should match event" + + def test_filtering_of_excluded_attributes(self): + """Ensure specified attributes are not passed to the log record.""" + event_dict = { + "level": "error", + "event": "Something happened!", + "timestamp": datetime.now(timezone.utc), + "exception": (ValueError, ValueError("An error occurred"), None) + } + + # Get the StructlogHandler instance + exporter_instance = self.structlog_exporter() + + with patch.object(exporter_instance._logger, "emit") as mocked_emit: + # Call the exporter_instance with the event_dict + exporter_instance(event_dict=event_dict, logger=None, name=None) + + # Check if emit method was called + mocked_emit.assert_called_once() + + # Get the log record passed to emit method + log_record = mocked_emit.call_args.args[0] + + # Check if the exception attribute is not present in the log record + assert "exception" not in log_record.attributes, "Excluded attributes should not be in the log record" + + + def test_trace_context_propogation(self): + """Ensure trace context is correctly propagated to the log record.""" + with self.tracer.start_as_current_span("test_span") as span: + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + trace_sampled = span.get_span_context().trace_flags.sampled + event_dict = { + "level": "info", + "event": "test event", + "timestamp": datetime.now(timezone.utc) + } + + # Get the StructlogHandler instance + exporter_instance = self.structlog_exporter() + + with patch.object(exporter_instance, "_logger") as mocked_logger: + exporter_instance(event_dict=event_dict, logger=None, name=None) + calls = mocked_logger.emit.call_args_list + log_record = calls[0][0][0] + + # Assert that the log record has the correct trace context + actual_span_id = format(log_record.span_id, "016x") + assert actual_span_id == span_id, "Span ID should be propagated" + + actual_trace_id = format(log_record.trace_id, "032x") + assert actual_trace_id == trace_id, "Trace ID should be propagated" + + assert log_record.trace_flags == trace_sampled, "Trace flags should be propagated" + +class TimestampRecord: + def __init__(self, data): + self.timestam = data + def timestamp(self): + return self.timestam + +class TestLoguruHandler(TestBase): + def setUp(self): + self.default_provider = get_logger_provider() + self.custom_provider = MagicMock() + + RecordFile = namedtuple('RecordFile', ['path', 'name']) + file_record = RecordFile( + path="test_file.py", + name = "test_file.py" + ) + + RecordProcess = namedtuple('RecordProcess', ['name', 'id']) + process_record = RecordProcess( + name = "MainProcess", + id = 1 + ) + + RecordThread = namedtuple('RecordThread', ['name', 'id']) + thread_record = RecordThread( + name = "MainThread", + id = 1 + ) + + timeRec = TimestampRecord(data=2.38763786) + + self.record = { + "time": timeRec, + "level": MagicMock(name="ERROR", no=40), + "message": "Test message", + "file": file_record, + "process": process_record, + "thread": thread_record, + "function": "test_function", + "line": 123, + "exception": None + } + + self.span_context = get_current_span().get_span_context() + self.current_span = MagicMock() + self.current_span.get_span_context.return_value = self.span_context + + def test_attributes_extraction_without_exception(self): + handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) + + attrs = handler._get_attributes(self.record) + + expected_attrs = { + SpanAttributes.CODE_FILEPATH: 'test_file.py', + SpanAttributes.CODE_FUNCTION: 'test_function', + SpanAttributes.CODE_LINENO: 123 + } + + self.assertEqual(attrs[SpanAttributes.CODE_FILEPATH], expected_attrs[SpanAttributes.CODE_FILEPATH]) + self.assertEqual(attrs[SpanAttributes.CODE_FUNCTION], expected_attrs[SpanAttributes.CODE_FUNCTION]) + self.assertEqual(attrs[SpanAttributes.CODE_LINENO], expected_attrs[SpanAttributes.CODE_LINENO]) + + @patch('traceback.format_exception') + def test_attributes_extraction_with_exception(self, mock_format_exception): + mock_format_exception.return_value = 'Exception traceback' + exception = Exception("Test exception") + + ExceptionRecord = namedtuple('ExceptionRecord', ['type', 'value', 'traceback']) + +# Example usage: + exception_record = ExceptionRecord( + type=type(exception).__name__, + value=str(exception), + traceback=mock_format_exception(exception) + ) + self.record['exception'] = exception_record + + + handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) + + attrs = handler._get_attributes(self.record) + + expected_attrs = { + SpanAttributes.CODE_FILEPATH: 'test_file.py', + SpanAttributes.CODE_FUNCTION: 'test_function', + SpanAttributes.CODE_LINENO: 123, + SpanAttributes.EXCEPTION_TYPE: 'Exception', + SpanAttributes.EXCEPTION_MESSAGE: 'Test exception', + SpanAttributes.EXCEPTION_STACKTRACE: 'Exception traceback' + } + + self.assertEqual(attrs[SpanAttributes.EXCEPTION_TYPE], expected_attrs[SpanAttributes.EXCEPTION_TYPE]) + self.assertEqual(attrs[SpanAttributes.EXCEPTION_MESSAGE], expected_attrs[SpanAttributes.EXCEPTION_MESSAGE]) + self.assertEqual(attrs[SpanAttributes.EXCEPTION_STACKTRACE], expected_attrs[SpanAttributes.EXCEPTION_STACKTRACE]) + + @patch('opentelemetry.trace.get_current_span') + def test_translation(self, mock_get_current_span): + mock_get_current_span.return_value = self.current_span + + handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) + + log_record = handler._translate(self.record) + self.assertEqual(log_record.trace_id, self.span_context.trace_id) + self.assertEqual(log_record.span_id, self.span_context.span_id) + self.assertEqual(log_record.trace_flags, self.span_context.trace_flags) + self.assertEqual(log_record.severity_number, _STD_TO_OTEL[self.record["level"].no]) + self.assertEqual(log_record.body, self.record["message"]) + + @patch('opentelemetry._logs.Logger.emit') + @patch('opentelemetry.trace.get_current_span') + def test_sink(self, mock_get_current_span, mock_emit): + mock_get_current_span.return_value = self.current_span + + handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) + + MessageRecord = namedtuple('MessageRecord', ['record']) + message = MessageRecord( + record=self.record + ) + + handler.sink(message) + + diff --git a/out.txt b/out.txt new file mode 100644 index 0000000000..2411b8bb72 --- /dev/null +++ b/out.txt @@ -0,0 +1,223 @@ +test-instrumentation-logging: commands_pre[0]> pip install 'opentelemetry-api@git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-api&subdirectory=opentelemetry-api' +Collecting opentelemetry-api@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-api&subdirectory=opentelemetry-api + Cloning https://github.com/open-telemetry/opentelemetry-python.git (to revision main) to /tmp/pip-install-1knewf_o/opentelemetry-api_282d8d799fb24ab896e904b085f29109 + Resolved https://github.com/open-telemetry/opentelemetry-python.git to commit 634c9f4095166a410350ecc73975b6e6fb360cd4 + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Getting requirements to build wheel: started + Getting requirements to build wheel: finished with status 'done' + Preparing metadata (pyproject.toml): started + Preparing metadata (pyproject.toml): finished with status 'done' +Requirement already satisfied: deprecated>=1.2.6 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-api@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-api&subdirectory=opentelemetry-api) (1.2.14) +Requirement already satisfied: importlib-metadata<=7.1,>=6.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-api@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-api&subdirectory=opentelemetry-api) (6.11.0) +Requirement already satisfied: wrapt<2,>=1.10 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from deprecated>=1.2.6->opentelemetry-api@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-api&subdirectory=opentelemetry-api) (1.16.0) +Requirement already satisfied: zipp>=0.5 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from importlib-metadata<=7.1,>=6.0->opentelemetry-api@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-api&subdirectory=opentelemetry-api) (3.17.0) +test-instrumentation-logging: commands_pre[1]> pip install 'opentelemetry-semantic-conventions@git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions' +Collecting opentelemetry-semantic-conventions@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions + Cloning https://github.com/open-telemetry/opentelemetry-python.git (to revision main) to /tmp/pip-install-cvfubdbv/opentelemetry-semantic-conventions_7e56a1d44143473d9348c6b1f84e3046 + Resolved https://github.com/open-telemetry/opentelemetry-python.git to commit 634c9f4095166a410350ecc73975b6e6fb360cd4 + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Getting requirements to build wheel: started + Getting requirements to build wheel: finished with status 'done' + Preparing metadata (pyproject.toml): started + Preparing metadata (pyproject.toml): finished with status 'done' +test-instrumentation-logging: commands_pre[2]> pip install 'opentelemetry-sdk@git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk' +Collecting opentelemetry-sdk@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk + Cloning https://github.com/open-telemetry/opentelemetry-python.git (to revision main) to /tmp/pip-install-lk1c77a2/opentelemetry-sdk_9c8115670b894894a9c0ecb14b38aaf8 + Resolved https://github.com/open-telemetry/opentelemetry-python.git to commit 634c9f4095166a410350ecc73975b6e6fb360cd4 + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Getting requirements to build wheel: started + Getting requirements to build wheel: finished with status 'done' + Preparing metadata (pyproject.toml): started + Preparing metadata (pyproject.toml): finished with status 'done' +Requirement already satisfied: opentelemetry-api==1.25.0.dev in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-sdk@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk) (1.25.0.dev0) +Requirement already satisfied: opentelemetry-semantic-conventions==0.46b0.dev in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-sdk@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk) (0.46b0.dev0) +Requirement already satisfied: typing-extensions>=3.7.4 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-sdk@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk) (4.9.0) +Requirement already satisfied: deprecated>=1.2.6 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-api==1.25.0.dev->opentelemetry-sdk@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk) (1.2.14) +Requirement already satisfied: importlib-metadata<=7.0,>=6.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-api==1.25.0.dev->opentelemetry-sdk@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk) (6.11.0) +Requirement already satisfied: wrapt<2,>=1.10 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from deprecated>=1.2.6->opentelemetry-api==1.25.0.dev->opentelemetry-sdk@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk) (1.16.0) +Requirement already satisfied: zipp>=0.5 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from importlib-metadata<=7.0,>=6.0->opentelemetry-api==1.25.0.dev->opentelemetry-sdk@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk) (3.17.0) +test-instrumentation-logging: commands_pre[3]> pip install 'opentelemetry-test-utils@git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils' +Collecting opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils + Cloning https://github.com/open-telemetry/opentelemetry-python.git (to revision main) to /tmp/pip-install-jjc0xm3t/opentelemetry-test-utils_b7d3f4b0c60a4857a3f5b4a8c2d32b34 + Resolved https://github.com/open-telemetry/opentelemetry-python.git to commit 634c9f4095166a410350ecc73975b6e6fb360cd4 + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Getting requirements to build wheel: started + Getting requirements to build wheel: finished with status 'done' + Preparing metadata (pyproject.toml): started + Preparing metadata (pyproject.toml): finished with status 'done' +Requirement already satisfied: asgiref~=3.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (3.7.2) +Requirement already satisfied: opentelemetry-api==1.25.0.dev in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (1.25.0.dev0) +Requirement already satisfied: opentelemetry-sdk==1.25.0.dev in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (1.25.0.dev0) +Requirement already satisfied: deprecated>=1.2.6 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-api==1.25.0.dev->opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (1.2.14) +Requirement already satisfied: importlib-metadata<=7.0,>=6.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-api==1.25.0.dev->opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (6.11.0) +Requirement already satisfied: opentelemetry-semantic-conventions==0.46b0.dev in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-sdk==1.25.0.dev->opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (0.46b0.dev0) +Requirement already satisfied: typing-extensions>=3.7.4 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-sdk==1.25.0.dev->opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (4.9.0) +Requirement already satisfied: wrapt<2,>=1.10 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from deprecated>=1.2.6->opentelemetry-api==1.25.0.dev->opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (1.16.0) +Requirement already satisfied: zipp>=0.5 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from importlib-metadata<=7.0,>=6.0->opentelemetry-api==1.25.0.dev->opentelemetry-test-utils@ git+https://github.com/open-telemetry/opentelemetry-python.git@main#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils) (3.17.0) +test-instrumentation-logging: commands_pre[4]> pip install /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/opentelemetry-instrumentation +Processing ./opentelemetry-instrumentation + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Getting requirements to build wheel: started + Getting requirements to build wheel: finished with status 'done' + Preparing metadata (pyproject.toml): started + Preparing metadata (pyproject.toml): finished with status 'done' +Requirement already satisfied: opentelemetry-api~=1.4 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-instrumentation==0.45b0.dev0) (1.25.0.dev0) +Requirement already satisfied: setuptools>=16.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-instrumentation==0.45b0.dev0) (69.1.1) +Requirement already satisfied: wrapt<2.0.0,>=1.0.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-instrumentation==0.45b0.dev0) (1.16.0) +Requirement already satisfied: deprecated>=1.2.6 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-api~=1.4->opentelemetry-instrumentation==0.45b0.dev0) (1.2.14) +Requirement already satisfied: importlib-metadata<=7.0,>=6.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-api~=1.4->opentelemetry-instrumentation==0.45b0.dev0) (6.11.0) +Requirement already satisfied: zipp>=0.5 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from importlib-metadata<=7.0,>=6.0->opentelemetry-api~=1.4->opentelemetry-instrumentation==0.45b0.dev0) (3.17.0) +Building wheels for collected packages: opentelemetry-instrumentation + Building wheel for opentelemetry-instrumentation (pyproject.toml): started + Building wheel for opentelemetry-instrumentation (pyproject.toml): finished with status 'done' + Created wheel for opentelemetry-instrumentation: filename=opentelemetry_instrumentation-0.45b0.dev0-py3-none-any.whl size=28475 sha256=9ba0c4bbb5acb27d366f629b255b37c78443be9e8cc7930d86aad9740e6103e7 + Stored in directory: /home/mcastilh/.cache/pip/wheels/53/1e/17/449f4361c03c07bdeee2403350a3749480b9e78fb831ca633a +Successfully built opentelemetry-instrumentation +Installing collected packages: opentelemetry-instrumentation + Attempting uninstall: opentelemetry-instrumentation + Found existing installation: opentelemetry-instrumentation 0.45b0.dev0 + Uninstalling opentelemetry-instrumentation-0.45b0.dev0: + Successfully uninstalled opentelemetry-instrumentation-0.45b0.dev0 +Successfully installed opentelemetry-instrumentation-0.45b0.dev0 +test-instrumentation-logging: commands_pre[5]> pip install -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt +Obtaining file:///mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/opentelemetry-instrumentation (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 18)) + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Checking if build backend supports build_editable: started + Checking if build backend supports build_editable: finished with status 'done' + Getting requirements to build editable: started + Getting requirements to build editable: finished with status 'done' + Installing backend dependencies: started + Installing backend dependencies: finished with status 'done' + Preparing editable metadata (pyproject.toml): started + Preparing editable metadata (pyproject.toml): finished with status 'done' +Obtaining file:///mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 19)) + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Checking if build backend supports build_editable: started + Checking if build backend supports build_editable: finished with status 'done' + Getting requirements to build editable: started + Getting requirements to build editable: finished with status 'done' + Installing backend dependencies: started + Installing backend dependencies: finished with status 'done' + Preparing editable metadata (pyproject.toml): started + Preparing editable metadata (pyproject.toml): finished with status 'done' +Requirement already satisfied: asgiref==3.7.2 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 1)) (3.7.2) +Requirement already satisfied: attrs==23.2.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 2)) (23.2.0) +Requirement already satisfied: Deprecated==1.2.14 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 3)) (1.2.14) +Requirement already satisfied: importlib-metadata==6.11.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 4)) (6.11.0) +Requirement already satisfied: iniconfig==2.0.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 5)) (2.0.0) +Requirement already satisfied: packaging==23.2 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 6)) (23.2) +Requirement already satisfied: pluggy==1.4.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 7)) (1.4.0) +Requirement already satisfied: py==1.11.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 8)) (1.11.0) +Requirement already satisfied: py-cpuinfo==9.0.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 9)) (9.0.0) +Requirement already satisfied: pytest==7.1.3 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 10)) (7.1.3) +Requirement already satisfied: pytest-benchmark==4.0.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 11)) (4.0.0) +Requirement already satisfied: tomli==2.0.1 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 12)) (2.0.1) +Requirement already satisfied: typing_extensions==4.9.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 13)) (4.9.0) +Requirement already satisfied: wrapt==1.16.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 14)) (1.16.0) +Requirement already satisfied: zipp==3.17.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 15)) (3.17.0) +Requirement already satisfied: structlog==24.1.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 16)) (24.1.0) +Requirement already satisfied: loguru==0.7.2 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from -r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 17)) (0.7.2) +Requirement already satisfied: opentelemetry-api~=1.4 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-instrumentation==0.45b0.dev0->-r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 18)) (1.25.0.dev0) +Requirement already satisfied: setuptools>=16.0 in ./.tox/test-instrumentation-logging/lib/python3.10/site-packages (from opentelemetry-instrumentation==0.45b0.dev0->-r /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt (line 18)) (69.1.1) +Building wheels for collected packages: opentelemetry-instrumentation, opentelemetry-instrumentation-logging + Building editable for opentelemetry-instrumentation (pyproject.toml): started + Building editable for opentelemetry-instrumentation (pyproject.toml): finished with status 'done' + Created wheel for opentelemetry-instrumentation: filename=opentelemetry_instrumentation-0.45b0.dev0-py3-none-any.whl size=7991 sha256=97b3a648568fd6f2e0e9f7b24eb07089b4abcb7e4fef063a4490ec8c7bc7bd79 + Stored in directory: /tmp/pip-ephem-wheel-cache-u7iyg0ou/wheels/53/1e/17/449f4361c03c07bdeee2403350a3749480b9e78fb831ca633a + Building editable for opentelemetry-instrumentation-logging (pyproject.toml): started + Building editable for opentelemetry-instrumentation-logging (pyproject.toml): finished with status 'done' + Created wheel for opentelemetry-instrumentation-logging: filename=opentelemetry_instrumentation_logging-0.45b0.dev0-py3-none-any.whl size=6424 sha256=19f0ee6f119b532ac641035008cfdd1d313741dce3d8f9923ea9d4440fdbd9e0 + Stored in directory: /tmp/pip-ephem-wheel-cache-u7iyg0ou/wheels/88/de/f3/36847e89208d714c671b6408e927acddfcb3b5f0c8ef00f1d7 +Successfully built opentelemetry-instrumentation opentelemetry-instrumentation-logging +Installing collected packages: opentelemetry-instrumentation, opentelemetry-instrumentation-logging + Attempting uninstall: opentelemetry-instrumentation + Found existing installation: opentelemetry-instrumentation 0.45b0.dev0 + Uninstalling opentelemetry-instrumentation-0.45b0.dev0: + Successfully uninstalled opentelemetry-instrumentation-0.45b0.dev0 + Attempting uninstall: opentelemetry-instrumentation-logging + Found existing installation: opentelemetry-instrumentation-logging 0.45b0.dev0 + Uninstalling opentelemetry-instrumentation-logging-0.45b0.dev0: + Successfully uninstalled opentelemetry-instrumentation-logging-0.45b0.dev0 +Successfully installed opentelemetry-instrumentation-0.45b0.dev0 opentelemetry-instrumentation-logging-0.45b0.dev0 +test-instrumentation-logging: commands[0]> pytest /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/tests +============================= test session starts ============================== +platform linux -- Python 3.10.12, pytest-7.1.3, pluggy-1.4.0 -- /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/.tox/test-instrumentation-logging/bin/python +cachedir: .tox/test-instrumentation-logging/.pytest_cache +benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000) +rootdir: /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib, configfile: pytest.ini +plugins: benchmark-4.0.0 +collecting ... collected 17 items + +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoggingInstrumentorProxyTracerProvider::test_trace_context_injection PASSED [ 5%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoggingInstrumentor::test_basic_config_called PASSED [ 11%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoggingInstrumentor::test_custom_format_and_level_api PASSED [ 17%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoggingInstrumentor::test_custom_format_and_level_env PASSED [ 23%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoggingInstrumentor::test_log_hook PASSED [ 29%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoggingInstrumentor::test_trace_context_injection PASSED [ 35%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoggingInstrumentor::test_trace_context_injection_without_span PASSED [ 41%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoggingInstrumentor::test_uninstrumented +-------------------------------- live log call --------------------------------- +WARNING opentelemetry.instrumentation.instrumentor:instrumentor.py:132 Attempting to uninstrument while already uninstrumented +PASSED [ 47%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestStructlogHandler::test_call_method_processes_log_correctly PASSED [ 52%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestStructlogHandler::test_initialization PASSED [ 58%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestStructlogHandler::test_parse_exception PASSED [ 64%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestStructlogHandler::test_parse_timestamp PASSED [ 70%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestStructlogHandler::test_post_process_formats_timestamp PASSED [ 76%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestStructlogHandler::test_pre_process_adds_timestamp PASSED [ 82%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoguruHandler::test_attributes_extraction_without_exception FAILED [ 88%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoguruHandler::test_initialization_with_custom_provider PASSED [ 94%] +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py::TestLoguruHandler::test_initialization_with_default_provider PASSED [100%] + +=================================== FAILURES =================================== +________ TestLoguruHandler.test_attributes_extraction_without_exception ________ + +self = + + def test_attributes_extraction_without_exception(self): + attrs = LoguruHandler._get_attributes(self.record) + expected_attrs = { + SpanAttributes.CODE_FILEPATH: 'test_file.py', + SpanAttributes.CODE_FUNCTION: 'test_function', + SpanAttributes.CODE_LINENO: 123 + } + print("Get attributes: ", attrs) + +> self.assertEqual(attrs, expected_attrs) +E AssertionError: {'time': 1581000000.000123, 'level': , +E - 'line': 123, +E - 'message': 'Test message', +E - 'time': 1581000000.000123} + +instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py:348: AssertionError +----------------------------- Captured stdout call ----------------------------- +Get attributes: {'time': 1581000000.000123, 'level': , 'message': 'Test message', 'file': 'test_file.py', 'function': 'test_function', 'line': 123, 'exception': None, 'code.filepath': 'test_file.py', 'code.function': 'test_function', 'code.lineno': 123} +=============================== warnings summary =============================== +opentelemetry-instrumentation/src/opentelemetry/instrumentation/dependencies.py:4 + /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/opentelemetry-instrumentation/src/opentelemetry/instrumentation/dependencies.py:4: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html + from pkg_resources import ( + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=================== 1 failed, 16 passed, 1 warning in 17.81s =================== +test-instrumentation-logging: exit 1 (51.41 seconds) /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib> pytest /mnt/c/Users/migca/OneDrive/Documents/Purdue_Files/ECE_595_SWE/project/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-logging/tests pid=209543 + test-instrumentation-logging: FAIL code 1 (534.73=setup[0.34]+cmd[60.77,59.86,52.61,56.45,72.26,181.03,51.41] seconds) + evaluation failed :( (534.91 seconds) diff --git a/tox.ini b/tox.ini index 068b5d583e..6d02aca61c 100644 --- a/tox.ini +++ b/tox.ini @@ -313,6 +313,7 @@ setenv = ; i.e: CORE_REPO_SHA=dde62cebffe519c35875af6d06fae053b3be65ec tox -e CORE_REPO_SHA={env:CORE_REPO_SHA:main} CORE_REPO=git+https://github.com/open-telemetry/opentelemetry-python.git@{env:CORE_REPO_SHA} + PYTHONPATH={toxinidir} commands_pre = ; Install without -e to test the actual installation