Statistics
| Branch: | Tag: | Revision:

root / docs / design / logging-management-commands.rst @ b2376ba3

History | View | Annotate | Download (4.7 kB)

1 b2376ba3 Ilias Tsitsimpis
===================================================
2 b2376ba3 Ilias Tsitsimpis
Logging mechanism for Synnefo's management commands
3 b2376ba3 Ilias Tsitsimpis
===================================================
4 b2376ba3 Ilias Tsitsimpis
5 b2376ba3 Ilias Tsitsimpis
6 b2376ba3 Ilias Tsitsimpis
Abstract
7 b2376ba3 Ilias Tsitsimpis
========
8 b2376ba3 Ilias Tsitsimpis
9 b2376ba3 Ilias Tsitsimpis
Log all stdout and stderr output of every invocation of snf-manage, on unique
10 b2376ba3 Ilias Tsitsimpis
filename under a given directory.
11 b2376ba3 Ilias Tsitsimpis
12 b2376ba3 Ilias Tsitsimpis
13 b2376ba3 Ilias Tsitsimpis
Current state and shortcomings
14 b2376ba3 Ilias Tsitsimpis
==============================
15 b2376ba3 Ilias Tsitsimpis
16 b2376ba3 Ilias Tsitsimpis
All Synnefo's management commands are written as custom django-admin commands.
17 b2376ba3 Ilias Tsitsimpis
This means that every management command is in fact a class that extends
18 b2376ba3 Ilias Tsitsimpis
Django's BaseCommand class.
19 b2376ba3 Ilias Tsitsimpis
20 b2376ba3 Ilias Tsitsimpis
Django's *BaseCommand* provides the attributes ``self.stdout`` and
21 b2376ba3 Ilias Tsitsimpis
``self.stderr`` and Django's documentation encourages the users to use these
22 b2376ba3 Ilias Tsitsimpis
attributes if they wish to write to the console. Django doesn't provide an
23 b2376ba3 Ilias Tsitsimpis
option to write the output to files and the user has to implement this
24 b2376ba3 Ilias Tsitsimpis
explicitly when implementing the ``handle`` method.
25 b2376ba3 Ilias Tsitsimpis
26 b2376ba3 Ilias Tsitsimpis
We would like to extend the above mechanism to allow every ``snf-manage``
27 b2376ba3 Ilias Tsitsimpis
command to log all stdout and stderr output on a unique filename under a given
28 b2376ba3 Ilias Tsitsimpis
directory. The implementation should change nothing in the way that users write
29 b2376ba3 Ilias Tsitsimpis
management commands (only acceptable change is that the new commands may have
30 b2376ba3 Ilias Tsitsimpis
to inherit a new class and not the *BaseCommand* one). This means that
31 b2376ba3 Ilias Tsitsimpis
existing management commands should play out of the box and also that the
32 b2376ba3 Ilias Tsitsimpis
logging mechanism will globally apply to all of them.
33 b2376ba3 Ilias Tsitsimpis
34 b2376ba3 Ilias Tsitsimpis
35 b2376ba3 Ilias Tsitsimpis
Proposed changes
36 b2376ba3 Ilias Tsitsimpis
================
37 b2376ba3 Ilias Tsitsimpis
38 b2376ba3 Ilias Tsitsimpis
In this section we will try to explain the way that the new logging mechanism
39 b2376ba3 Ilias Tsitsimpis
will be implemented as well as the reasons behind these decisions.
40 b2376ba3 Ilias Tsitsimpis
41 b2376ba3 Ilias Tsitsimpis
As we previously saw, we want the logging mechanism to be global and to work
42 b2376ba3 Ilias Tsitsimpis
for all the ``snf-manage`` commands without extra efford. This means that the
43 b2376ba3 Ilias Tsitsimpis
management commands will continue to use the ``self.stdout`` and
44 b2376ba3 Ilias Tsitsimpis
``self.stderr`` attributes from *BaseCommand* class to provide console
45 b2376ba3 Ilias Tsitsimpis
output. Therefor we have to provide our own ``self.stdout`` and ``self.stderr``
46 b2376ba3 Ilias Tsitsimpis
objects that will preserve the previous functionality and log to files at the
47 b2376ba3 Ilias Tsitsimpis
same time. There are two ways to achieve that:
48 b2376ba3 Ilias Tsitsimpis
49 b2376ba3 Ilias Tsitsimpis
Patch the Django's *BaseCommand* class and replace ``self.stdout`` and
50 b2376ba3 Ilias Tsitsimpis
``self.stderr`` attributes.
51 b2376ba3 Ilias Tsitsimpis
52 b2376ba3 Ilias Tsitsimpis
    This solution requires the minimum amount of changes to the management
53 b2376ba3 Ilias Tsitsimpis
    commands' code as they will use our patched version of *BaseCommand*.
54 b2376ba3 Ilias Tsitsimpis
    The downside is that we have to patch a library provided class. We are not
