Skip to content

Commit 5a2e2ee

Browse files
authored
Merge pull request #4 from carolinecgilbert/handler_examples
Handler examples
2 parents 170ba02 + 27bf6d9 commit 5a2e2ee

File tree

11 files changed

+614
-39
lines changed

11 files changed

+614
-39
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# OpenTelemetry Python `loguru` Handler Example with Docker
2+
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.
3+
4+
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.
5+
6+
## Prerequisites
7+
Python 3
8+
9+
## Installation
10+
Prior to building the example application, set up the directory and virtual environment:
11+
```
12+
mkdir otel-loguru-example
13+
cd otel-loguru-example
14+
python3 -m venv venv
15+
source ./venv/bin/activate
16+
```
17+
18+
After activating the virtual environment `venv`, install flask and loguru.
19+
```
20+
pip install flask
21+
pip install loguru
22+
pip install opentelemetry-exporter-otlp
23+
```
24+
25+
### Create and Launch HTTP Server
26+
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.
27+
28+
Notice the importance of the following imports for using the loguru handler: `import loguru` and `from handlers.opentelemetry_loguru.src.exporter import LoguruHandler`.
29+
30+
```
31+
from random import randint
32+
from flask import Flask, request
33+
from loguru import logger as loguru_logger
34+
import sys
35+
sys.path.insert(0, '../../..')
36+
from handlers.opentelemetry_loguru.src.exporter import LoguruHandler
37+
38+
from opentelemetry._logs import set_logger_provider
39+
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
40+
OTLPLogExporter,
41+
)
42+
from opentelemetry.sdk._logs import LoggerProvider
43+
from opentelemetry.sdk.resources import Resource
44+
45+
46+
47+
logger_provider = LoggerProvider(
48+
resource=Resource.create(
49+
{
50+
"service.name": "shoppingcart",
51+
"service.instance.id": "instance-12",
52+
}
53+
),
54+
)
55+
set_logger_provider(logger_provider)
56+
57+
# Replace the standard logging configuration with Loguru
58+
loguru_handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True))
59+
loguru_logger.add(loguru_handler.sink) # Add LoguruHandler to the logger
60+
61+
app = Flask(__name__)
62+
63+
@app.route("/rolldice")
64+
def roll_dice():
65+
player = request.args.get('player', default=None, type=str)
66+
result = str(roll())
67+
if player:
68+
loguru_logger.warning("Player is rolling the dice: num")
69+
else:
70+
loguru_logger.warning("Anonymous player is rolling the dice: num")
71+
return result
72+
73+
74+
def roll():
75+
return randint(1, 6)
76+
77+
```
78+
79+
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.
80+
81+
```
82+
flask run -p 8080
83+
```
84+
85+
However, do not be alarmed if you receive these errors since Docker is not yet set up to export the logs:
86+
```
87+
Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 1s.
88+
Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 2s.
89+
Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 4s.
90+
...
91+
```
92+
93+
## Run with Docker
94+
95+
To serve the application on Docker, first create the `otel-collector-config.yaml` file locally in the application's repository.
96+
```
97+
# otel-collector-config.yaml
98+
receivers:
99+
otlp:
100+
protocols:
101+
grpc:
102+
103+
processors:
104+
batch:
105+
106+
exporters:
107+
logging:
108+
verbosity: detailed
109+
110+
service:
111+
pipelines:
112+
logs:
113+
receivers: [otlp]
114+
processors: [batch]
115+
exporters: [logging]
116+
```
117+
118+
Next, start the Docker container:
119+
```
120+
docker run \
121+
-p 4317:4317 \
122+
-v $(pwd)/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml \
123+
otel/opentelemetry-collector-contrib:latest
124+
```
125+
126+
And lastly, run the basic application with flask:
127+
```
128+
flask run -p 8080
129+
```
130+
131+
Here is some example output:
132+
```
133+
134+
```
135+
136+
137+
## Contributors
138+
Caroline Gilbert: [carolincgilbert](https://github.com/carolinecgilbert)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from random import randint
2+
from flask import Flask, request
3+
from loguru import logger as loguru_logger
4+
import sys
5+
sys.path.insert(0, '../../..')
6+
from handlers.opentelemetry_loguru.src.exporter import LoguruHandler
7+
8+
from opentelemetry._logs import set_logger_provider
9+
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
10+
OTLPLogExporter,
11+
)
12+
from opentelemetry.sdk._logs import LoggerProvider
13+
from opentelemetry.sdk.resources import Resource
14+
15+
16+
17+
18+
# Replace the standard logging configuration with Loguru
19+
loguru_handler = LoguruHandler(service_name="flask-loguru-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True))
20+
loguru_logger.add(loguru_handler.sink) # Add LoguruHandler to the logger
21+
22+
23+
app = Flask(__name__)
24+
25+
@app.route("/rolldice")
26+
def roll_dice():
27+
player = request.args.get('player', default=None, type=str)
28+
result = str(roll())
29+
if player:
30+
loguru_logger.warning(f"Player {player} is rolling the dice: {result}")
31+
else:
32+
loguru_logger.warning(f"Anonymous player is rolling the dice: {result}")
33+
return result
34+
35+
36+
def roll():
37+
return randint(1, 6)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# otel-collector-config.yaml
2+
receivers:
3+
otlp:
4+
protocols:
5+
grpc:
6+
7+
processors:
8+
batch:
9+
10+
exporters:
11+
logging:
12+
verbosity: detailed
13+
14+
service:
15+
pipelines:
16+
logs:
17+
receivers: [otlp]
18+
processors: [batch]
19+
exporters: [logging]
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# OpenTelemetry Python `structlog` Handler Example with Docker
2+
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.
3+
4+
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.
5+
6+
## Prerequisites
7+
Python 3
8+
9+
## Installation
10+
Prior to building the example application, set up the directory and virtual environment:
11+
```
12+
mkdir otel-structlog-example
13+
cd otel-structlog-example
14+
python3 -m venv venv
15+
source ./venv/bin/activate
16+
```
17+
18+
After activating the virtual environment `venv`, install flask and structlog.
19+
```
20+
pip install flask
21+
pip install structlog
22+
pip install opentelemetry-exporter-otlp
23+
```
24+
25+
### Create and Launch HTTP Server
26+
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.
27+
28+
Notice the importance of the following imports for using the structlog handler: `import structlog` and `from handlers.opentelemetry_structlog.src.exporter import StructlogHandler`.
29+
30+
```
31+
from random import randint
32+
from flask import Flask, request
33+
import structlog
34+
import sys
35+
sys.path.insert(0, '../../..')
36+
from handlers.opentelemetry_structlog.src.exporter import StructlogHandler
37+
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
38+
OTLPLogExporter,
39+
)
40+
from opentelemetry.sdk._logs import LoggerProvider
41+
from opentelemetry.sdk.resources import Resource
42+
from opentelemetry._logs import set_logger_provider
43+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
44+
from opentelemetry.sdk.resources import Resource
45+
46+
logger_provider = LoggerProvider(
47+
resource=Resource.create(
48+
{
49+
"service.name": "shoppingcart",
50+
"service.instance.id": "instance-12",
51+
}
52+
),
53+
)
54+
set_logger_provider(logger_provider)
55+
56+
# Replace the standard logging configuration with Loguru
57+
structlog_handler = StructlogHandler(service_name="flask-structlog-demo", server_hostname="instance-1", exporter=OTLPLogExporter(insecure=True))
58+
structlog_handler._logger_provider = logger_provider
59+
structlog_logger = structlog.wrap_logger(structlog.get_logger(), processors=[structlog_handler]) # Add StructlogHandler to the logger
60+
61+
app = Flask(__name__)
62+
63+
@app.route("/rolldice")
64+
def roll_dice():
65+
player = request.args.get('player', default=None, type=str)
66+
result = str(roll())
67+
if player:
68+
structlog_logger.warning("Player %s is rolling the dice: %s", player, result, level="warning")
69+
else:
70+
structlog_logger.warning("Anonymous player is rolling the dice: %s", result, level="warning")
71+
return result
72+
73+
74+
def roll():
75+
return randint(1, 6)
76+
```
77+
78+
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.
79+
80+
```
81+
flask run -p 8080
82+
```
83+
84+
However, do not be alarmed if you receive these errors since Docker is not yet set up to export the logs:
85+
```
86+
Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 1s.
87+
Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 2s.
88+
Transient error StatusCode.UNAVAILABLE encountered while exporting logs to localhost:4317, retrying in 4s.
89+
...
90+
```
91+
92+
## Run with Docker
93+
94+
To serve the application on Docker, first create the `otel-collector-config.yaml` file locally in the application's repository.
95+
```
96+
# otel-collector-config.yaml
97+
receivers:
98+
otlp:
99+
protocols:
100+
grpc:
101+
102+
processors:
103+
batch:
104+
105+
exporters:
106+
logging:
107+
verbosity: detailed
108+
109+
service:
110+
pipelines:
111+
logs:
112+
receivers: [otlp]
113+
processors: [batch]
114+
exporters: [logging]
115+
```
116+
117+
Next, start the Docker container:
118+
```
119+
docker run \
120+
-p 4317:4317 \
121+
-v $(pwd)/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml \
122+
otel/opentelemetry-collector-contrib:latest
123+
```
124+
125+
And lastly, run the basic application with flask:
126+
```
127+
flask run -p 8080
128+
```
129+
130+
Here is some example output:
131+
```
132+
* Debug mode: off
133+
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
134+
* Running on http://127.0.0.1:8080
135+
Press CTRL+C to quit
136+
2024-04-28 23:15:22 [warning ] Anonymous player is rolling the dice: 1
137+
127.0.0.1 - - [28/Apr/2024 23:15:22] "GET /rolldice HTTP/1.1" 200 -
138+
2024-04-28 23:15:27 [warning ] Anonymous player is rolling the dice: 6
139+
127.0.0.1 - - [28/Apr/2024 23:15:27] "GET /rolldice HTTP/1.1" 200 -
140+
2024-04-28 23:15:28 [warning ] Anonymous player is rolling the dice: 3
141+
127.0.0.1 - - [28/Apr/2024 23:15:28] "GET /rolldice HTTP/1.1" 200 -
142+
2024-04-28 23:15:29 [warning ] Anonymous player is rolling the dice: 4
143+
127.0.0.1 - - [28/Apr/2024 23:15:29] "GET /rolldice HTTP/1.1" 200 -
144+
2024-04-28 23:15:29 [warning ] Anonymous player is rolling the dice: 1
145+
127.0.0.1 - - [28/Apr/2024 23:15:29] "GET /rolldice HTTP/1.1" 200 -
146+
2024-04-28 23:15:30 [warning ] Anonymous player is rolling the dice: 2
147+
127.0.0.1 - - [28/Apr/2024 23:15:30] "GET /rolldice HTTP/1.1" 200 -
148+
2024-04-28 23:15:31 [warning ] Anonymous player is rolling the dice: 3
149+
127.0.0.1 - - [28/Apr/2024 23:15:31] "GET /rolldice HTTP/1.1" 200 -
150+
2024-04-28 23:16:14 [warning ] Anonymous player is rolling the dice: 4
151+
127.0.0.1 - - [28/Apr/2024 23:16:14] "GET /rolldice HTTP/1.1" 200 -
152+
```
153+
154+
155+
## Contributors
156+
Caroline Gilbert: [carolincgilbert](https://github.com/carolinecgilbert)

0 commit comments

Comments
 (0)