The uWSGI alarm subsystem (from 1.3)

As of 1.3, uWSGI includes an alarm system. This subsystem allows thedeveloper/sysadmin to ‘announce’ special conditions of an app via variouschannels. For example, you may want to get notified via Jabber/XMPP of a fulllisten queue, or a harakiri condition. The alarm subsystem is based ontwo components: an event monitor and an event action.

An event monitor is something waiting for a specific condition (like an event on a file descriptor or a specific log message).

As soon as the condition is true an action (like sending an email) is triggered.

Embedded event monitors

Event monitors can be added via plugins, the uWSGI core includes the following:

  • log-alarm triggers an alarm when a specific regexp matches a log line
  • alarm-fd triggers an alarm when the specified file descriptor is ready (which is pretty low-level and the basis of most of the alarm plugins)
  • alarm-backlog triggers an alarm when the socket backlog queue is full
  • alarm-segfault (since 1.9.9) triggers an alarm when uWSGI segfaults.
  • alarm-cheap Use main alarm thread rather than creating dedicated threads for each curl-based alarm

Defining an alarm

You can define an unlimited number of alarms. Each alarm has a unique name.

Currently the following alarm actions are available in the main distribution:

  1. 'cmd' - run a command passing the log line to the stdin
  2. 'signal' - generate an uWSGI signal
  3. 'mule' - send the log line to a mule
  4. 'curl' - pass the log line to a curl url (http,https and smtp are supported)
  5. 'xmpp' - send the log line via XMPP/jabber

To define an alarm, use the option —alarm.

  1. --alarm "<name> <plugin>:<opts>"

Remember to quote ONLY when you are defining alarms on the command line.

  1. [uwsgi]
  2. alarm = mailme cmd:mail -s 'uWSGI alarm' -a 'From: foobar@example.com' admin@example.com
  3. alarm = cachefull signal:17

Here we define two alarms: mailme and cachefull. The first one invokesthe mail binary to send the log line to a mail address; the second onegenerates an uWSGI signal. We now need to add rules to trigger alarms:

  1. [uwsgi]
  2. alarm = mailme cmd:mail -s 'uWSGI alarm' -a 'From: foobar@example.com' admin@example.com
  3. alarm = cachefull signal:17
  4. log-alarm = cachefull,mailme uWSGI listen queue of socket
  5. log-alarm = mailme HARAKIRI ON WORKER

The syntax of log-alarm is

  1. --log-alarm "<name> <regexp>"

In our previous example we defined two conditions using regexps applied to loglines. The first one will trigger both alarms when the listen queue is full,while the second will only invoke ‘mailme’ when a worker commits harakiri.

Damnit, this… this is the rawest thing I’ve seen…

You may be right. But if you throw away your “being a cool programmer with alot of friends and zero money” book for a moment, you will realize just howmany things you can do with such a simple system. Want an example?

  1. [uwsgi]
  2. alarm = jabber xmpp:foobar@jabber.xxx;mysecretpassword;admin@jabber.xxx,admin2@jabber.xxx
  3. log-alarm = jabber ^TERRIBLE ALARM

Now in your app you only need to add

  1. print "TERRIBLE ALARM! The world exploded!!!"

to send a Jabber message to admin@jabber.xxx and admin2@jabber.xxxwithout adding any significant overhead to your app (as alarms are triggered byone or more threads in the master process, without bothering workers).

How about another example?

Check this Rack middleware:

  1. class UploadCheck
  2. def initialize(app)
  3. @app = app
  4. end
  5.  
  6. def call(env)
  7. if env['REQUEST_METHOD'] == 'POST' and env['PATH_INFO'] == '/upload'
  8. puts "TERRIBLE ALARM! An upload has been made!"
  9. end
  10. @app.call(env)
  11. end
  12. end

Protecting from bad rules

Such a versatile system could be open to a lot of ugly bugs, mainly infiniteloops. Thus, try to build your regexps carefully. The embedded anti-loopsubsystem should protect against loglines wrongly generated by alarm plugin.This system is not perfect so please double-check your regexps.