55 b2376ba3 Ilias Tsitsimpis
    encouraging these type of patches because it obfuscates the code (the
56 b2376ba3 Ilias Tsitsimpis
    programmer is expecting to use Django's *BaseCommand* class, not ours)
57 b2376ba3 Ilias Tsitsimpis
    and does not preserve compatiblity with other Django versions (if the
58 b2376ba3 Ilias Tsitsimpis
    implementation of Django's *BaseCommand* changes our patch will not
59 b2376ba3 Ilias Tsitsimpis
    work).
60 b2376ba3 Ilias Tsitsimpis
61 b2376ba3 Ilias Tsitsimpis
Create a new class that extends Django's *BaseCommand*.
62 b2376ba3 Ilias Tsitsimpis
63 b2376ba3 Ilias Tsitsimpis
    The downside of this solution is that we have to change the existing code
64 b2376ba3 Ilias Tsitsimpis
    so all management commands will inherit our new class and not Django's
65 b2376ba3 Ilias Tsitsimpis
    *BaseCommand*. But we find this solution to be cleaner.
66 b2376ba3 Ilias Tsitsimpis
67 b2376ba3 Ilias Tsitsimpis
For the above reasons we decided to go with the second option.
68 b2376ba3 Ilias Tsitsimpis
69 b2376ba3 Ilias Tsitsimpis
70 b2376ba3 Ilias Tsitsimpis
Django's ``self.stdout`` and ``self.stderr`` are implemented as
71 b2376ba3 Ilias Tsitsimpis
*OutputWrapper* objects. We will create our own class which will handle the
72 b2376ba3 Ilias Tsitsimpis
file part of the logging and we will handle the console part to the original
73 b2376ba3 Ilias Tsitsimpis
*OutputWrapper* object. We can't have used the ``logger`` libary as we want
74 b2376ba3 Ilias Tsitsimpis
to preserve the functionality of Django's *OutputWrapper*  (it uses style
75 b2376ba3 Ilias Tsitsimpis
functions to pretty print the messages).
76 b2376ba3 Ilias Tsitsimpis
77 b2376ba3 Ilias Tsitsimpis
Our new class has to be a **descriptor**. This is because *BaseCommand* doesn't
78 b2376ba3 Ilias Tsitsimpis
initialize the ``stdout`` and ``stderr`` attributes at ``__init__`` but sets
79 b2376ba3 Ilias Tsitsimpis
them only when it needs to (meaning inside the *execute* method).
80 b2376ba3 Ilias Tsitsimpis
81 b2376ba3 Ilias Tsitsimpis
The above classes will be written in snf-django-lib package meaning that all
82 b2376ba3 Ilias Tsitsimpis
the other packages will have a dependency in snf-django-lib.
83 b2376ba3 Ilias Tsitsimpis
84 b2376ba3 Ilias Tsitsimpis
We will combine timestamp, command name and PID to form unique names, e.g.:
85 b2376ba3 Ilias Tsitsimpis
20140120113432-server-modify-4564, where "4564" was the PID. The timestamp will
86 b2376ba3 Ilias Tsitsimpis
be first so that files will be chronologically sorted.
87 b2376ba3 Ilias Tsitsimpis
88 b2376ba3 Ilias Tsitsimpis
89 b2376ba3 Ilias Tsitsimpis
Implementation details
90 b2376ba3 Ilias Tsitsimpis
======================
91 b2376ba3 Ilias Tsitsimpis
92 b2376ba3 Ilias Tsitsimpis
The implementation will follow the folowing steps:
93 b2376ba3 Ilias Tsitsimpis
94 b2376ba3 Ilias Tsitsimpis
- Change current management commands to use ``self.stdout`` and ``self.stderr``
95 b2376ba3 Ilias Tsitsimpis
  to provide console output instead of ``sys.stdout``, ``print`` or anything
96 b2376ba3 Ilias Tsitsimpis
  else. This change complies with Django's documentation.
97 b2376ba3 Ilias Tsitsimpis
98 b2376ba3 Ilias Tsitsimpis
- Write a new class that will replace Django's *OutputWrapper*.
99 b2376ba3 Ilias Tsitsimpis
100 b2376ba3 Ilias Tsitsimpis
- Write a new class (named **SynnefoBaseCommand**) that will extend Django's
101 b2376ba3 Ilias Tsitsimpis
  *BaseCommand* and will replace ``stdout`` and ``stderr`` attributes.
102 b2376ba3 Ilias Tsitsimpis
103 b2376ba3 Ilias Tsitsimpis
- Change all management commands to inherit this new class
104 b2376ba3 Ilias Tsitsimpis
  **SynnefoBaseCommand**.
105 b2376ba3 Ilias Tsitsimpis
106 b2376ba3 Ilias Tsitsimpis
- Update package dependencies.
107 b2376ba3 Ilias Tsitsimpis
108 b2376ba3 Ilias Tsitsimpis
- Add a new Synnefo setting to allow the user the change the directory where
109 b2376ba3 Ilias Tsitsimpis
  the output will be saved.