logger.py 5.84 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
#
Iustin Pop's avatar
Iustin Pop committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#

# Copyright (C) 2006, 2007 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


"""Logging for Ganeti

This module abstracts the logging handling away from the rest of the
Ganeti code. It offers some utility functions for easy logging.
"""

# pylint: disable-msg=W0603,C0103

import sys
import logging
import os, os.path

from ganeti import constants

_program = '(unknown)'
_errlog = None
_inflog = None
_dbglog = None
_stdout = None
_stderr = None
_debug = False


def _SetDestination(name, filename, stream=None):
  """Configure the destination for a given logger

  This function configures the logging destination for a given loger.
  Parameters:
    - name: the logger name
    - filename: if not empty, log messages will be written (also) to this file
    - stream: if not none, log messages will be output (also) to this stream

  Returns:
    - the logger identified by the `name` argument
  """
  ret = logging.getLogger(name)

  if filename:
    fmtr = logging.Formatter('%(asctime)s %(message)s')

    hdlr = logging.FileHandler(filename)
    hdlr.setFormatter(fmtr)
    ret.addHandler(hdlr)

  if stream:
    if name in ('error', 'info', 'debug'):
      fmtr = logging.Formatter('%(asctime)s %(message)s')
    else:
      fmtr = logging.Formatter('%(message)s')
    hdlr = logging.StreamHandler(stream)
    hdlr.setFormatter(fmtr)
    ret.addHandler(hdlr)

  ret.setLevel(logging.INFO)

  return ret


def _GenericSetup(program, errfile, inffile, dbgfile,
                  twisted_workaround=False):
  """Configure logging based on arguments

  Arguments:
    - name of program
    - error log filename
    - info log filename
    - debug log filename
    - twisted_workaround: if true, emit all messages to stderr
  """
  global _program
  global _errlog
  global _inflog
  global _dbglog
  global _stdout
  global _stderr

  _program = program
  if twisted_workaround:
    _errlog = _SetDestination('error', None, sys.stderr)
    _inflog = _SetDestination('info', None, sys.stderr)
    _dbglog = _SetDestination('debug', None, sys.stderr)
  else:
    _errlog = _SetDestination('error', errfile)
    _inflog = _SetDestination('info', inffile)
    _dbglog = _SetDestination('debug', dbgfile)

  _stdout = _SetDestination('user', None, sys.stdout)
  _stderr = _SetDestination('stderr', None, sys.stderr)


def SetupLogging(twisted_workaround=False, debug=False, program='ganeti'):
  """Setup logging for ganeti

  On failure, a check is made whether process is run by root or not,
  and an appropriate error message is printed on stderr, then process
  exits.

  This function is just a wraper over `_GenericSetup()` using specific
  arguments.

  Parameter:
    twisted_workaround: passed to `_GenericSetup()`

  """
  try:
    _GenericSetup(program,
                  os.path.join(constants.LOG_DIR, "errors"),
                  os.path.join(constants.LOG_DIR, "info"),
                  os.path.join(constants.LOG_DIR, "debug"),
                  twisted_workaround)
  except IOError:
    # The major reason to end up here is that we're being run as a
    # non-root user.  We might also get here if xen has not been
    # installed properly.  This is not the correct place to enforce
    # being run by root; nevertheless, here makes sense because here
    # is where we first notice it.
    if os.getuid() != 0:
      sys.stderr.write('This program must be run by the superuser.\n')
    else:
      sys.stderr.write('Unable to open log files.  Incomplete system?\n')

    sys.exit(2)

  global _debug
  _debug = debug


def _WriteEntry(log, txt):
  """
  Write a message to a given log.
  Splits multi-line messages up into a series of log writes, to
  keep consistent format on lines in file.

  Parameters:
    - log: the destination log
    - txt: the message

  """
  if log is None:
    sys.stderr.write("Logging system not initialized while processing"
                     " message:\n")
    sys.stderr.write("%s\n" % txt)
    return

  lines = txt.split('\n')

  spaces = ' ' * len(_program) + '| '

  lines = ([ _program + ': ' + lines[0] ] +
           map(lambda a: spaces + a, lines[1:]))

  for line in lines:
    log.log(logging.INFO, line)


def ToStdout(txt):
  """Write a message to stdout only, bypassing the logging system

  Parameters:
    - txt: the message

  """
  sys.stdout.write(txt + '\n')
  sys.stdout.flush()


def ToStderr(txt):
  """Write a message to stderr only, bypassing the logging system

  Parameters:
    - txt: the message

  """
  sys.stderr.write(txt + '\n')
  sys.stderr.flush()


def Error(txt):
  """Write a message to our error log

  Parameters:
    - dbg: if true, the message will also be output to stderr
    - txt: the log message

  """
  _WriteEntry(_errlog, txt)
  sys.stderr.write(txt + '\n')


def Info(txt):
  """Write a message to our general messages log

  If the global debug flag is true, the log message will also be
  output to stderr.

  Parameters:
    - txt: the log message

  """
  _WriteEntry(_inflog, txt)
  if _debug:
    _WriteEntry(_stderr, txt)


def Debug(txt):
  """Write a message to the debug log

  If the global debug flag is true, the log message will also be
  output to stderr.

  Parameters:
    - txt: the log message

  """
  _WriteEntry(_dbglog, txt)
  if _debug:
    _WriteEntry(_stderr, txt)