From a097455030ec8f08169ae3715c1e114c91bf2960 Mon Sep 17 00:00:00 2001 From: Caroline Gilbert Date: Wed, 1 May 2024 13:26:43 -0400 Subject: [PATCH 1/5] CG: added structlog example --- .../handlers/opentelemetry_loguru/README.md | 138 ++++++++++++++++ examples/handlers/opentelemetry_loguru/app.py | 46 ++++++ .../otel-collector-config.yaml | 19 +++ .../opentelemetry_structlog/README.md | 156 ++++++++++++++++++ .../handlers/opentelemetry_structlog/app.py | 47 ++++++ .../otel-collector-config.yaml | 19 +++ 6 files changed, 425 insertions(+) create mode 100644 examples/handlers/opentelemetry_loguru/README.md create mode 100644 examples/handlers/opentelemetry_loguru/app.py create mode 100644 examples/handlers/opentelemetry_loguru/otel-collector-config.yaml create mode 100644 examples/handlers/opentelemetry_structlog/README.md create mode 100644 examples/handlers/opentelemetry_structlog/app.py create mode 100644 examples/handlers/opentelemetry_structlog/otel-collector-config.yaml 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..7f54ba6d9e --- /dev/null +++ b/examples/handlers/opentelemetry_loguru/app.py @@ -0,0 +1,46 @@ +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) + 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 From e08356566435b7ee0014b57a45ad5ce9bf7bc9c4 Mon Sep 17 00:00:00 2001 From: Caroline Gilbert Date: Fri, 3 May 2024 00:56:19 -0400 Subject: [PATCH 2/5] CG: loguru finalized with documentation --- handlers/opentelemetry_loguru/src/README.md | 53 ++++++++++++ handlers/opentelemetry_loguru/src/exporter.py | 42 +++++++--- .../opentelemetry_structlog/src/README.md | 60 ++++++++++++++ .../test-requirements.txt | 1 + .../tests/test_logging.py | 82 ++++++++++++------- 5 files changed, 199 insertions(+), 39 deletions(-) create mode 100644 handlers/opentelemetry_loguru/src/README.md create mode 100644 handlers/opentelemetry_structlog/src/README.md 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/exporter.py b/handlers/opentelemetry_loguru/src/exporter.py index 92014ce65f..d18eea28b3 100644 --- a/handlers/opentelemetry_loguru/src/exporter.py +++ b/handlers/opentelemetry_loguru/src/exporter.py @@ -94,7 +94,7 @@ 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`: @@ -102,22 +102,40 @@ class LoguruHandler: def __init__( self, - logger_provider=None, + service_name: str, + server_hostname: str, + exporter: LogExporter, ) -> None: - - self._logger_provider = logger_provider or get_logger_provider() - self._logger = get_logger( - __name__, logger_provider=self._logger_provider + 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()} + 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'] #This includes file and path -> (file, path) + 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: @@ -138,11 +156,13 @@ def _loguru_to_otel(self, levelno: int) -> SeverityNumber: return _STD_TO_OTEL[levelno] + + def _translate(self, record) -> LogRecord: #Timestamp - timestamp = record["time"] + timestamp = int((record["time"].timestamp()) * 1e9) #Observed timestamp observedTimestamp = time_ns() @@ -179,6 +199,6 @@ def _translate(self, record) -> LogRecord: ) def sink(self, record) -> None: - - self._logger.emit(self._translate(record)) + print("\n BALLIN HERE\n") + self._logger.emit(self._translate(record.record)) \ No newline at end of file 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/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt index 1f015a3373..ad7b238d7c 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt @@ -15,5 +15,6 @@ 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 cfcf581945..0f5c6f23ec 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py +++ b/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging.py @@ -27,7 +27,9 @@ 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 @@ -389,41 +391,58 @@ def test_trace_context_propogation(self): 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": 1581000000.000123, + "time": timeRec, "level": MagicMock(name="ERROR", no=40), "message": "Test message", - "file": "test_file.py", + "file": file_record, + "process": process_record, + "thread": thread_record, "function": "test_function", "line": 123, "exception": None } - # self.span_context = SpanContext( - # trace_id=1234, - # span_id=5678, - # trace_flags=TraceFlags(1), - # is_remote=False - # ) + self.span_context = get_current_span().get_span_context() self.current_span = MagicMock() - self.current_span.get_span_context.return_value = self.span_context + self.current_span.get_span_context.return_value = self.span_context - def test_initialization_with_default_provider(self): - handler = LoguruHandler() - self.assertEqual(handler._logger_provider, self.default_provider) + def test_attributes_extraction_without_exception(self): + handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True)) - def test_initialization_with_custom_provider(self): - handler = LoguruHandler(logger_provider=self.custom_provider) - self.assertEqual(handler._logger_provider, self.custom_provider) + attrs = handler._get_attributes(self.record) - 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', @@ -448,11 +467,11 @@ def test_attributes_extraction_with_exception(self, mock_format_exception): traceback=mock_format_exception(exception) ) self.record['exception'] = exception_record - # self.record['exception'].type = type(exception).__name__ - # self.record['exception'].value = str(exception) - # self.record['exception'].traceback = mock_format_exception(exception) + - attrs = LoguruHandler()._get_attributes(self.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', @@ -470,7 +489,9 @@ def test_attributes_extraction_with_exception(self, mock_format_exception): @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(logger_provider=self.custom_provider) + + 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) @@ -482,9 +503,14 @@ def test_translation(self, mock_get_current_span): @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(logger_provider=self.custom_provider) - handler.sink(self.record) - #mock_emit.assert_called_once() - handler._logger.emit.assert_called_once() + + 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) From 4561989ba47824f2b5374c0b79de9c4459f1f581 Mon Sep 17 00:00:00 2001 From: Caroline Gilbert Date: Fri, 3 May 2024 01:22:11 -0400 Subject: [PATCH 3/5] removed print statement --- handlers/opentelemetry_loguru/src/exporter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/handlers/opentelemetry_loguru/src/exporter.py b/handlers/opentelemetry_loguru/src/exporter.py index d18eea28b3..4b3e475ab5 100644 --- a/handlers/opentelemetry_loguru/src/exporter.py +++ b/handlers/opentelemetry_loguru/src/exporter.py @@ -199,6 +199,5 @@ def _translate(self, record) -> LogRecord: ) def sink(self, record) -> None: - print("\n BALLIN HERE\n") self._logger.emit(self._translate(record.record)) \ No newline at end of file From 37ef8f784bcc593465b514133b0dadc916d53c24 Mon Sep 17 00:00:00 2001 From: Caroline Gilbert Date: Fri, 3 May 2024 01:23:37 -0400 Subject: [PATCH 4/5] updated app --- examples/handlers/opentelemetry_loguru/app.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/examples/handlers/opentelemetry_loguru/app.py b/examples/handlers/opentelemetry_loguru/app.py index 7f54ba6d9e..bff776c5fe 100644 --- a/examples/handlers/opentelemetry_loguru/app.py +++ b/examples/handlers/opentelemetry_loguru/app.py @@ -14,20 +14,12 @@ -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") @@ -43,4 +35,3 @@ def roll_dice(): def roll(): return randint(1, 6) - From 27bf6d93b4e478e98bc49f08014d38150acfa7f6 Mon Sep 17 00:00:00 2001 From: Caroline Gilbert Date: Fri, 3 May 2024 01:48:15 -0400 Subject: [PATCH 5/5] updated loguru app --- examples/handlers/opentelemetry_loguru/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/handlers/opentelemetry_loguru/app.py b/examples/handlers/opentelemetry_loguru/app.py index bff776c5fe..2a23dab402 100644 --- a/examples/handlers/opentelemetry_loguru/app.py +++ b/examples/handlers/opentelemetry_loguru/app.py @@ -27,9 +27,9 @@ 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") + loguru_logger.warning(f"Player {player} is rolling the dice: {result}") else: - loguru_logger.warning("Anonymous player is rolling the dice: num") + loguru_logger.warning(f"Anonymous player is rolling the dice: {result}") return result