@@ -399,11 +399,12 @@ or on a server, so we'll use the `-e` flag again:
399
399
400
400
[subs="specialcharacters,quotes"]
401
401
----
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*
407
408
----
408
409
409
410
@@ -460,19 +461,85 @@ Find out how in the next exciting installment...
460
461
461
462
462
463
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.
464
474
465
475
466
- provoke a 500 error somehow and make sure we see tracebacks for it.
467
476
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.
468
489
469
- eg delete db.sqlite3
470
490
471
- Will show no logs, because by default when DEBUG=True logs are emailed to admins
472
491
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):
474
506
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).
476
543
477
544
478
545
[role="sourcecode"]
@@ -493,8 +560,34 @@ LOGGING = {
493
560
----
494
561
====
495
562
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.
496
584
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!
498
591
499
592
500
593
[role="pagebreak-before less_space"]
@@ -525,5 +618,10 @@ Change things one at a time and rerun your tests frequently::
525
618
and either be confident that everything works as well as it did before,
526
619
or find out immediately if we did something wrong.
527
620
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!
528
626
529
627
*******************************************************************************
0 commit comments