blob: 1c462e7a2b202b28fd8dfeb20316631187d2a174 [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.
kurtis.heimerl5a872472013-05-31 21:47:25 +00004* Copyright 2011, 2012 Range Networks, Inc.
dburgess82c46ff2011-10-07 02:40:51 +00005*
6*
7* This software is distributed under the terms of the GNU Affero Public License.
8* See the COPYING file in the main directory for details.
9*
10* This use of this software may be subject to additional restrictions.
11* See the LEGAL file in the main directory for details.
12
13 This program is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Affero General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU Affero General Public License for more details.
22
23 You should have received a copy of the GNU Affero General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26*/
27
28#include <string.h>
29#include <cstdio>
30#include <fstream>
31#include <string>
kurtis.heimerl00913d72012-12-16 06:08:18 +000032#include <stdarg.h>
Alexander Chemeris4793f462017-03-17 18:35:48 -070033#include <sys/time.h> // For gettimeofday
dburgess82c46ff2011-10-07 02:40:51 +000034
35#include "Configuration.h"
36#include "Logger.h"
kurtis.heimerl5a872472013-05-31 21:47:25 +000037#include "Threads.h" // pat added
dburgess82c46ff2011-10-07 02:40:51 +000038
Pau Espin Pedrolddf47432018-01-09 13:25:38 +010039#define MAX_ALARMS 20
dburgess82c46ff2011-10-07 02:40:51 +000040
41using namespace std;
42
Alexander Chemerisa8cf2082015-07-30 20:04:18 -040043// Switches to enable/disable logging targets
44// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
45bool gLogToConsole = true;
46bool gLogToSyslog = false;
47FILE *gLogToFile = NULL;
48Mutex gLogToLock;
49
50
dburgess82c46ff2011-10-07 02:40:51 +000051// Reference to a global config table, used all over the system.
52extern ConfigurationTable gConfig;
Pau Espin Pedrolf3837d22018-01-09 14:49:33 +010053// Global log level threshold:
54int config_log_level;
dburgess82c46ff2011-10-07 02:40:51 +000055
56/**@ The global alarms table. */
57//@{
58Mutex alarmsLock;
59list<string> alarmsList;
60void addAlarm(const string&);
61//@}
62
63
64
kurtis.heimerl5a872472013-05-31 21:47:25 +000065// (pat) If Log messages are printed before the classes in this module are inited
66// (which happens when static classes have constructors that do work)
67// the OpenBTS just crashes.
68// Prevent that by setting sLoggerInited to true when this module is inited.
69static bool sLoggerInited = 0;
70static struct CheckLoggerInitStatus {
71 CheckLoggerInitStatus() { sLoggerInited = 1; }
72} sCheckloggerInitStatus;
73
74
dburgess82c46ff2011-10-07 02:40:51 +000075
76/** Names of the logging levels. */
77const char *levelNames[] = {
78 "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
79};
80int numLevels = 8;
81
82
kurtis.heimerl5a872472013-05-31 21:47:25 +000083int levelStringToInt(const string& name)
dburgess82c46ff2011-10-07 02:40:51 +000084{
85 // Reverse search, since the numerically larger levels are more common.
86 for (int i=numLevels-1; i>=0; i--) {
87 if (name == levelNames[i]) return i;
88 }
kurtis.heimerl5a872472013-05-31 21:47:25 +000089
90 // Common substitutions.
91 if (name=="INFORMATION") return 6;
92 if (name=="WARN") return 4;
93 if (name=="ERROR") return 3;
94 if (name=="CRITICAL") return 2;
95 if (name=="EMERGENCY") return 0;
96
97 // Unknown level.
98 return -1;
99}
100
Alexander Chemeris4793f462017-03-17 18:35:48 -0700101static std::string format(const char *fmt, ...)
102{
103 va_list ap;
104 char buf[300];
105 va_start(ap,fmt);
106 int n = vsnprintf(buf,300,fmt,ap);
107 va_end(ap);
108 if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
109 return std::string(buf);
110}
111
112const std::string timestr()
113{
114 struct timeval tv;
115 struct tm tm;
116 gettimeofday(&tv,NULL);
117 localtime_r(&tv.tv_sec,&tm);
118 unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
119 return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
120}
121
122std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
123{
124 return os << ss.str();
125}
dburgess82c46ff2011-10-07 02:40:51 +0000126
dburgess82c46ff2011-10-07 02:40:51 +0000127// 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);
Pau Espin Pedrolddf47432018-01-09 13:25:38 +0100145 while (alarmsList.size() > MAX_ALARMS) alarmsList.pop_front();
dburgess82c46ff2011-10-07 02:40:51 +0000146 alarmsLock.unlock();
147}
148
149
150Log::~Log()
151{
dburgess7b2d5222011-11-20 00:22:41 +0000152 if (mDummyInit) return;
dburgess82c46ff2011-10-07 02:40:51 +0000153 // Anything at or above LOG_CRIT is an "alarm".
154 // Save alarms in the local list and echo them to stderr.
Alexander Chemeriscc6f79b2015-03-01 10:30:12 +0100155 if (mPriority <= LOG_ERR) {
kurtis.heimerl5a872472013-05-31 21:47:25 +0000156 if (sLoggerInited) addAlarm(mStream.str().c_str());
dburgess82c46ff2011-10-07 02:40:51 +0000157 cerr << mStream.str() << endl;
158 }
Alexander Chemeris57219202015-05-24 13:20:44 -0400159 // Current logging level was already checked by the macro. So just log.
160 // Log to syslog
161 if (gLogToSyslog) {
162 syslog(mPriority, "%s", mStream.str().c_str());
163 }
164 // Log to file and console
kurtis.heimerl5a872472013-05-31 21:47:25 +0000165 if (gLogToConsole||gLogToFile) {
166 int mlen = mStream.str().size();
167 int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
Alexander Chemeris909ffbf2015-06-04 00:09:29 -0400168 ScopedLock lock(gLogToLock);
kurtis.heimerl5a872472013-05-31 21:47:25 +0000169 if (gLogToConsole) {
170 // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
171 // so just use std::cout.
172 std::cout << mStream.str();
173 if (neednl) std::cout<<"\n";
174 }
175 if (gLogToFile) {
176 fputs(mStream.str().c_str(),gLogToFile);
177 if (neednl) {fputc('\n',gLogToFile);}
178 fflush(gLogToFile);
179 }
kurtis.heimerl5a872472013-05-31 21:47:25 +0000180 }
dburgess82c46ff2011-10-07 02:40:51 +0000181}
182
183
dburgess7b2d5222011-11-20 00:22:41 +0000184Log::Log(const char* name, const char* level, int facility)
185{
186 mDummyInit = true;
187 gLogInit(name, level, facility);
188}
189
190
dburgess82c46ff2011-10-07 02:40:51 +0000191ostringstream& Log::get()
192{
193 assert(mPriority<numLevels);
194 mStream << levelNames[mPriority] << ' ';
195 return mStream;
196}
197
198
199
Pau Espin Pedrolf3837d22018-01-09 14:49:33 +0100200void gLogInit(const char* name, const char* level, int facility, char* fn)
dburgess82c46ff2011-10-07 02:40:51 +0000201{
kurtis.heimerl5a872472013-05-31 21:47:25 +0000202 // Set the level if one has been specified.
Pau Espin Pedrolf3837d22018-01-09 14:49:33 +0100203 if (level)
204 config_log_level = levelStringToInt(level);
dburgess82c46ff2011-10-07 02:40:51 +0000205
kurtis.heimerl5a872472013-05-31 21:47:25 +0000206 // Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
Pau Espin Pedrolf3837d22018-01-09 14:49:33 +0100207 if (!gLogToFile && fn) {
208 gLogToFile = fopen(fn,"w"); // New log file each time we start.
209 if (gLogToFile) {
210 time_t now;
211 time(&now);
212 fprintf(gLogToFile,"Starting at %s",ctime(&now));
213 fflush(gLogToFile);
214 std::cout << "Logging to file: " << fn << "\n";
kurtis.heimerl5a872472013-05-31 21:47:25 +0000215 }
dburgess82c46ff2011-10-07 02:40:51 +0000216 }
217
218 // Open the log connection.
219 openlog(name,0,facility);
220}
221
222
kurtis.heimerl00913d72012-12-16 06:08:18 +0000223void gLogEarly(int level, const char *fmt, ...)
224{
225 va_list args;
Alexander Chemerisa8cf2082015-07-30 20:04:18 -0400226
kurtis.heimerl00913d72012-12-16 06:08:18 +0000227 va_start(args, fmt);
Alexander Chemerisa8cf2082015-07-30 20:04:18 -0400228
229 if (gLogToSyslog) {
230 va_list args_copy;
231 va_copy(args_copy, args);
232 vsyslog(level | LOG_USER, fmt, args_copy);
233 va_end(args_copy);
234 }
235
236 if (gLogToConsole) {
237 va_list args_copy;
238 va_copy(args_copy, args);
239 vprintf(fmt, args_copy);
240 printf("\n");
241 va_end(args_copy);
242 }
243
244 if (gLogToFile) {
245 va_list args_copy;
246 va_copy(args_copy, args);
247 vfprintf(gLogToFile, fmt, args_copy);
248 fprintf(gLogToFile, "\n");
249 va_end(args_copy);
250 }
251
kurtis.heimerl00913d72012-12-16 06:08:18 +0000252 va_end(args);
253}
dburgess82c46ff2011-10-07 02:40:51 +0000254
255// vim: ts=4 sw=4