summaryrefslogtreecommitdiff
path: root/include/logger.h
blob: 754fa26e8181f59d1ca7576e6cd97fbf073ef29d (plain)
1
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
/*       +------------------------------------+
 *       | Inspire Internet Relay Chat Daemon |
 *       +------------------------------------+
 *
 *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
 * See: http://www.inspircd.org/wiki/index.php/Credits
 *
 * This program is free but copyrighted software; see
 *            the file COPYING for details.
 *
 * ---------------------------------------------------
 */

#ifndef __LOGMANAGER_H
#define __LOGMANAGER_H

/** This class implements a nonblocking writer.
 * Most people writing an ircd give little thought to their disk
 * i/o. On a congested system, disk writes can block for long
 * periods of time (e.g. if the system is busy and/or swapping
 * a lot). If we just use a blocking fprintf() call, this could
 * block for undesirable amounts of time (half of a second through
 * to whole seconds). We DO NOT want this, so we make our logfile
 * nonblocking and hook it into the SocketEngine.
 * NB: If the operating system does not support nonblocking file
 * I/O (linux seems to, as does freebsd) this will default to
 * blocking behaviour.
 */
class CoreExport FileWriter : public EventHandler
{
 protected:
	/** The creator/owner of this object
	 */
	InspIRCd* ServerInstance;

	/** The log file (fd is inside this somewhere,
	 * we get it out with fileno())
	 */
	FILE* log;

	/** Buffer of pending log lines to be written
	 */
	std::string buffer;

	/** Number of write operations that have occured
	 */
	int writeops;

 public:
	/** The constructor takes an already opened logfile.
	 */
	FileWriter(InspIRCd* Instance, FILE* logfile);

	/** This returns false, logfiles are writeable.
	 */
	virtual bool Readable();

	/** Handle pending write events.
	 * This will flush any waiting data to disk.
	 * If any data remains after the fprintf call,
	 * another write event is scheduled to write
	 * the rest of the data when possible.
	 */
	virtual void HandleEvent(EventType et, int errornum = 0);

	/** Write one or more preformatted log lines.
	 * If the data cannot be written immediately,
	 * this class will insert itself into the
	 * SocketEngine, and register a write event,
	 * and when the write event occurs it will
	 * attempt again to write the data.
	 */
	void WriteLogLine(const std::string &line);

	/** Close the log file and cancel any events.
	 */
	virtual void Close();

	/** Close the log file and cancel any events.
	 * (indirectly call Close()
	 */
	virtual ~FileWriter();
};



/*
 * New world logging!
 * The brief summary:
 *  Logging used to be a simple affair, a FILE * handled by a nonblocking logging class inheriting from EventHandler, that was inserted
 *  into the socket engine, and wrote lines. If nofork was on, it was printf()'d.
 *
 *  We decided to horribly overcomplicate matters, and create vastly customisable logging. LogManager and LogStream form the visible basis
 *  of the new interface. Basically, a LogStream can be inherited to do different things with logging output. We inherit from it once in core
 *  to create a FileLogStream, that writes to a file, for example. Different LogStreams can hook different types of log messages, and different
 *  levels of output too, for extreme customisation. Multiple LogStreams can hook the same message/levels of output, meaning that e.g. output
 *  can go to a channel as well as a file.
 *
 *  HOW THIS WORKS
 *   LogManager handles all instances of LogStreams, LogStreams (or more likely, derived classes) are instantiated and passed to it.
 */

/** LogStream base class. Modules (and other stuff) inherit from this to decide what logging they are interested in, and what to do with it.
 */
class CoreExport LogStream : public classbase
{
 protected:
	InspIRCd *ServerInstance;
	int loglvl;
 public:
	LogStream(InspIRCd *Instance, int loglevel) : loglvl(loglevel)
	{
		this->ServerInstance = Instance;
	}

	virtual ~LogStream() { }

	/** XXX document me properly.
	 * Used for on the fly changing of loglevel.
	 */
	void ChangeLevel(int lvl) { this->loglvl = lvl; }

	/** Called when there is stuff to log for this particular logstream. The derived class may take no action with it, or do what it
	 * wants with the output, basically. loglevel and type are primarily for informational purposes (the level and type of the event triggered)
	 * and msg is, of course, the actual message to log.
	 */
	virtual void OnLog(int loglevel, const std::string &type, const std::string &msg) = 0;
};

typedef std::map<FileWriter*, int> FileLogMap;

class CoreExport LogManager : public classbase
{
 private:
	bool Logging;														// true when logging, avoids recursion
	LogStream* noforkstream;											// LogStream for nofork.
	InspIRCd *ServerInstance;
	std::map<std::string, std::vector<LogStream *> > LogStreams;
	std::map<LogStream *, int> AllLogStreams;							// holds all logstreams
	std::vector<LogStream *> GlobalLogStreams;							//holds all logstreams with a type of *
	FileLogMap FileLogs;												// Holds all file logs, refcounted
 public:
	LogManager(InspIRCd *Instance)
	{
		ServerInstance = Instance;
		Logging = false;
	}

	void SetupNoFork();

	/** XXX document me properly. */
	void AddLoggerRef(FileWriter* fw)
	{
		FileLogMap::iterator i = FileLogs.find(fw);
		if (i == FileLogs.end())
		{
			FileLogs.insert(std::make_pair(fw, 1));
		}
		else
		{
			++i->second;
		}
	}

	/** XXX document me properly. */
	void DelLoggerRef(FileWriter* fw)
	{
		FileLogMap::iterator i = FileLogs.find(fw);
		if (i == FileLogs.end()) return; /* Maybe should log this? */
		if (--i->second < 1)
		{
			delete i->first;
			FileLogs.erase(i);
		}
	}

	/** XXX document me properly. */
	void OpenSingleFile(FILE* f, const std::string& type, int loglevel);
	
	/** XXX document me properly. */
	void OpenFileLogs();
	
	/** Gives all logstreams a chance to clear up (in destructors) while it deletes them.
	 */
	void CloseLogs();
	
	/** Registers a new logstream into the logging core, so it can be called for future events
	 * XXX document me properly.
	 */
	bool AddLogType(const std::string &type, LogStream *l, bool autoclose);
	
	/** Removes a logstream from the core. After removal, it will not recieve further events.
	 */
	void DelLogStream(LogStream* l);
	
	/** XXX document me properly. */
	bool DelLogType(const std::string &type, LogStream *l);
	
	/** Pretty self explanatory.
	 */
	void Log(const std::string &type, int loglevel, const std::string &msg);
	
	/** Duh.
	 */
	void Log(const std::string &type, int loglevel, const char *fmt, ...);
};

#endif