Skip to content

Commit 5b5cf02

Browse files
committed
Add logging into 10, feed thru to 11
1 parent b1a2836 commit 5b5cf02

File tree

5 files changed

+137
-27
lines changed

5 files changed

+137
-27
lines changed

chapter_09_docker.asciidoc

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,23 +1419,19 @@ https://en.wikipedia.org/wiki/Digestive_biscuit[chocolate biscuit].
14191419
.Test-Driving Server Configuration and Deployment
14201420
*******************************************************************************
14211421
1422-
TODO update this recap.
1423-
1424-
14251422
Tests and small steps some of the uncertainty out of deployment::
14261423
For developers, ops and infra work is always "fun",
14271424
by which I mean a process full of uncertainty and surprises.
14281425
My aim during this chapter was to show that a step-by-step approach
14291426
helps to minimise risk, especially when allied to a functional test suite
14301427
that can help us to catch errors early.
14311428
1432-
// TODO amend the rest
14331429
Some typical pain points--networking, ports, static files, and the database::
1434-
The things that you need to keep an eye out for on any deployment include
1435-
making sure your database configuration, static files, software
1436-
dependencies, and custom settings that differ between development and
1437-
production. You'll need to think through each of these for your own
1438-
deployments.
1430+
Moving from the local django development server to a container
1431+
is chance to rehearse the fiddliness of configuring networking
1432+
in a deployed environment.
1433+
It's also a chance to think about persistence and the database,
1434+
and some configuration issues like static files.
14391435
14401436
14411437
*******************************************************************************

chapter_10_production_readiness.asciidoc

Lines changed: 110 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -399,11 +399,12 @@ or on a server, so we'll use the `-e` flag again:
399399

400400
[subs="specialcharacters,quotes"]
401401
----
402-
$ *docker build -t superlists . && \
403-
docker run -p 8888:8888 -v ./src/db.sqlite3:/src/db.sqlite3 \
404-
-e DJANGO_SECRET_KEY=sekrit \
405-
-e DJANGO_ALLOWED_HOST=localhost \
406-
-it superlists*
402+
$ *docker build -t superlists . && docker run \
403+
-p 8888:8888 \
404+
-v ./src/db.sqlite3:/src/db.sqlite3 \
405+
-e DJANGO_SECRET_KEY=sekrit \
406+
-e DJANGO_ALLOWED_HOST=localhost \
407+
-it superlists*
407408
----
408409

409410

@@ -460,19 +461,85 @@ Find out how in the next exciting installment...
460461

461462

462463

463-
=== TODO: log files
464+
=== Configuring logging
465+
466+
One last thing we'll want to do is make sure that we can get logs out of our server.
467+
If things go wrong, we want to be able to get to the tracebacks, and as we'll soon see,
468+
switching DEBUG off means that Django's default logging configuration changes.
469+
470+
471+
==== Provoking a deliberate error
472+
473+
To test this, we'll provoke a deliberate error by deleting the database file.
464474

465475

466-
provoke a 500 error somehow and make sure we see tracebacks for it.
467476

477+
[subs="specialcharacters,quotes"]
478+
----
479+
$ *rm src/db.sqlite3 && touch src/db.sqlite3*
480+
----
481+
482+
TIP: If you use the `-v` option for docker run to mount a nonexistent path,
483+
Docker will create a new folder at that path.
484+
This is why we do the `touch db.sqlite3`
485+
so that we have a placeholder empty file where the datbase should be.
486+
Otherwise we get a folder instead of an empty file and django gets even more confused;
487+
if you ever see an error saying `chown: permission denied`, that's probably it.
488+
Do a `rm -rf src/db.sqlite3 && touch src/db.sqlite3` to fix it.
468489

469-
eg delete db.sqlite3
470490

471-
Will show no logs, because by default when DEBUG=True logs are emailed to admins
472491

473-
https://docs.djangoproject.com/en/5.0/ref/logging/#default-logging-configuration
492+
Now if you run the tests, you'll see they fail;
493+
494+
[role="small-code"]
495+
[subs="specialcharacters,macros"]
496+
----
497+
$ pass:quotes[*TEST_SERVER=localhost:8888 ./src/manage.py test functional_tests --failfast*]
498+
[...]
499+
500+
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate
501+
element: [id="id_list_table"]; [...]
502+
----
503+
504+
And you might spot in the browser that we just see a minimal error page,
505+
with no debug info (try it manually if you like):
474506

