Skip to content

Commit e506d59

Browse files
committed
feat: Add logging to production
1 parent 2143eee commit e506d59

File tree

7 files changed

+118
-15
lines changed

7 files changed

+118
-15
lines changed

.github/workflows/deploy.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ jobs:
4444
if [ "${{ inputs.target_tag }}" = "prod-latest" ]; then
4545
scp -o StrictHostKeyChecking=no docker-compose.prod.yml ${{ secrets.DEPLOYMENT_SSH_USER }}@${{ secrets.DEPLOYMENT_SERVER }}:/opt/rcb-deployments/${{ vars.PROJECT_NAME }}/docker-compose.override.yml
4646
scp -o StrictHostKeyChecking=no .env ${{ secrets.DEPLOYMENT_SSH_USER }}@${{ secrets.DEPLOYMENT_SERVER }}:/opt/rcb-deployments/${{ vars.PROJECT_NAME }}/.env.prod
47-
echo "${{ secrets.HONEYCOMB_CONFIG }}" > honeycomb.toml
48-
scp -o StrictHostKeyChecking=no honeycomb.toml ${{ secrets.DEPLOYMENT_SSH_USER }}@${{ secrets.DEPLOYMENT_SERVER }}:/opt/rcb-deployments/${{ vars.PROJECT_NAME }}/honeycomb.toml
4947
else
5048
scp -o StrictHostKeyChecking=no docker-compose.dev.yml ${{ secrets.DEPLOYMENT_SSH_USER }}@${{ secrets.DEPLOYMENT_SERVER }}:/opt/rcb-deployments/${{ vars.PROJECT_NAME }}/docker-compose.override.yml
5149
scp -o StrictHostKeyChecking=no .env ${{ secrets.DEPLOYMENT_SSH_USER }}@${{ secrets.DEPLOYMENT_SERVER }}:/opt/rcb-deployments/${{ vars.PROJECT_NAME }}/.env.development
@@ -64,8 +62,16 @@ jobs:
6462
IMAGE="ghcr.io/$OWNER/$REPO:${{ inputs.target_tag }}"
6563
echo "Deploying $IMAGE to VPS..."
6664
ssh -o StrictHostKeyChecking=no ${{ secrets.DEPLOYMENT_SSH_USER }}@${{ secrets.DEPLOYMENT_SERVER }} "\
65+
66+
# exports general variables
6767
export PROJECT_NAME='${{ vars.PROJECT_NAME }}' && \
6868
export GHCR_USER='${{ secrets.MACHINE_USER }}' && \
6969
export GHCR_PAT='${{ secrets.MACHINE_PAT }}' && \
7070
export APPLICATION_IMAGE='$IMAGE' && \
71+
72+
# applies only to production for logging
73+
export HONEYCOMB_API_KEY='${{ secrets.HONEYCOMB_API_KEY }}' && \
74+
export HONEYCOMB_DATASET='${{ secrets.HONEYCOMB_DATASET }}' && \
75+
76+
# runs deploy script
7177
/opt/rcb-deployments/${{ vars.PROJECT_NAME }}/deploy.sh $IMAGE"

docker-compose.override.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,18 @@ services:
77
env_file: .env.local
88
mysql:
99
env_file: .env.local
10+
11+
# uncomment for testing locally, but you'll need your own honeycomb account and creds
12+
# otel-collector:
13+
# image: otel/opentelemetry-collector:latest
14+
# container_name: rcb-discord-bot-otel-collector
15+
# environment:
16+
# HONEYCOMB_API_KEY: ${HONEYCOMB_API_KEY}
17+
# HONEYCOMB_DATASET: ${HONEYCOMB_DATASET}
18+
# volumes:
19+
# - ./otel-config.yaml:/etc/otel/config.yaml:ro
20+
# command: ["--config", "/etc/otel/config.yaml"]
21+
# ports:
22+
# - "4317:4317"
23+
# networks:
24+
# - app-network

docker-compose.prod.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ services:
88
env_file: .env.prod
99

1010
# add logging in production environment
11-
honeycomb-agent:
12-
image: honeycombio/honeycomb-agent:latest
13-
container_name: honeycomb-agent
11+
otel-collector:
12+
image: otel/opentelemetry-collector:latest
13+
container_name: rcb-discord-bot-otel-collector
14+
environment:
15+
HONEYCOMB_API_KEY: ${HONEYCOMB_API_KEY}
16+
HONEYCOMB_DATASET: ${HONEYCOMB_DATASET}
1417
volumes:
15-
- ./honeycomb.toml:/etc/honeycomb/honeycomb.toml:ro
18+
- ./otel-config.yaml:/etc/otel/config.yaml:ro
19+
command: ["--config", "/etc/otel/config.yaml"]
1620
ports:
17-
- "514:514"
21+
- "4317:4317"
1822
networks:
1923
- app-network

