Writing an Event Listener

An event listener implementation is a program that is willing toaccept structured input on its stdin stream and produce structuredoutput on its stdout stream. An event listener implementation shouldoperate in “unbuffered” mode or should flush its stdout every time itneeds to communicate back to the supervisord process. Event listenerscan be written to be long-running or may exit after a single request(depending on the implementation and the autorestart parameter inthe eventlistener’s configuration).

An event listener can send arbitrary output to its stderr, which willbe logged or ignored by supervisord depending on the stderr-relatedlogfile configuration in its [eventlistener:x] section.

Event Notification Protocol

When supervisord sends a notification to an event listener process,the listener will first be sent a single “header” line on itsstdin. The composition of the line is a set of colon-separated tokens(each of which represents a key-value pair) separated from each otherby a single space. The line is terminated with a \n (linefeed)character. The tokens on the line are not guaranteed to be in anyparticular order. The types of tokens currently defined are in thetable below.

Header Tokens
KeyDescriptionExample
verThe event system protocol version3.0
serverThe identifier of the supervisord sending theevent (see config file [supervisord]section identifier value.
serialAn integer assigned to each event. No twoevents generated during the lifetime ofa supervisord process will havethe same serial number. The value is usefulfor functional testing and detecting eventordering anomalies.30
poolThe name of the event listener pool whichgenerated this event.myeventpool
poolserialAn integer assigned to each event by theeventlistener pool which it is being sentfrom. No two events generated by the sameeventlister pool during the lifetime of asupervisord process will have thesame poolserial number. This value canbe used to detect event ordering anomalies.30
eventnameThe specific event type name (seeEvent Types)TICK_5
lenAn integer indicating the number of bytes inthe event payload, aka the PAYLOAD_LENGTH22

An example of a complete header line is as follows.

  1. ver:3.0 server:supervisor serial:21 pool:listener poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT len:54

Directly following the linefeed character in the header is the eventpayload. It consists of PAYLOADLENGTH bytes representing aserialization of the event data. See [_Event Types](http://supervisord.org/#event-types) for thespecific event data serialization definitions.

An example payload for a PROCESS_COMMUNICATION_STDOUT eventnotification is as follows.

  1. processname:foo groupname:bar pid:123
  2. This is the data that was sent between the tags

The payload structure of any given event is determined only by theevent’s type.

Event Listener States

An event listener process has three possible states that aremaintained by supervisord:

NameDescription
ACKNOWLEDGEDThe event listener has acknowledged (acceptedor rejected) an event send.
READYEvent notificatons may be sent to this eventlistener
BUSYEvent notifications may not be sent to thisevent listener.

When an event listener process first starts, supervisor automaticallyplaces it into the ACKNOWLEDGED state to allow for startupactivities or guard against startup failures (hangs). Until thelistener sends a READY\n string to its stdout, it will stay inthis state.

When supervisor sends an event notification to a listener in theREADY state, the listener will be placed into the BUSY stateuntil it receives an OK or FAIL response from the listener, atwhich time, the listener will be transitioned back into theACKNOWLEDGED state.

Event Listener Notification Protocol

Supervisor will notify an event listener in the READY state of anevent by sending data to the stdin of the process. Supervisor willnever send anything to the stdin of an event listener process whilethat process is in the BUSY or ACKNOWLEDGED state. Supervisorstarts by sending the header.

Once it has processed the header, the event listener implementationshould read PAYLOAD_LENGTH bytes from its stdin, perform anarbitrary action based on the values in the header and the data parsedout of the serialization. It is free to block for an arbitrary amountof time while doing this. Supervisor will continue processingnormally as it waits for a response and it will send other events ofthe same type to other listener processes in the same pool asnecessary.

After the event listener has processed the event serialization, inorder to notify supervisord about the result, it should send back aresult structure on its stdout. A result structure is the word“RESULT”, followed by a space, followed by the result length, followedby a line feed, followed by the result content. For example,RESULT2\nOK is the result “OK”. Conventionally, an eventlistener will use either OK or FAIL as the result content.These strings have special meaning to the default result handler.

If the default result handler receives OK as result content, itwill assume that the listener processed the event notificationsuccessfully. If it receives FAIL, it will assume that thelistener has failed to process the event, and the event will berebuffered and sent again at a later time. The event listener mayreject the event for any reason by returning a FAIL result. Thisdoes not indicate a problem with the event data or the event listener.Once an OK or FAIL result is received by supervisord, theevent listener is placed into the ACKNOWLEDGED state.

Once the listener is in the ACKNOWLEDGED state, it may either exit(and subsequently may be restarted by supervisor if itsautorestart config parameter is true), or it may continuerunning. If it continues to run, in order to be placed back into theREADY state by supervisord, it must send a READY tokenfollowed immediately by a line feed to its stdout.

Example Event Listener Implementation

A Python implementation of a “long-running” event listener whichaccepts an event notification, prints the header and payload to itsstderr, and responds with an OK result, and then subsequently aREADY is as follows.

  1. import sys
  2.  
  3. def write_stdout(s):
  4. # only eventlistener protocol messages may be sent to stdout
  5. sys.stdout.write(s)
  6. sys.stdout.flush()
  7.  
  8. def write_stderr(s):
  9. sys.stderr.write(s)
  10. sys.stderr.flush()
  11.  
  12. def main():
  13. while 1:
  14. # transition from ACKNOWLEDGED to READY
  15. write_stdout('READY\n')
  16.  
  17. # read header line and print it to stderr
  18. line = sys.stdin.readline()
  19. write_stderr(line)
  20.  
  21. # read event payload and print it to stderr
  22. headers = dict([ x.split(':') for x in line.split() ])
  23. data = sys.stdin.read(int(headers['len']))
  24. write_stderr(data)
  25.  
  26. # transition from READY to ACKNOWLEDGED
  27. write_stdout('RESULT 2\nOK')
  28.  
  29. if __name__ == '__main__':
  30. main()

Other sample event listeners are present within the superlancepackage, including one which can monitor supervisor subprocesses andrestart a process if it is using “too much” memory.

Event Listener Error Conditions

If the event listener process dies while the event is beingtransmitted to its stdin, or if it dies before sending an resultstructure back to supervisord, the event is assumed to not beprocessed and will be rebuffered by supervisord and sent again later.

If an event listener sends data to its stdout which supervisor doesnot recognize as an appropriate response based on the state that theevent listener is in, the event listener will be placed into theUNKNOWN state, and no further event notifications will be sent toit. If an event was being processed by the listener during this time,it will be rebuffered and sent again later.

Miscellaneous

Event listeners may use the Supervisor XML-RPC interface to call “backin” to Supervisor. As such, event listeners can impact the state of aSupervisor subprocess as a result of receiving an event notification.For example, you may want to generate an event every few minutesrelated to process usage of Supervisor-controlled subprocesses, and ifany of those processes exceed some memory threshold, you would liketo restart it. You would write a program that caused supervisor togenerate PROCESS_COMMUNICATION events every so often with memoryinformation in them, and an event listener to perform an action basedon processing the data it receives from these events.