475-
simple logging config, send everything to stdout:
507+
[[minimal-error-page]]
508+
.Minimal default server error 500
509+
image::images/server_error_500.png["A minimal error page saying just Server error (500)"]
510+
511+
512+
But if you look in your docker terminal, you'll see there is no traceback:
513+
514+
----
515+
[2024-02-28 10:41:53 +0000] [7] [INFO] Starting gunicorn 21.2.0
516+
[2024-02-28 10:41:53 +0000] [7] [INFO] Listening at: http://0.0.0.0:8888 (7)
517+
[2024-02-28 10:41:53 +0000] [7] [INFO] Using worker: sync
518+
[2024-02-28 10:41:53 +0000] [8] [INFO] Booting worker with pid: 8
519+
----
520+
521+
522+
Where have the tracebacks gone?
523+
You might have been expecting that the django debug page and its tracebacks
524+
would disappear from our web browser,
525+
but it's more of shock to see that they are no longer appearing in the terminal either!
526+
If you're like me you might find yourself wondering if we really _did_ see them earlier
527+
and starting to doubt your own sanity.
528+
But the explanation is that Django's
529+
https://docs.djangoproject.com/en/5.0/ref/logging/#default-logging-configuration[default logging configuration]
530+
changes when DEBUG is turned off:
531+
532+
This means we need to interact with the standard library's `logging` module,
533+
unfortunately one of the most fiddly parts of the Python standard libraryfootnote:[
534+
It's not necessarily for bad reasons, but it is all very Java-ey and enterprisey.
535+
I mean, yes, separating the concepts of handlers and loggers and filters,
536+
and making it all configurable in a nested hierarchy is all well and good
537+
and covers every possible use case,
538+
but sometimes you just wanna say "just print stuff to stdout pls",
539+
and you wish that configuring the simplest thing was a little easier].
540+
541+
Here's pretty much the simplest possible logging config
542+
which just prints everything to the console (ie standard out).
476543

477544

478545
[role="sourcecode"]
@@ -493,8 +560,34 @@ LOGGING = {
493560
----
494561
====
495562

563+
Rebuild and restart our container,
564+
try the FT again (or submitting a new list item manually)
565+
and we now should see a clear error message:
566+
567+
568+
----
569+
Internal Server Error: /lists/new
570+
Traceback (most recent call last):
571+
[...]
572+
File "/src/lists/views.py", line 10, in new_list
573+
nulist = List.objects.create()
574+
^^^^^^^^^^^^^^^^^^^^^
575+
[...]
576+
File "/venv/lib/python3.12/site-packages/django/db/backends/sqlite3/base.py",
577+
line 328, in execute
578+
return super().execute(query, params)
579+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
580+
django.db.utils.OperationalError: no such table: lists_list
581+
----
582+
583+
Re-create the datase with `./src/manage.py migrate` and we'll be back to a working state.
496584

497-
docker logs might be enough.
585+
Don't forget to commit our changes to _settings.py_,
586+
and I think we can call it job done!
587+
We've at least touched on many or most of the things you might need to think about
588+
when considering production-readiness,
589+
we've worked in small steps and used our tests all the way along,
590+
and we're now ready to deploy our container to a real server!
498591

499592

500593
[role="pagebreak-before less_space"]
@@ -525,5 +618,10 @@ Change things one at a time and rerun your tests frequently::
525618
and either be confident that everything works as well as it did before,
526619
or find out immediately if we did something wrong.
527620
621+
Think about logging and observability::
622+
When things go wrong, you need to be able to find out what happened.
623+
At a minimum you need a way of getting logs and tracebacks out of your server,
624+
and in more advanced environments you'll want to think about metrics and tracing too.
625+
But we can't cover all that in this book!
528626
529627
*******************************************************************************

chapter_11_ansible.asciidoc

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -522,8 +522,10 @@ false, "AttachStdout": true, "Cmd": ["gunicorn", "--bind", ":8888",
522522
[...]
523523
----
524524

525+
525526
TODO: sort out macos M1/arch issues, `docker build --platform linux/amd64` etc.
526-
////
527+
528+
----
527529
- name: Build container image locally
528530
community.docker.docker_image:
529531
name: superlists
@@ -533,7 +535,8 @@ TODO: sort out macos M1/arch issues, `docker build --platform linux/amd64` etc.
533535
path: ../Dockerfile
534536
platform: linux/amd64
535537
delegate_to: 127.0.0.1
536-
////
538+
----
539+
537540

538541
Looks ok! Let's see if that worked?
539542

@@ -660,10 +663,23 @@ and reloads it automatically if it crashes.
660663
////
661664

662665

663-
=== Mounting the database on the server
666+
=== Mounting the database on the server and running migrations
667+
668+
todo show test output and/or error page
669+
670+
[subs="specialcharacters,quotes"]
671+
----
672+
*ssh [email protected] docker logs superlists*
673+
[...]
674+
django.db.utils.OperationalError: no such table: lists_list
675+
----
676+
677+
todo add db.sqlite mount and migrate
678+
664679

665-
TODOOooo
680+
=== It workssss
666681

682+
hooray
667683

668684
[role="small-code"]
669685
[subs="specialcharacters,macros"]

0 commit comments

Comments
 (0)