- 
                Notifications
    You must be signed in to change notification settings 
- Fork 803
Structlog and Loguru Handlers with Examples #2492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Closed
      
      
    
  
     Closed
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            26 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      1133701
              
                added handlers dir
              
              
                carolinecgilbert 4e18784
              
                Able to import structlog handler
              
              
                MigCast9 21e5c62
              
                Minor changes: removed useless stuff
              
              
                MigCast9 2e22c2e
              
                Parth Doshi - Added Test Cases
              
              
                doshi36 12a4035
              
                all structlog tests pass except test_call_method_processes_log_correc…
              
              
                carolinecgilbert 097be29
              
                Caroline Gilbert: fixed tests - all pass
              
              
                carolinecgilbert 8bf5af4
              
                Caroline Gilbert: fixed test name
              
              
                carolinecgilbert 8515ee0
              
                Merge pull request #1 from carolinecgilbert/structlog-testing
              
              
                doshi36 39cde55
              
                loguru handler
              
              
                MigCast9 3a64898
              
                Buggy Test Cases
              
              
                doshi36 45aa0a3
              
                Caroline Gilbert: new structlog tests pass
              
              
                carolinecgilbert 96548d6
              
                Merge pull request #2 from carolinecgilbert/structlog-testing
              
              
                MigCast9 804b579
              
                All but one tests passing
              
              
                MigCast9 2a84a08
              
                Final test not passing for loguru
              
              
                MigCast9 952c003
              
                CG: loguru tests pass 100%
              
              
                carolinecgilbert 21e411e
              
                Merge branch 'main' into loguru_handler_WORKING
              
              
                carolinecgilbert 3d3ae4f
              
                Merge pull request #3 from carolinecgilbert/loguru_handler_WORKING
              
              
                MigCast9 170ba02
              
                removed old import file
              
              
                carolinecgilbert a097455
              
                CG: added structlog example
              
              
                carolinecgilbert e083565
              
                CG: loguru finalized with documentation
              
              
                carolinecgilbert 4561989
              
                removed print statement
              
              
                carolinecgilbert 37ef8f7
              
                updated app
              
              
                carolinecgilbert 27bf6d9
              
                updated loguru app
              
              
                carolinecgilbert 5a2e2ee
              
                Merge pull request #4 from carolinecgilbert/handler_examples
              
              
                MigCast9 32881ce
              
                Update README.md
              
              
                MigCast9 1c92c51
              
                Update README.md
              
              
                MigCast9 File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -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) | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -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) | 
        
          
  
    
      
          
            19 changes: 19 additions & 0 deletions
          
          19 
        
  examples/handlers/opentelemetry_loguru/otel-collector-config.yaml
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -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] | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -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) | 
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not look generic enough