Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (4.7 kB)

1
===================================================
2
Logging mechanism for Synnefo's management commands
3
===================================================
4

    
5

    
6
Abstract
7
========
8

    
9
Log all stdout and stderr output of every invocation of snf-manage, on unique
10
filename under a given directory.
11

    
12

    
13
Current state and shortcomings
14
==============================
15

    
16
All Synnefo's management commands are written as custom django-admin commands.
17
This means that every management command is in fact a class that extends
18
Django's BaseCommand class.
19

    
20
Django's *BaseCommand* provides the attributes ``self.stdout`` and
21
``self.stderr`` and Django's documentation encourages the users to use these
22
attributes if they wish to write to the console. Django doesn't provide an
23
option to write the output to files and the user has to implement this
24
explicitly when implementing the ``handle`` method.
25

    
26
We would like to extend the above mechanism to allow every ``snf-manage``
27
command to log all stdout and stderr output on a unique filename under a given
28
directory. The implementation should change nothing in the way that users write
29
management commands (only acceptable change is that the new commands may have
30
to inherit a new class and not the *BaseCommand* one). This means that
31
existing management commands should play out of the box and also that the
32
logging mechanism will globally apply to all of them.
33

    
34

    
35
Proposed changes
36
================
37

    
38
In this section we will try to explain the way that the new logging mechanism
39
will be implemented as well as the reasons behind these decisions.
40

    
41
As we previously saw, we want the logging mechanism to be global and to work
42
for all the ``snf-manage`` commands without extra efford. This means that the
43
management commands will continue to use the ``self.stdout`` and
44
``self.stderr`` attributes from *BaseCommand* class to provide console
45
output. Therefor we have to provide our own ``self.stdout`` and ``self.stderr``
46
objects that will preserve the previous functionality and log to files at the
47
same time. There are two ways to achieve that:
48

    
49
Patch the Django's *BaseCommand* class and replace ``self.stdout`` and
50
``self.stderr`` attributes.
51

    
52
    This solution requires the minimum amount of changes to the management
53
    commands' code as they will use our patched version of *BaseCommand*.
54
    The downside is that we have to patch a library provided class. We are not
55
    encouraging these type of patches because it obfuscates the code (the
56
    programmer is expecting to use Django's *BaseCommand* class, not ours)
57
    and does not preserve compatiblity with other Django versions (if the
58
    implementation of Django's *BaseCommand* changes our patch will not
59
    work).
60

    
61
Create a new class that extends Django's *BaseCommand*.
62

    
63
    The downside of this solution is that we have to change the existing code
64
    so all management commands will inherit our new class and not Django's
65
    *BaseCommand*. But we find this solution to be cleaner.
66

    
67
For the above reasons we decided to go with the second option.
68

    
69

    
70
Django's ``self.stdout`` and ``self.stderr`` are implemented as
71
*OutputWrapper* objects. We will create our own class which will handle the
72
file part of the logging and we will handle the console part to the original
73
*OutputWrapper* object. We can't have used the ``logger`` libary as we want
74
to preserve the functionality of Django's *OutputWrapper*  (it uses style
75
functions to pretty print the messages).
76

    
77
Our new class has to be a **descriptor**. This is because *BaseCommand* doesn't
78
initialize the ``stdout`` and ``stderr`` attributes at ``__init__`` but sets
79
them only when it needs to (meaning inside the *execute* method).
80

    
81
The above classes will be written in snf-django-lib package meaning that all
82
the other packages will have a dependency in snf-django-lib.
83

    
84
We will combine timestamp, command name and PID to form unique names, e.g.:
85
20140120113432-server-modify-4564, where "4564" was the PID. The timestamp will
86
be first so that files will be chronologically sorted.
87

    
88

    
89
Implementation details
90
======================
91

    
92
The implementation will follow the folowing steps:
93

    
94
- Change current management commands to use ``self.stdout`` and ``self.stderr``
95
  to provide console output instead of ``sys.stdout``, ``print`` or anything
96
  else. This change complies with Django's documentation.
97

    
98
- Write a new class that will replace Django's *OutputWrapper*.
99

    
100
- Write a new class (named **SynnefoBaseCommand**) that will extend Django's
101
  *BaseCommand* and will replace ``stdout`` and ``stderr`` attributes.
102

    
103
- Change all management commands to inherit this new class
104
  **SynnefoBaseCommand**.
105

    
106
- Update package dependencies.
107

    
108
- Add a new Synnefo setting to allow the user the change the directory where
109
  the output will be saved.