Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Logging.hs @ c92b4671

History | View | Annotate | Download (4.9 kB)

1
{-# LANGUAGE TemplateHaskell #-}
2

    
3
{-| Implementation of the Ganeti logging functionality.
4

    
5
This currently lacks the following (FIXME):
6

    
7
- log file reopening
8

    
9
Note that this requires the hslogger library version 1.1 and above.
10

    
11
-}
12

    
13
{-
14

    
15
Copyright (C) 2011, 2012, 2013 Google Inc.
16

    
17
This program is free software; you can redistribute it and/or modify
18
it under the terms of the GNU General Public License as published by
19
the Free Software Foundation; either version 2 of the License, or
20
(at your option) any later version.
21

    
22
This program is distributed in the hope that it will be useful, but
23
WITHOUT ANY WARRANTY; without even the implied warranty of
24
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25
General Public License for more details.
26

    
27
You should have received a copy of the GNU General Public License
28
along with this program; if not, write to the Free Software
29
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
30
02110-1301, USA.
31

    
32
-}
33

    
34
module Ganeti.Logging
35
  ( setupLogging
36
  , MonadLog(..)
37
  , Priority(..)
38
  , logDebug
39
  , logInfo
40
  , logNotice
41
  , logWarning
42
  , logError
43
  , logCritical
44
  , logAlert
45
  , logEmergency
46
  , SyslogUsage(..)
47
  , syslogUsageToRaw
48
  , syslogUsageFromRaw
49
  ) where
50

    
51
import Control.Monad
52
import Control.Monad.Reader
53
import System.Log.Logger
54
import System.Log.Handler.Simple
55
import System.Log.Handler.Syslog
56
import System.Log.Handler (setFormatter, LogHandler)
57
import System.Log.Formatter
58
import System.IO
59

    
60
import Ganeti.THH
61
import qualified Ganeti.ConstantUtils as ConstantUtils
62

    
63
-- | Syslog usage type.
64
$(declareLADT ''String "SyslogUsage"
65
  [ ("SyslogNo",   "no")
66
  , ("SyslogYes",  "yes")
67
  , ("SyslogOnly", "only")
68
  ])
69

    
70
-- | Builds the log formatter.
71
logFormatter :: String  -- ^ Program
72
             -> Bool    -- ^ Multithreaded
73
             -> Bool    -- ^ Syslog
74
             -> LogFormatter a
75
logFormatter prog mt syslog =
76
  let parts = [ if syslog
77
                  then "[$pid]:"
78
                  else "$time: " ++ prog ++ " pid=$pid"
79
              , if mt then if syslog then " ($tid)" else "/$tid"
80
                  else ""
81
              , " $prio $msg"
82
              ]
83
  in tfLogFormatter "%F %X,%q %Z" $ concat parts
84

    
85
-- | Helper to open and set the formatter on a log if enabled by a
86
-- given condition, otherwise returning an empty list.
87
openFormattedHandler :: (LogHandler a) => Bool
88
                     -> LogFormatter a -> IO a -> IO [a]
89
openFormattedHandler False _ _ = return []
90
openFormattedHandler True fmt opener = do
91
  handler <- opener
92
  return [setFormatter handler fmt]
93

    
94
-- | Sets up the logging configuration.
95
setupLogging :: Maybe String    -- ^ Log file
96
             -> String    -- ^ Program name
97
             -> Bool      -- ^ Debug level
98
             -> Bool      -- ^ Log to stderr
99
             -> Bool      -- ^ Log to console
100
             -> SyslogUsage -- ^ Syslog usage
101
             -> IO ()
102
setupLogging logf program debug stderr_logging console syslog = do
103
  let level = if debug then DEBUG else INFO
104
      destf = if console then Just ConstantUtils.devConsole else logf
105
      fmt = logFormatter program False False
106
      file_logging = syslog /= SyslogOnly
107

    
108
  updateGlobalLogger rootLoggerName (setLevel level)
109

    
110
  stderr_handlers <- openFormattedHandler stderr_logging fmt $
111
                     streamHandler stderr level
112

    
113
  file_handlers <- case destf of
114
                     Nothing -> return []
115
                     Just path -> openFormattedHandler file_logging fmt $
116
                                  fileHandler path level
117

    
118
  let handlers = file_handlers ++ stderr_handlers
119
  updateGlobalLogger rootLoggerName $ setHandlers handlers
120
  -- syslog handler is special (another type, still instance of the
121
  -- typeclass, and has a built-in formatter), so we can't pass it in
122
  -- the above list
123
  when (syslog /= SyslogNo) $ do
124
    syslog_handler <- openlog program [PID] DAEMON INFO
125
    updateGlobalLogger rootLoggerName $ addHandler syslog_handler
126

    
127
-- * Logging function aliases
128

    
129
-- | A monad that allows logging.
130
class Monad m => MonadLog m where
131
  -- | Log at a given level.
132
  logAt :: Priority -> String -> m ()
133

    
134
instance MonadLog IO where
135
  logAt = logM rootLoggerName
136

    
137
instance (MonadLog m) => MonadLog (ReaderT r m) where
138
  logAt p x = lift $ logAt p x
139

    
140
-- | Log at debug level.
141
logDebug :: (MonadLog m) => String -> m ()
142
logDebug = logAt DEBUG
143

    
144
-- | Log at info level.
145
logInfo :: (MonadLog m) => String -> m ()
146
logInfo = logAt INFO
147

    
148
-- | Log at notice level.
149
logNotice :: (MonadLog m) => String -> m ()
150
logNotice = logAt NOTICE
151

    
152
-- | Log at warning level.
153
logWarning :: (MonadLog m) => String -> m ()
154
logWarning = logAt WARNING
155

    
156
-- | Log at error level.
157
logError :: (MonadLog m) => String -> m ()
158
logError = logAt ERROR
159

    
160
-- | Log at critical level.
161
logCritical :: (MonadLog m) => String -> m ()
162
logCritical = logAt CRITICAL
163

    
164
-- | Log at alert level.
165
logAlert :: (MonadLog m) => String -> m ()
166
logAlert = logAt ALERT
167

    
168
-- | Log at emergency level.
169
logEmergency :: (MonadLog m) => String -> m ()
170
logEmergency = logAt EMERGENCY