blob: e167b26714f07bfb7fa9d3f881a9cbee63a20cd2 [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001package net.floodlightcontroller.debugevent;
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11import java.util.concurrent.ConcurrentHashMap;
12
13import org.slf4j.Logger;
14import org.slf4j.LoggerFactory;
15
16import net.floodlightcontroller.core.module.FloodlightModuleContext;
17import net.floodlightcontroller.core.module.FloodlightModuleException;
18import net.floodlightcontroller.core.module.IFloodlightModule;
19import net.floodlightcontroller.core.module.IFloodlightService;
20import net.floodlightcontroller.debugevent.web.DebugEventRoutable;
21import net.floodlightcontroller.restserver.IRestApiService;
22
23import com.google.common.collect.Sets;
24/**
25 * This class implements a central store for all events used for debugging the
26 * system. The basic idea is that given the functionality provided by this class,
27 * it should be unnecessary to resort to scraping through system DEBUG/TRACE logs
28 * to understand behavior in a running system.
29 *
30 * @author saurav
31 */
32public class DebugEvent implements IFloodlightModule, IDebugEventService {
33 protected static Logger log = LoggerFactory.getLogger(DebugEvent.class);
34
35 /**
36 * Every registered event type gets an event id, the value for which is obtained
37 * while holding the lock.
38 */
39 protected int eventIdCounter = 0;
40 protected Object eventIdLock = new Object();
41
42 private static final int PCT_LOCAL_CAP = 10; // % of global capacity
43 private static final int MIN_LOCAL_CAPACITY = 10; //elements
44
45 /**
46 * Event Information
47 */
48 public class EventInfo {
49 int eventId;
50 boolean enabled;
51 int bufferCapacity;
52 EventType etype;
53 String eventDesc;
54 String eventName;
55 String moduleName;
56 String moduleEventName;
57 Class<?> eventClass;
58 String[] metaData;
59
60 public EventInfo(int eventId, boolean enabled, int bufferCapacity,
61 EventType etype, Class<?> eventClass, String eventDesc,
62 String eventName, String moduleName, String... metaData) {
63 this.enabled = enabled;
64 this.eventId = eventId;
65 this.bufferCapacity = bufferCapacity;
66 this.etype = etype;
67 this.eventClass = eventClass;
68 this.eventDesc = eventDesc;
69 this.eventName = eventName;
70 this.moduleName = moduleName;
71 this.moduleEventName = moduleName + "/" + eventName;
72 this.metaData = metaData;
73 }
74
75 public int getEventId() { return eventId; }
76 public boolean isEnabled() { return enabled; }
77 public int getBufferCapacity() { return bufferCapacity; }
78 public EventType getEtype() { return etype; }
79 public String getEventDesc() { return eventDesc; }
80 public String getEventName() { return eventName; }
81 public String getModuleName() { return moduleName; }
82 public String getModuleEventName() { return moduleEventName; }
83 public String[] getMetaData() { return metaData; }
84 }
85
86 //******************
87 // Global stores
88 //******************
89
90 /**
91 * Event history for a particular event-id is stored in a circular buffer
92 */
93 protected class DebugEventHistory {
94 EventInfo einfo;
95 CircularBuffer<Event> eventBuffer;
96
97 public DebugEventHistory(EventInfo einfo, int capacity) {
98 this.einfo = einfo;
99 this.eventBuffer = new CircularBuffer<Event>(capacity);
100 }
101 }
102
103 /**
104 * Global storage for all event types and their corresponding event buffers.
105 * A particular event type is accessed by directly indexing into the array
106 * with the corresponding event-id.
107 */
108 protected DebugEventHistory[] allEvents =
109 new DebugEventHistory[MAX_EVENTS];
110
111 /**
112 * Global storage for all event ids registered for a module. The map is indexed
113 * by the module name and event name and returns the event-ids that correspond to the
114 * event types registered by that module (for example module 'linkdiscovery'
115 * may register events that have ids 0 and 1 that correspond to link up/down
116 * events, and receiving malformed LLDP packets, respectively).
117 */
118 protected ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>>
119 moduleEvents = new ConcurrentHashMap<String,
120 ConcurrentHashMap<String, Integer>>();
121
122 /**
123 * A collection of event ids that are currently enabled for logging
124 */
125 protected Set<Integer> currentEvents = Collections.newSetFromMap(
126 new ConcurrentHashMap<Integer,Boolean>());
127
128 //******************
129 // Thread local stores
130 //******************
131
132 /**
133 * Thread local storage for events
134 */
135 protected class LocalEventHistory {
136 int nextIndex;
137 int maxCapacity;
138 boolean enabled;
139 ArrayList<Event> eventList;
140
141 public LocalEventHistory(boolean enabled, int maxCapacity) {
142 this.nextIndex = 0;
143 this.maxCapacity = maxCapacity;
144 this.enabled = enabled;
145 this.eventList = new ArrayList<Event>();
146 }
147 }
148
149 /**
150 * Thread local event buffers used for maintaining event history local to
151 * a thread. Eventually this locally maintained information is flushed
152 * into the global event buffers.
153 */
154 protected final ThreadLocal<LocalEventHistory[]> threadlocalEvents =
155 new ThreadLocal<LocalEventHistory[]>() {
156 @Override
157 protected LocalEventHistory[] initialValue() {
158 return new LocalEventHistory[MAX_EVENTS];
159 }
160 };
161
162 /**
163 * Thread local cache for event-ids that are currently active.
164 */
165 protected final ThreadLocal<Set<Integer>> threadlocalCurrentEvents =
166 new ThreadLocal<Set<Integer>>() {
167 @Override
168 protected Set<Integer> initialValue() {
169 return new HashSet<Integer>();
170 }
171 };
172
173 //*******************************
174 // IEventUpdater
175 //*******************************
176
177 protected class EventUpdaterImpl<T> implements IEventUpdater<T> {
178 private final int eventId;
179
180 public EventUpdaterImpl(int evId) {
181 this.eventId = evId;
182 }
183
184 @Override
185 public void updateEventNoFlush(Object event) {
186 if (!validEventId()) return;
187 updateEvent(eventId, false, event);
188 }
189
190 @Override
191 public void updateEventWithFlush(Object event) {
192 if (!validEventId()) return;
193 updateEvent(eventId, true, event);
194 }
195
196 private boolean validEventId() {
197 if (eventId < 0 || eventId >= MAX_EVENTS) {
198 throw new IllegalStateException();
199 }
200 return true;
201 }
202
203 }
204
205 //*******************************
206 // IDebugEventService
207 //*******************************
208
209 @Override
210 public <T> IEventUpdater<T> registerEvent(String moduleName, String eventName,
211 String eventDescription, EventType et,
212 Class<T> eventClass, int bufferCapacity,
213 String... metaData) throws MaxEventsRegistered {
214 int eventId = -1;
215 synchronized (eventIdLock) {
216 eventId = Integer.valueOf(eventIdCounter++);
217 }
218 if (eventId > MAX_EVENTS-1) {
219 throw new MaxEventsRegistered();
220 }
221
222 // register event id for moduleName
223 if (!moduleEvents.containsKey(moduleName)) {
224 moduleEvents.put(moduleName, new ConcurrentHashMap<String, Integer>());
225 }
226 if (!moduleEvents.get(moduleName).containsKey(eventName)) {
227 moduleEvents.get(moduleName).put(eventName, new Integer(eventId));
228 } else {
229 int existingEventId = moduleEvents.get(moduleName).get(eventName);
230 log.error("Duplicate event registration for moduleName {} eventName {}",
231 moduleName, eventName);
232 return new EventUpdaterImpl<T>(existingEventId);
233 }
234
235 // create storage for event-type
236 boolean enabled = (et == EventType.ALWAYS_LOG) ? true : false;
237 EventInfo ei = new EventInfo(eventId, enabled, bufferCapacity,
238 et, eventClass, eventDescription, eventName,
239 moduleName, metaData);
240 allEvents[eventId] = new DebugEventHistory(ei, bufferCapacity);
241 if (enabled) {
242 currentEvents.add(eventId);
243 }
244
245 return new EventUpdaterImpl<T>(eventId);
246 }
247
248 private void updateEvent(int eventId, boolean flushNow, Object eventData) {
249 if (eventId < 0 || eventId > MAX_EVENTS-1) return;
250
251 LocalEventHistory[] thishist = this.threadlocalEvents.get();
252 if (thishist[eventId] == null) {
253 // seeing this event for the first time in this thread - create local
254 // store by consulting global store
255 DebugEventHistory de = allEvents[eventId];
256 if (de != null) {
257 boolean enabled = de.einfo.enabled;
258 int localCapacity = de.einfo.bufferCapacity * PCT_LOCAL_CAP/ 100;
259 if (localCapacity < 10) localCapacity = MIN_LOCAL_CAPACITY;
260 thishist[eventId] = new LocalEventHistory(enabled, localCapacity);
261 if (enabled) {
262 Set<Integer> thisset = this.threadlocalCurrentEvents.get();
263 thisset.add(eventId);
264 }
265 } else {
266 log.error("updateEvent seen locally for event {} but no global"
267 + "storage exists for it yet .. not updating", eventId);
268 return;
269 }
270 }
271
272 // update local store if enabled locally for updating
273 LocalEventHistory le = thishist[eventId];
274 if (le.enabled) {
275 long timestamp = System.currentTimeMillis();
276 long thisthread = Thread.currentThread().getId();
277 String thisthreadname = Thread.currentThread().getName();
278 if (le.nextIndex < le.eventList.size()) {
279 if (le.eventList.get(le.nextIndex) == null) {
280 le.eventList.set(le.nextIndex, new Event(timestamp, thisthread,
281 thisthreadname,
282 eventData));
283 } else {
284 Event e = le.eventList.get(le.nextIndex);
285 e.timestamp = timestamp;
286 e.threadId = thisthread;
287 e.threadName = thisthreadname;
288 e.eventData = eventData;
289 e.nullifyCachedFormattedEvent();
290 }
291 } else {
292 le.eventList.add(new Event(timestamp, thisthread, thisthreadname, eventData));
293 }
294 le.nextIndex++;
295
296 if (le.nextIndex >= le.maxCapacity || flushNow) {
297 // flush this buffer now
298 DebugEventHistory de = allEvents[eventId];
299 if (de.einfo.enabled) {
300 le.eventList = de.eventBuffer.addAll(le.eventList, le.nextIndex);
301 } else {
302 // global buffer is disabled - don't flush, disable locally
303 le.enabled = false;
304 Set<Integer> thisset = this.threadlocalCurrentEvents.get();
305 thisset.remove(eventId);
306 }
307 le.nextIndex = 0;
308 }
309 }
310 }
311
312 @Override
313 public void flushEvents() {
314 LocalEventHistory[] thishist = this.threadlocalEvents.get();
315 Set<Integer> thisset = this.threadlocalCurrentEvents.get();
316 ArrayList<Integer> temp = new ArrayList<Integer>();
317
318 for (int eventId : thisset) {
319 LocalEventHistory le = thishist[eventId];
320 if (le != null && le.nextIndex > 0) {
321 // flush this buffer now
322 DebugEventHistory de = allEvents[eventId];
323 if (de.einfo.enabled) {
324 le.eventList = de.eventBuffer.addAll(le.eventList, le.nextIndex);
325 } else {
326 // global buffer is disabled - don't flush, disable locally
327 le.enabled = false;
328 temp.add(eventId);
329 }
330 le.nextIndex = 0;
331 }
332 }
333 for (int eId : temp)
334 thisset.remove(eId);
335
336 // sync thread local currently enabled set of eventIds with global set.
337 Sets.SetView<Integer> sv = Sets.difference(currentEvents, thisset);
338 for (int eventId : sv) {
339 if (thishist[eventId] != null) {
340 thishist[eventId].enabled = true;
341 thisset.add(eventId);
342 }
343 }
344
345 }
346
347 @Override
348 public boolean containsModuleEventName(String moduleName, String eventName) {
349 if (!moduleEvents.containsKey(moduleName)) return false;
350 if (moduleEvents.get(moduleName).containsKey(eventName)) return true;
351 return false;
352 }
353
354 @Override
355 public boolean containsModuleName(String moduleName) {
356 return moduleEvents.containsKey(moduleName);
357 }
358
359 @Override
360 public List<DebugEventInfo> getAllEventHistory() {
361 List<DebugEventInfo> moduleEventList = new ArrayList<DebugEventInfo>();
362 for (Map<String, Integer> modev : moduleEvents.values()) {
363 for (int eventId : modev.values()) {
364 DebugEventHistory de = allEvents[eventId];
365 if (de != null) {
366 List<Map<String,String>> ret = new ArrayList<Map<String,String>>();
367 for (Event e : de.eventBuffer) {
368 ret.add(e.getFormattedEvent(de.einfo.eventClass,
369 de.einfo.moduleEventName));
370 }
371 moduleEventList.add(new DebugEventInfo(de.einfo, ret));
372 }
373 }
374 }
375 return moduleEventList;
376 }
377
378 @Override
379 public List<DebugEventInfo> getModuleEventHistory(String moduleName) {
380 if (!moduleEvents.containsKey(moduleName)) return Collections.emptyList();
381 List<DebugEventInfo> moduleEventList = new ArrayList<DebugEventInfo>();
382 for (int eventId : moduleEvents.get(moduleName).values()) {
383 DebugEventHistory de = allEvents[eventId];
384 if (de != null) {
385 List<Map<String,String>> ret = new ArrayList<Map<String,String>>();
386 for (Event e : de.eventBuffer) {
387 ret.add(e.getFormattedEvent(de.einfo.eventClass,
388 de.einfo.moduleEventName));
389 }
390 moduleEventList.add(new DebugEventInfo(de.einfo, ret));
391 }
392 }
393 return moduleEventList;
394 }
395
396 @Override
397 public DebugEventInfo getSingleEventHistory(String moduleName, String eventName,
398 int last) {
399 if (!moduleEvents.containsKey(moduleName)) return null;
400 Integer eventId = moduleEvents.get(moduleName).get(eventName);
401 if (eventId == null) return null;
402 DebugEventHistory de = allEvents[eventId];
403 if (de != null) {
404 int num = 1;
405 List<Map<String,String>> ret = new ArrayList<Map<String,String>>();
406 for (Event e : de.eventBuffer) {
407 if (num > last)
408 break;
409 Map<String, String> temp = e.getFormattedEvent(de.einfo.eventClass,
410 de.einfo.moduleEventName);
411 temp.put("#", String.valueOf(num++));
412 ret.add(temp);
413 }
414 return new DebugEventInfo(de.einfo, ret);
415 }
416 return null;
417 }
418
419 @Override
420 public void resetAllEvents() {
421 for (Map<String, Integer> eventMap : moduleEvents.values()) {
422 for (Integer evId : eventMap.values()) {
423 allEvents[evId].eventBuffer.clear();
424 }
425 }
426 }
427
428 @Override
429 public void resetAllModuleEvents(String moduleName) {
430 if (!moduleEvents.containsKey(moduleName)) return;
431 Map<String, Integer> modEvents = moduleEvents.get(moduleName);
432 for (Integer evId : modEvents.values()) {
433 allEvents[evId].eventBuffer.clear();
434 }
435 }
436
437 @Override
438 public void resetSingleEvent(String moduleName, String eventName) {
439 if (!moduleEvents.containsKey(moduleName)) return;
440 Integer eventId = moduleEvents.get(moduleName).get(eventName);
441 if (eventId == null) return;
442 DebugEventHistory de = allEvents[eventId];
443 if (de != null) {
444 de.eventBuffer.clear();
445 }
446 }
447
448 @Override
449 public List<String> getModuleList() {
450 List<String> el = new ArrayList<String>();
451 el.addAll(moduleEvents.keySet());
452 return el;
453 }
454
455 @Override
456 public List<String> getModuleEventList(String moduleName) {
457 if (!moduleEvents.containsKey(moduleName))
458 return Collections.emptyList();
459 List<String> el = new ArrayList<String>();
460 el.addAll(moduleEvents.get(moduleName).keySet());
461 return el;
462 }
463
464 //*******************************
465 // IFloodlightModule
466 //*******************************
467
468 @Override
469 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
470 Collection<Class<? extends IFloodlightService>> l =
471 new ArrayList<Class<? extends IFloodlightService>>();
472 l.add(IDebugEventService.class);
473 return l;
474 }
475
476 @Override
477 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
478 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
479 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
480 m.put(IDebugEventService.class, this);
481 return m;
482 }
483
484 @Override
485 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
486 ArrayList<Class<? extends IFloodlightService>> deps =
487 new ArrayList<Class<? extends IFloodlightService>>();
488 deps.add(IRestApiService.class);
489 return deps;
490 }
491
492 @Override
493 public void init(FloodlightModuleContext context)
494 throws FloodlightModuleException {
495 }
496
497 @Override
498 public void startUp(FloodlightModuleContext context)
499 throws FloodlightModuleException {
500 IRestApiService restService =
501 context.getServiceImpl(IRestApiService.class);
502 restService.addRestletRoutable(new DebugEventRoutable());
503 DebugEventAppender.setDebugEventServiceImpl(this);
504 }
505
506}