# Listeners

During the life cylce of a worker process, Sanic provides you with four(4) opportunities to inject an operation. This enables you to execute startup/teardown code as your server starts or closes.

  • before_server_start
  • after_server_start
  • before_server_stop
  • after_server_stop

The life cycle of a worker process looks like this:

event     <worker process starts>
listener      before_server_start
event             <server started>
listener              after_server_start

event                     <serving requests>
event                     <graceful shutdown initiated>
event                     <complete remaining requests>

listener              before_server_stop
event             <server stopped>
listener      after_server_stop
event     <process exits>

# Attaching a listener

The process to setup a function as a listener is similar to declaring a route.

The two injected arguments are the currently running Sanic() instance, and the currently running loop.

async def setup_db(app, loop):
    app.db = await db_setup()

app.register_listener(setup_db, "before_server_start")

The Sanic app instance also has a convenience decorator.

@app.listener("before_server_start")
async def setup_db(app, loop):
    app.db = await db_setup()

# Order of execution

Listeners are executed in the order they are declared during startup, and reverse order of declaration during teardown

Phase Order
before_server_start startup regular
after_server_start startup regular
before_server_stop shutdown reverse
after_server_stop shutdown reverse

Given the following setup, we should expect to see this in the console.

@app.listener("before_server_start")
async def listener_1(app, loop):
    print("listener_1")

@app.listener("before_server_start")
async def listener_2(app, loop):
    print("listener_2")

@app.listener("after_server_start")
async def listener_3(app, loop):
    print("listener_3")

@app.listener("after_server_start")
async def listener_4(app, loop):
    print("listener_4")

@app.listener("before_server_stop")
async def listener_5(app, loop):
    print("listener_5")

@app.listener("before_server_stop")
async def listener_6(app, loop):
    print("listener_6")

@app.listener("after_server_stop")
async def listener_7(app, loop):
    print("listener_7")

@app.listener("after_server_stop")
async def listener_8(app, loop):
    print("listener_8")
[INFO] Goin' Fast @ http://127.0.0.1:8000
listener_1
listener_2
listener_3
listener_4
[INFO] Starting worker
[INFO] Stopping worker
listener_6
listener_5
listener_8
listener_7
[INFO] Server Stopped

FYI

The practical result of this is that if the first listener in before_server_start handler setups a database connection, listeners that are registered after it can rely upon that connection being alive both when they are started and stopped.

MIT Licensed | Copyright © 2018-present Sanic Community Organization