otel-config.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
receivers:
2+
otlp:
3+
protocols:
4+
grpc:
5+
endpoint: 0.0.0.0:4317
6+
7+
exporters:
8+
otlp:
9+
endpoint: api.honeycomb.io:443
10+
headers:
11+
x-honeycomb-team: ${HONEYCOMB_API_KEY}
12+
x-honeycomb-dataset: ${HONEYCOMB_DATASET}
13+
compression: gzip
14+
15+
processors:
16+
batch:
17+
18+
service:
19+
pipelines:
20+
logs:
21+
receivers: [otlp]
22+
processors: [batch]
23+
exporters: [otlp]
24+
traces:
25+
receivers: [otlp]
26+
processors: [batch]
27+
exporters: [otlp]

requirements.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ httpx==0.28.1
1717
hyperlink==21.0.0
1818
idna==3.10
1919
multidict==6.1.0
20+
opentelemetry-api==1.31.1
21+
opentelemetry-exporter-otlp==1.31.1
22+
opentelemetry-exporter-otlp-proto-common==1.31.1
23+
opentelemetry-exporter-otlp-proto-grpc==1.31.1
24+
opentelemetry-exporter-otlp-proto-http==1.31.1
25+
opentelemetry-proto==1.31.1
26+
opentelemetry-sdk==1.31.1
27+
opentelemetry-semantic-conventions==0.52b1
2028
packaging==24.2
2129
pydantic==2.10.6
2230
pydantic-settings==2.8.1

src/bot/utils/console_logger.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
"""
2-
Logger configuration module for console output.
2+
Logger configuration module for console output with OTEL integration.
33
4-
Initializes and configures a console logger using values from the
5-
logging config. Sets the log level, output format, and ensures no
6-
duplicate handlers are attached.
4+
This module sets up the standard console logger and attaches the OTEL handler
5+
(from otel_handler.py) so that logs are both printed to the console and sent
6+
to the OpenTelemetry Collector in a structured format.
77
"""
88

99
import logging
1010

1111
from bot.config.logging import logging_config
12+
from bot.utils.otel_handler import otel_handler
1213

1314
# creates logger
1415
console_logger = logging.getLogger(logging_config.logger_prefix)
@@ -18,10 +19,10 @@
1819
console_logger.setLevel(log_level_num)
1920

2021
# formats logger output
21-
handler = logging.StreamHandler()
2222
formatter = logging.Formatter(logging_config.logger_format)
23-
handler.setFormatter(formatter)
24-
if not console_logger.hasHandlers():
25-
console_logger.addHandler(handler)
23+
24+
# sets the formatter
25+
otel_handler.setFormatter(formatter)
26+
console_logger.addHandler(otel_handler)
2627

2728
console_logger.info("✅ Logger is successfully configured.")

src/bot/utils/otel_handler.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
OpenTelemetry Logging Handler Module.
3+
4+
Sets up the OpenTelemetry LoggerProvider, exporter, and processor.
5+
Defines OTELHandler for Python's logging module that forwards logs
6+
to the OTEL Collector in structured format.
7+
8+
The code snippet below is taken from the otel python example here:
9+
https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples/logs
10+
"""
11+
12+
import logging
13+
14+
from opentelemetry import trace
15+
from opentelemetry._logs import set_logger_provider
16+
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
17+
OTLPLogExporter,
18+
)
19+
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
20+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
21+
from opentelemetry.sdk.resources import Resource
22+
from opentelemetry.sdk.trace import TracerProvider
23+
from opentelemetry.sdk.trace.export import (
24+
BatchSpanProcessor,
25+
ConsoleSpanExporter,
26+
)
27+
28+
trace.set_tracer_provider(TracerProvider())
29+
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
30+
31+
logger_provider = LoggerProvider(
32+
resource=Resource.create(
33+
{
34+
"service.name": "rcb-discord-bot",
35+
}
36+
),
37+
)
38+
set_logger_provider(logger_provider)
39+
40+
exporter = OTLPLogExporter(endpoint="otel-collector:4317", insecure=True)
41+
logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
42+
otel_handler = LoggingHandler(level=logging.NOTSET, logger_provider=logger_provider)

0 commit comments

Comments
 (0)