If you are building a plugin, be sure to prepend your log messages with the‘[uwsgi-alarm’ string. These lines will be skipped and directly passed to thelog subsystem. A convenience API function is available: uwsgi_log_alarm().

How does log-alarm work?

Enabling log-alarm automatically puts the uWSGI instance in log-mastermode, delegating log writes to the master. The alarm subsystem is executed bythe master just before passing the log line to the log plugin. Blocking alarmplugins should run in a thread (like the curl and xmpp one), while the simpleones (like signal and cmd) may run directly in the master.

Available plugins and their syntax

cmd

Run a shell command, passing the log line to its stdin:

  1. cmd:<command>

signal

Raise an uWSGI signal.

  1. signal:[signum]

See also

The uWSGI Signal Framework

mule

Send the log line to a mule waiting for messages.

  1. mule:[mule_id]

See also

uWSGI Mules

curl

Send the log line to a cURL-able URL. This alarm plugin is not compiled in by default, so if you need to build it just run:

  1. python uwsgiconfig.py --plugin plugins/alarm_curl
  1. curl:<url>[;opt1=val1;opt2=val2]

url is any standard cURL URL, while the options currently exposed are

  • “auth_pass”
  • “auth_user”
  • “conn_timeout”
  • “mail_from”
  • “mail_to”
  • “method”
  • “ssl”
  • “subject”
  • “timeout”
  • “url”
  • “ssl_insecure”

So, for sending mail via SMTP AUTH:

  1. [uwsgi]
  2. plugins = alarm_curl
  3. alarm = test curl:smtp://mail.example.com;mail_to=admin@example.com;mail_from=uwsgi@example.com;auth_user=uwsgi;auth_pass=secret;subject=alarm from uWSGI !!!

Or we can use Gmail to send alarms:

  1. [uwsgi]
  2. plugins = alarm_curl
  3. alarm = gmail curl:smtps://smtp.gmail.com;mail_to=admin@example.com;auth_user=uwsgi@gmail.com;auth_pass=secret;subject=alarm from uWSGI !!!

Or to PUT the log line to an HTTP server protected with basic authentication:

  1. [uwsgi]
  2. plugins = alarm_curl
  3. alarm = test2 curl:http://192.168.173.6:9191/argh;auth_user=topogigio;auth_pass=foobar

Or to POST the log line to an HTTPS server with self-generated SSL certificate.

  1. [uwsgi]
  2. plugins = alarm_curl
  3. alarm = test3 curl:https://192.168.173.6/argh;method=POST;ssl_insecure=true

xmpp

Probably the most interesting one of the built-in bunch. You need the libgloox package to build the XMPP alarm plugin (on Debian/Ubuntu, apt-get install gloox-dev).

  1. python uwsgiconfig.py --plugin plugins/alarm_xmpp
  1. xmpp:<jid>;<password>;<recipients>

You can set multiple recipients using ‘,’ as delimiter.

  1. [uwsgi]
  2. plugins = alarm_xmpp
  3. alarm = jabber xmpp:app@example.it;secret1;foo1@foo.it,foo2@foo.it

An even more interesting thing still about the XMPP plugin is that you will see the Jabber account of your app going down when your app dies. :-)

Some XMPP servers (most notably the OSX Server one) requires you to bind to a resource. You can do thus by appending /resource to the JID.

  1. [uwsgi]
  2. plugins = alarm_xmpp
  3. alarm = jabber xmpp:max@server.local/uWSGI;secret1;foo1@foo.it,foo2@foo.it

speech

A toy plugin for OSX, used mainly for showing off Objective-C integration with uWSGI.It simply uses the OSX speech synthesizer to ‘announce’ the alarm.

  1. python uwsgiconfig.py --plugin plugins/alarm_speech
  1. [uwsgi]
  2. plugins = alarm_speech
  3. http-socket = :8080
  4. alarm = say speech:
  5. log-alarm = say .*

Turn on your speakers, run uWSGI and start listening…

airbrake

Starting with 1.9.9 uWSGI includes the —alarm-segfault option to raise analarm when uWSGI segfaults.

The airbrake plugin can be used to send segfault backtraces to airbrakecompatible servers. Like Airbrake itself and its open source clone errbit(https://github.com/errbit/errbit), Airbrake support is experimental and itmight not fully work in all cases.

  1. plugins = airbrake
  2. alarm = errbit airbrake:http://errbit.domain.com/notifier_api/v2/notices;apikey=APIKEY;subject=uWSGI segfault
  3. alarm-segfault = errbit

Note that alarm-segfault does not require the Airbrake plugin. A backtrace can just as well be sent using any other alarm plugin.