blob: d8bfc6e6f60ce353c75a7e4470e8c0f312863a8a [file] [log] [blame]
dburgess82c46ff2011-10-07 02:40:51 +00001/*
2* Copyright 2009, 2010 Free Software Foundation, Inc.
3* Copyright 2010 Kestrel Signal Processing, Inc.
4*
5*
6* This software is distributed under the terms of the GNU Affero Public License.
7* See the COPYING file in the main directory for details.
8*
9* This use of this software may be subject to additional restrictions.
10* See the LEGAL file in the main directory for details.
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU Affero General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Affero General Public License for more details.
21
22 You should have received a copy of the GNU Affero General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24
25*/
26
27#include <string.h>
28#include <cstdio>
29#include <fstream>
30#include <string>
31
32#include "Configuration.h"
33#include "Logger.h"
34
35
36using namespace std;
37
38// Reference to a global config table, used all over the system.
39extern ConfigurationTable gConfig;
40
41
42/**@ The global alarms table. */
43//@{
44Mutex alarmsLock;
45list<string> alarmsList;
46void addAlarm(const string&);
47//@}
48
49
50
51
52/** Names of the logging levels. */
53const char *levelNames[] = {
54 "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
55};
56int numLevels = 8;
57
58
59/** Given a string, return the corresponding level name. */
60int lookupLevel(const string& name)
61{
62 // Reverse search, since the numerically larger levels are more common.
63 for (int i=numLevels-1; i>=0; i--) {
64 if (name == levelNames[i]) return i;
65 }
66 // This should never be called with a bogus name.
67 LOG(ERR) << "undefined logging level " << name << "defaulting to ERR";
68 return LOG_ERR;
69}
70
71
72int getLoggingLevel(const char* filename)
73{
74 // Default level?
75 if (!filename) return lookupLevel(gConfig.getStr("Log.Level"));
76
77 // This can afford to be inefficient since it is not called that often.
78 const string keyName = string("Log.Level.") + string(filename);
79 if (gConfig.defines(keyName)) return lookupLevel(gConfig.getStr(keyName));
80 return lookupLevel(gConfig.getStr("Log.Level"));
81}
82
83
84
85int gGetLoggingLevel(const char* filename)
86{
87 // This is called a lot and needs to be efficient.
88
89 static Mutex sLogCacheLock;
90 static map<uint64_t,int> sLogCache;
91 static unsigned sCacheCount;
92 static const unsigned sCacheRefreshCount = 1000;
93
94 if (filename==NULL) return gGetLoggingLevel("");
95
96 HashString hs(filename);
97 uint64_t key = hs.hash();
98
99 sLogCacheLock.lock();
100 // Time for a cache flush?
101 if (sCacheCount>sCacheRefreshCount) {
102 sLogCache.clear();
103 sCacheCount=0;
104 }
105 // Is it cached already?
106 map<uint64_t,int>::const_iterator where = sLogCache.find(key);
107 sCacheCount++;
108 if (where!=sLogCache.end()) {
109 int retVal = where->second;
110 sLogCacheLock.unlock();
111 return retVal;
112 }
113 // Look it up in the config table and cache it.
114 // FIXME: Figure out why unlock and lock below fix the config table deadlock.
115 sLogCacheLock.unlock();
116 int level = getLoggingLevel(filename);
117 sLogCacheLock.lock();
118 sLogCache.insert(pair<uint64_t,int>(key,level));
119 sLogCacheLock.unlock();
120 return level;
121}
122
123
124
125
126
127// copies the alarm list and returns it. list supposed to be small.
128list<string> gGetLoggerAlarms()
129{
130 alarmsLock.lock();
131 list<string> ret;
132 // excuse the "complexity", but to use std::copy with a list you need
133 // an insert_iterator - copy technically overwrites, doesn't insert.
134 insert_iterator< list<string> > ii(ret, ret.begin());
135 copy(alarmsList.begin(), alarmsList.end(), ii);
136 alarmsLock.unlock();
137 return ret;
138}
139
140/** Add an alarm to the alarm list. */
141void addAlarm(const string& s)
142{
143 alarmsLock.lock();
144 alarmsList.push_back(s);
145 unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
146 while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
147 alarmsLock.unlock();
148}
149
150
151Log::~Log()
152{
dburgess7b2d5222011-11-20 00:22:41 +0000153 if (mDummyInit) return;
dburgess82c46ff2011-10-07 02:40:51 +0000154 // Anything at or above LOG_CRIT is an "alarm".
155 // Save alarms in the local list and echo them to stderr.
156 if (mPriority <= LOG_CRIT) {
157 addAlarm(mStream.str().c_str());
158 cerr << mStream.str() << endl;
159 }
160 // Current logging level was already checked by the macro.
161 // So just log.
162 syslog(mPriority, "%s", mStream.str().c_str());
163}
164
165
dburgess7b2d5222011-11-20 00:22:41 +0000166Log::Log(const char* name, const char* level, int facility)
167{
168 mDummyInit = true;
169 gLogInit(name, level, facility);
170}
171
172
dburgess82c46ff2011-10-07 02:40:51 +0000173ostringstream& Log::get()
174{
175 assert(mPriority<numLevels);
176 mStream << levelNames[mPriority] << ' ';
177 return mStream;
178}
179
180
181
182void gLogInit(const char* name, const char* level, int facility)
183{
184 // Set the level.
185 if (level) {
186 gConfig.set("Log.Level",level);
187 } else {
188 if (!gConfig.defines("Log.Level")) {
189 gConfig.set("Log.Level","WARNING");
190 }
191 }
192
193 // Define other logging parameters in the global config.
194 if (!gConfig.defines("Log.Alarms.Max")) {
195 gConfig.set("Log.Alarms.Max",DEFAULT_MAX_ALARMS);
196 }
197
198 // Open the log connection.
199 openlog(name,0,facility);
200}
201
202
203
204
205// vim: ts=4 sw=4