This assumes you have Flask up and running in a Docker container. If not, check out my article Flask - Install uWSGI Nginx Flask on Docker.
Let's say your default route prints Hello World and returns "lookin good".
from flask import Blueprint
blueprint = Blueprint('views', __name__)
@blueprint.route('/')
def home():
print("Hello World")
return "lookin good"
Each time your default route is requested, something like this should appear in your docker logs. Let's customize this, so we can have better control over the logging.
~]$ sudo docker logs recipes --tail=10
172.31.19.227 - - [18/May/2024:03:24:58 +0000] "GET /home HTTP/1.1" 200 34603 "https://www.example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/111.0.5563.146 Safari/537.36 "44.201.69.85"
[pid: 13|app: 0|req: 2304/3753] 172.31.19.227 () {50 vars in 1008 bytes} [Sat May 18 03:24:58 2024] GET /home => generated 34603 bytes in 46 msecs via sendfile() (HTTP/1.1 200) 7 headers in 276 bytes (0 switches on core 0)
Hello World
nginx in the Docker container should be configured to
- send error logs to /var/log/nginx/error.log
- send access logs to /var/log/nginx/access.log
]$ sudo docker exec my-container cat /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
daemon off;
And events in the access.log get redirected to /dev/stdout and events in the error.log get redirected to /dev/stderr.
~]$ sudo docker exec my-container ls -l /var/log/nginx/
lrwxrwxrwx. 1 root root 11 Sep 4 2023 /var/log/nginx/access.log -> /dev/stdout
lrwxrwxrwx. 1 root root 11 Sep 4 2023 /var/log/nginx/error.log -> /dev/stderr
The supervisord.conf file is also configured to redirect uwsgi and nginx logs to /dev/stdout and /dev/stderr.
~]$ sudo docker exec my-container cat /etc/supervisor/conf.d/supervisord.conf
[supervisord]
nodaemon=true
[program:uwsgi]
command=/usr/local/bin/uwsgi --ini /etc/uwsgi/uwsgi.ini
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
startsecs = 0
autorestart=false
[program:nginx]
command=/usr/sbin/nginx
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
# Graceful stop, see http://nginx.org/en/docs/control.html
stopsignal=QUIT
startsecs = 0
autorestart=false
[eventlistener:quit_on_failure]
events=PROCESS_STATE_STOPPED,PROCESS_STATE_EXITED,PROCESS_STATE_FATAL
command=/etc/supervisor/stop-supervisor.sh
Let's say you want the print statements in your Flask app to go to stdout so that they appear in the docker logs command and all other logs should be written to a log file and NOT redirected to stdout or stderr.
Let's take a copy of /etc/supervisor/conf.d/supervisord.conf file in your flask uwsgi nginx container and create a copy of the supervisord.conf file on your Docker system and updated the supervisord.conf file with the following.
- --disable-logging to disable request logging (for example, the logs beginning with an IP address to GET a resource)
- --log-master so that print statements go to stdout instead of stderr
- uwsgi stderr gets appended to /var/log/supervisor/uwsgi_stderr.log
- nginx stdout gets appended to /var/log/supervisor/nginx_stdout.log
- nginx stderr gets appended to /var/log/supervisor/nginx_stderr.log
[supervisord]
nodaemon=true
user=root
[program:uwsgi]
command=/usr/local/bin/uwsgi --ini /etc/uwsgi/uwsgi.ini --disable-logging --log-master
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/var/log/supervisor/uwsgi_stderr.log
stderr_logfile_maxbytes=0
startsecs = 0
autorestart=false
[program:nginx]
command=/usr/sbin/nginx
stdout_logfile=/var/log/supervisor/nginx_stdout.log
stdout_logfile_maxbytes=0
stderr_logfile=/var/log/supervisor/nginx_stderr.log
stderr_logfile_maxbytes=0
# Graceful stop, see http://nginx.org/en/docs/control.html
stopsignal=QUIT
startsecs = 0
autorestart=false
[eventlistener:quit_on_failure]
events=PROCESS_STATE_STOPPED,PROCESS_STATE_EXITED,PROCESS_STATE_FATAL
command=/etc/supervisor/stop-supervisor.sh
Recall that nginx is configured to redirect /var/log/nginx/access.log to /dev/stdout and to redirect /var/log/nginx/error.log to /dev/stderr. Let's create a Dockerfile that includes RUN unlink /var/log/nginx/access.log and RUN unlink /var/log/nginx/error.log so that the symbolic link from the access.log to stdout is removed and the symbolic link from the error.log to stderr is removed. Events will still be appened to the access.log and error.log files in the container.
FROM tiangolo/uwsgi-nginx-flask:python3.11
RUN apt-get update -y
RUN pip install --upgrade pip
COPY ./requirements.txt /usr/requirements.txt
RUN pip install -r /usr/requirements.txt
RUN unlink /var/log/nginx/access.log
RUN unlink /var/log/nginx/error.log
For example, perhaps an image iagged uwsgi-nginx-flask:unlinked.
~]$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
uwsgi-nginx-flask unlinked 7e238450853c 32 hours ago 1.13GB
And create and start the container using the unlinked image and the modified supervisord.conf file. For more details on this, check out my article Flask - Install uWSGI Nginx Flask on Docker.
sudo docker run \
--detach \
--name my-container
--volume /path/to/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf \
--publish 0.0.0.0:80:80 \
uwsgi-nginx-flask:unlinked
Now, events in the nginx access.log and error.log should no longer be redirected to stdout and stderr.
~]$ sudo docker exec my-container ls -l /var/log/nginx
-rw-r--r--. 1 root root 0 May 20 03:38 access.log
-rw-r--r--. 1 root root 0 May 20 03:38 error.log
Likewise, the log files that were defined in supervisord.conf should now be at /var/log/supervisor and it's not uncommon for there to be events in the uwsgi error.log.
~]$ sudo docker exec my-container ls -l /var/log/supervisor
total 16
-rw-r--r--. 1 root root 0 May 21 11:52 nginx_stderr.log
-rw-r--r--. 1 root root 0 May 21 11:52 nginx_stdout.log
-rw-------. 1 root root 0 May 21 11:52 quit_on_failure-stderr---supervisor-xhpmtwy7.log
-rw-------. 1 root root 6 May 21 11:52 quit_on_failure-stdout---supervisor-5dk8y_m3.log
-rw-r--r--. 1 root root 1130 May 21 11:52 supervisord.log
-rw-r--r--. 1 root root 7523 May 21 11:55 uwsgi_stderr.log
-rw-r--r--. 1 root root 0 May 21 11:52 uwsgi_stdout.log
And the nginx and uwsgi requests and sendlog events should no longer be returned by the docker logs command. So far, so good.
~]$ sudo docker logs my-container
Checking for script in /app/prestart.sh
Running script /app/prestart.sh
Running inside /app/prestart.sh, you could add migrations to this file, e.g.:
2024-05-21 11:52:52,900 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
2024-05-21 11:52:52,903 INFO RPC interface 'supervisor' initialized
2024-05-21 11:52:52,903 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2024-05-21 11:52:52,904 INFO supervisord started with pid 1
2024-05-21 11:52:53,906 INFO spawned: 'quit_on_failure' with pid 9
2024-05-21 11:52:53,909 INFO spawned: 'nginx' with pid 10
2024-05-21 11:52:53,911 INFO spawned: 'uwsgi' with pid 11
2024-05-21 11:52:53,913 INFO success: nginx entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
2024-05-21 11:52:53,913 INFO success: uwsgi entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
2024-05-21 11:52:55,748 INFO success: quit_on_failure entered RUNNING state, process has stayed up for > than 1 seconds (startsecs
Now, since requests events are no longer being redirected to stdout and stderr, we should be able to use the plain ole print statement which, by default, writes to stdout.
from flask import Blueprint
blueprint = Blueprint('routes_home', __name__)
@blueprint.route('/')
def home():
print("Hello World")
return "lookin good"
And now the docker logs command should have Hello World. Nice!
~]$ sudo docker logs my-container
Hello World
Did you find this article helpful?
If so, consider buying me a coffee over at