Prosody IM Website


file doc/developers/moduleapi.md in changeset cd4821e1c16a

View latest ↓ Download file

line wrap: on
line source

---
title: Prosody module API
---

# Module

## module:get\_name()

An API method for the forgetful.

## module:get\_host()

Returns the host this module is loaded on. It returns \'\*\' for global
modules, which is not a real host. You may also directly read the
current host from \'module.host\'. Since a module never moves between
hosts it is safe to cache this value in a local variable if you use it a
lot.

## module:get\_host\_type()

Returns the type of the current host. Possible values map to different
types of host defined in the config file. It returns \"local\" for a
normal VirtualHost, and \"component\" for one defined as a Component.

The main different between a VirtualHost and a Component is that users
log into one but not the other. A global module is not associated with a
host, and this method will return nil for such a module.

## module:set\_global()

This sets a module as global. A global module is not associated with any
particular host (module.host will be set to \'\*\').

If you have a module that needs to mix per-host activities (e.g. events)
with some global things (such as a listening port) then you should read
up on [shared modules](/doc/developers/global_modules#shared_modules).

## module:get\_directory()

Returns the file system directory that this module was loaded from. If
you want to load files from this directory, see
[module:load\_resource()](#module_load_resource_path_mode).

## module:load\_resource(path, mode) {#moduleload_resource_path_mode}

Opens a resource file and returns the Lua file handle, or nil and an
error message. `path` is the path to the file, if it is not absolute
then it is treated as relative to the module\'s main file, so that
`module:load_resource('data.json')` will open a file `data.json` from
the same directory as the module\'s `.lua` file.

# Logging

## module:log(level, message, \...) {#modulelog_level_message}

Sends a message to Prosodys log. Available log levels are `debug`,
`info`, `warn` and `error`. The `message` argument and any additional
arguments work like
[printf](http://www.lua.org/manual/5.1/manual.html#pdf-string.format).

# Configuration

## module:get\_option(option\_name, default\_value) {#moduleget_option_option_name_default_value}

This retrieves a value from the config file, defaulting to the value
specified for the current host if any. Otherwise it looks for the value
in the global section of the config. Finally it returns default\_value
if that is specified.

There are a number of variants of this method that should be used for
specific data types. These all have the same syntax as
module:get\_option(), but are accessed as module:get\_option\_**type**()
(replacing **type** by the name of the type you expect). They
automatically convert between types where applicable, and warn the user
if the value in the config file is incorrect. Available types are:

-   `string`
-   `number`
-   `boolean`
-   `array` (see [util.array](/doc/developers/util/array))
-   `set` (see [util.set](/doc/developers/util/set))
-   `inherited_set` (see explanation below)

`module:get_option_inherited_set()` differs from the set variant in that
it returns the union of the global and host-specific options, if both
exists.

`module:get_option_path()` behaves similar to `string` but takes the
additional step of resolving a relative file path into an absolute one.
It takes a third argument `parent` in addition to `option_name` and
`default_value`, which specifies the parent directory if the
configuration option is a relative filename. If left empty, it will be
relative to the module directory. Other options are:

-   `"config"` -- the configuration directory
-   `"data"` -- the data directory
-   `"source"` -- the path prosody was installed into
-   or any directory name

# Events

## module:fire\_event(event\_name, data) {#modulefire_event_event_name_data}

Fires the specified event on the current host (or a global event if the
current module is global). If `data` is given it is passed to event
handlers. If any handler returns a value (that isn\'t `nil`) then
processing will halt and that value will be returned.

## module:hook(event\_name, handler, priority) {#modulehook_event_name_handler_priority}

Add a handler for the specified event on the current host, with the
current priority. The priority defaults to zero, but may be any number
(including negative and floating-point). Handlers with a higher priority
are executed first.

Returning any value other than nil will halt processing of the event,
and return that value to the code that fired the event.

Standard event names are documented in our [events
reference](/doc/developers/events).

## module:hook\_global(event\_name, handler, priority) {#modulehook_global_event_name_handler_priority}

The same as module:hook(), but hooks a global (server-wide) event.

For global modules this method is exactly equivalent to module:hook().

## module:hook\_object\_event(event\_name, handler, priority) {#modulehook_object_event_event_name_handler_priority}

Less-commonly needed, this variant allows you to add a hook to any
[util.events](/doc/developers/util/events) object. That is, any object
with compatible add\_handler() and remove\_handler() functions.

It is better to use this method than manually adding a handler to an
events object with events.add\_handler() so that the handler will be
correctly removed when the module is unloaded.

## module:hook\_tag(xmlns, name, handler, priority) {#modulehook_tag_xmlns_name_handler_priority}

A convenience function to build the correct event string to hook a
stream-level element, such as those used in negotiation of stream
features. The \'xmlns\' and \'name\' parameters specify the namespace
and tag name of the element. The \'handler\' and \'priority\' parameters
are equivalent to module:hook()\'s.

## module:unhook(event\_name, handler) {#moduleunhook_event_name_handler}

Remove a handler for a given event, that was previously added by one of
the module:hook() methods.

This method was added in Prosody 0.10.

# Communication

## module:send(stanza, origin) {#modulesend_stanza}

Sends a stanza from the current host. Must have valid \'to\' and
\'from\' addresses. Uses stanza\_router.core\_post\_stanza() internally.

The optional `origin` argument gives the session that originated the
stanza. It defaults to the host the module is loaded on.

## module:send\_iq(stanza, origin, timeout)

Sends an IQ stanza and keeps track of the response using a
[promise](/doc/developers/util/promise) that is either resolved with an
event payload or rejected with an
[`util.error`](/doc/developers/util/error) object.

The optional `origin` argument works like with `module:send()`.

There is a limit of 256 in flight stanzas at the same time. After this
the oldest IQ stanza promise is rejected.

The optional `timeout` argument changes the timeout before the promise
is rejected. Defaults to 120 seconds.

The stanzas must have unique `id` attributes.

```lua
local new_id = require"util.id".medium;
local iq = st.iq({ type = "get", id = new_id(),
    to = "example.com", from = "localhost" })
module:send_iq(iq)
    :next(function(response_event)
            module:log("info", "Got pong: %s", response_event.stanza);
        end,
        function(response_event)
            module:log("error", "Ping failed: %s", response_event);
        end);

```

This method was added in Prosody trunk, to be included in 0.11+1.

# Timers

## module:add\_timer(delay, callback) {#moduleadd_timer_delay_callback}

Triggers callback after the specified delay (in seconds, but may be
contain a fraction part). If the callback returns a number, this is used
as a new delay, and the timer repeats.

The timer is removed when the module is unloaded.

# Sharing code and data {#sharing_code_and_data}

## module:require(lib\_name) {#modulerequire_lib_name}

Loads a plugin library, \"lib\_name.lib.lua\", using the same search
paths as for normal modules. The library is loaded into the current
module\'s environment, giving it access to this module API and any other
global variables in the module.

## module:depends(module\_name) {#moduledepends_module_name}

Ensures that another module is loaded before this one. If it is not
loaded, loads it.

## module:shared(\...) {#moduleshared}

Creates one or more shared tables with the specified names. All shared
tables are at virtual paths of the form:

``` {.code}
  /host/module/name
```

However you may specify paths relative to the current host and module.
Some examples:

``` {.code .lua}
-- Get a shared table called 'sessions' for this module on this host:
local sessions = module:shared("sessions") -- shared at "/current_host/current_module/sessions"
-- Get a shared table called 'sessions' for a module named 'xmpp' on this host:
local sessions = module:shared("xmpp/sessions");
-- Get a table called 'sessions' shared by a global module called 'notify':
local sessions = module:shared("/*/notify/sessions");
```

There is no way to set a shared table, an empty table is created
automatically for the first module to access it. Shared tables are
automatically released when no loaded modules are using them.

## module:context(host) {#modulecontext_host}

Produces a fake API context for a given host (or global). E.g.
`module:context("*"):get_option("foo")` to get global options.

# Service discovery {#service_discovery}

## module:add\_feature(var) {#moduleadd_feature_var}

Adds a [XEP-0030: Service
Discovery](http://xmpp.org/extensions/xep-0030.html) feature to the
current host.

This is to signal to a client that Prosody supports a certain XMPP
extension, for example. These features are usually given in the
\"Discovering support\" section of the relevant XEPs. A full list of all
official features in specs published by the XSF can be found at [XSF
Registrar: Service Discovery
Features](http://xmpp.org/registrar/disco-features.html).

If making your own extension, use a URL of a site that you own. e.g. for
Prosody we use URLs of the form http://prosody.im/protocol/\* .

## module:add\_identity(category, type, name) {#moduleadd_identity_category_type_name}

Adds a [XEP-0030: Service
Discovery](http://xmpp.org/extensions/xep-0030.html) identity to the
current host. Identities are similar to features, but rather than
telling clients what the host *supports* it says what the host *is*.
XEPs inform you when a host needs to have a certain identity. A full
list of all official identities published by the XSF can be found at
[XSF Registrar: Service Discovery
Categories](http://xmpp.org/registrar/disco-categories.html).

## module:add\_extension(data) {#moduleadd_extension_data}

Adds a [XEP-0128: Service Discovery
Extensions](http://xmpp.org/extensions/xep-0128.html) object to the
current host. This must be a util.stanza object in the correct format.
See the XEP for further details.

# Publishing items {#publishing_items}

## module:add\_item(array\_name, item) {#moduleadd_item_array_name_item}

Every host supports generic arrays of different kinds, to allow modules
to add things like disco features and other data. Added items are
automatically removed from the array on unload.

This method adds \'item\' to the array with the given \'array\_name\'
and fires \"item-added/\<key\>\" on the current host.

## module:remove\_item(array\_name, value) {#moduleremove_item_array_name_value}

Remove the given value from the array and fires \"item-removed/\<key\>\"
on the current host.

## module:handle\_items(array\_name, add\_handler, remove\_handler, handle\_existing) {#modulehandle_items_array_name_add_handler_remove_handler_handle_existing}

A convenience function for modules that want to watch for item adds and
removes on a host.

Simply specify a handler to receive the item-added event, and one to
received the item-removed. event. By default the \'add\_handler\' will
also be called for any items that were added prior to this function
being called. Set \'handle\_existing\' to false to ignore existing items
in the array and only receive notifications of future adds and removes.

## module:get\_host\_items(array\_name) {#moduleget_host_items_array_name}

Returns an array of all items added to the host array with the specified
name. This includes items added by global modules.

## module:provides(name, item) {#moduleprovides_name_item}

This related function is a high-level wrapper around add\_item(). The
item is added to the array \'\<name\>-provider\' (e.g. \'auth-provider\'
for module:provides(\'auth\', ...)).

Also item.name is set to the name of the current module if it is not set
already. If the module name is prefixed by \'name\' (e.g. mod\_auth\_foo
and name == \"auth\") then that prefix is first stripped (so that
item.name ends up as \"foo\").

# Storage

## module:open\_store(store, type) {#moduleopen_store_store_type}

Opens a data store through the Prosody storage API.

-   `store`: the name of the store to open (modules can create their own
    stores).
-   `type`: The type of the store. Common values are:
    -   `keyval`: Simple key-value storage, most commonly supported by
        storage modules and the **default**.
    -   `map`: Similar to `keyval` but with two level keys.
    -   `archive`: Ordered key-value list storage.

The return value is an object that is used to read and write to the
store. Available methods depend on the store type and what is
implemented by the storage module providing it.

## keyval store methods {#keyval_store_methods}

The key field is usually an username, but this is not required.

-   `:get(username)`: Returns the data stored under this username/key,
    `nil` if the store is empty or `nil, "error message` on errors.
-   `:set(username, value)`: Stores the `value` data under the specified
    key. Returns `true` on success or `nil, "error message"`.
-   `:users()`: Returns an iterator over all available keys, if
    implemented. *Not provided by all modules.*

## map store methods {#map_store_methods}

-   `:get(key, subkey)`
-   `:set(key, subkey, value)`
-   `:set_keys(key, { key = value })`

## archive store methods {#archive_store_methods}

-   `:append(username, key, value, when, with)`: Store a value, which
    can be an `util.stanza` object.
    -   `key` is the id of the value, which must be unique in the
        archive.
    -   `value` is the value, which may be an `util.stanza` object.
    -   `when` is a time stamp attached to the value
    -   `with` is a metadata string
-   `:find(username, query)`: Find items matching the query, a table
    with the following possible fields:
    -   `start` and `end`: Matches items on the `with` field, inclusive.
    -   `with`: Matches the `with` field given to `:append()`.
    -   `before` and `after`: Specifies a range of items by `key` field,
        not inclusive.
    -   `total`: Request that a count of the total number of matching
        items be returned. *Not guaranteed to be supported*
    -   `limit`: Maximum number of items to return. Use `before` or
        `after` to page.
    -   `reverse`: Boolean true to get items in reverse chronological
        order.

# Metrics {#metrics}

Methods for reporting metrics from modules. For more information see
[Statistics](/doc/statistics). Prosody 0.10+ required.

In Prosody 0.12, a new, lower-level API to gather metrics was introduced. That
new way is modelled closely to the [OpenMetrics](https://openmetrics.io) data
model.

When measuring a quantity, you will have to decide whether to use the
high-level API (which is easier to use, but does not have as much flexibility)
or the low-level API. You can mix low- and high-level API in the same module
and migrate seamlessly from high- to low-level, but not vice versa.

You should use the low-level API if either of the below holds true:

- You need labels (key/value pairs) on your metrics to distinguish different
  aspects of the same thing in an aggregatable way.

  E.g.: If you are counting stanzas, you might want a `stanza_kind` label on
  the metric which gets values like `message`, `iq`, ...

- You need a histogram for things other than timings or sizes.

- You want or need fine-grained control over the OpenMetrics types and
  settings used.

Note that the low-level API is only available starting with Prosody 0.12.

Otherwise, the high-level API may very well be enough for you.

The low-level API directly exposes concepts from OpenMetrics and is available
as [`module:metric()`](#metric).

The high-level API is the API already known from Prosody before 0.12 as
[`module:measure()`](#measure).

## module:measure(name, type, conf) {#measure}

This function allows you to report useful metrics from your module. The
`type` parameter should be one of:

Type           Description                                                                    Examples
----           -----------                                                                    --------
`amount`       Report the number of something that varies over time                           Current memory used
`counter`      Similar to `amount`, but used when the number always goes up and down in steps The number of connected users
`rate`         Count the number of times something happened, when the total is not tracked    Stanzas per second
`times`        Time how long something takes (in seconds)                                     Time taken to execute a function, time before a HTTP request is responded to
`sizes`        Measure the size (in bytes) of an item being processed                         Size of a stanza, or size of a HTTP response
`distribution` Measure the distribution of any series of values                               Number of connected resources a user has

The `name` parameter should be a name for the metric, which begins with
a letter and has only letters, numbers or underscores (\'\_\') following
that.

Since Prosody 0.12, the optional `conf` parameter is supported. It must be a
table or absent. If it is a table, the following keys are supported:

- `unit` (optional): A string which indicates the unit of measurement of the
  metric (e.g. `"seconds"`, `"bytes"`).
- `description` (optional): A human-readable, tooltip-like string to expose
  as a "help" text together with the metric.

Some measurement types have additional options (such as `"times"` and
`"sizes"`).

This method was added in Prosody 0.10. Since Prosody 0.12, this is a wrapper
around the interface exposed by [`module:metric()`](#metric). The
module:metric() API offers more features at the expense of ease of use.

## module:measure(name, \"amount\", conf) {#measure_amount}

Creates a metric to represent the current amount of something.

Returns a function that you can call to update the metric value.

Use cases:

- Current number of active s2s connections
- Number of registered users
- Duration of the last run of a "rare" action (such as MAM cleanup, HTTP
  upload expiry)

Anti use cases:

- Total number of connections ever accepted; use a `"counter"` or `"rate"`
  instead.
- Duration of a short-running task (e.g. an event); use a `"times"` metric
  instead.

**Example:**

``` {.code .lua}
   local measure_memory = module:measure("memory_used", "amount");

   -- Example, get the current memory usage
   local memory_used = get_current_memory_usage();

   -- Update the metric with the current memory usage
   measure_memory(memory_used);
```

## module:measure(name, \"counter\", conf) {#measure_counter}

Creates a \'counter\' metric.

A counter metric can only ever increase; decreasing a counter is an invalid
operation which may lead to issues in downstream monitoring systems.

Returns a function that you can call to increase the metric.

The `conf` table accepts an additional key:

- `initial` (optional): If given, it must be a number. The metric will be
  initialized at that value during startup.

  Do not use unless you have a good reason to.

`conf` may also be a number, which will be treated the same as `initial`,
for backward compatibility.

Use cases:

- Total number of stanzas processed
- Total number of bytes uploaded

Anti use cases:

- Number of currently established connections; use `"amount"`

**Example:**

``` {.code .lua}
   local measure_connections = module:measure("connections", "counter");

   -- Increase the number of connections by 1
   local sock = accept(); -- accept a new connection
   measure_connections(1); -- and then increase the metric by one to record it
```

## module:measure(name, \"rate\", conf) {#measure_rate}

Creates a \'rate\' metric. This is an alias of \'counter\' for backward
compatibility.

Returns a function that you call when the event that you want to count
happens.

**Example:**

``` {.code .lua}
   -- Create a metric to measure the request rate (requests per second)
   local measure_request_rate = module:measure("requests", "rate");

   function handle_request()
       -- Update our metric to indicate a new request was received
       measure_request_rate();
       -- Handle the request
   end
```

## module:measure(name, \"times\", conf) {#measure_times}

Creates a \'times\' metric.

Returns a function that you can call to start timing something. That
function returns another function that you must call to stop timing. If
the second function is never called, the time is not recorded in the
metric.

The `conf` table accepts an additional key:

- `buckets` (optional): If given, must be a sorted (ascendingly) array of
  bucket thresholds. The `"times"` metric is internally a histogram. The
  buckets represent the upper bounds of each bucket.

  The default implementation is suitable for measuring things which normally
  run quickly but may run long in some rare cases. It thus provides a
  logarithmic scale covering 0.001s through 100.0s.

  Customization should rarely be necessary (see "maybe use cases" below).

The `conf.unit` argument is ignored and the unit is forced to be `"seconds"`.

Use cases:

- Record the time it takes to process an event

Anti use cases:

- Timing seldom (such as HTTP upload expiry) tasks; use an `"amount"` to
  record the duration of the last run instead.

Maybe use cases:

- Timing long-lived things (such as S2S connection lifetimes); not really an
  easy thing to measure. Look into `"distribution"` as an alternative or tune
  the `buckets` of the `"times"` to fit your use case.

**Example:**

``` {.code .lua}
   local measure_request_time = module:measure("request_time", "times");

   local mark_request_completed = measure_request_time(); -- Start timing here, and save the 'stop' function
   do_request("https://example.com/", function ()
       -- Request completed!
       mark_request_completed(); -- Stop timing and record the result in the metric
   end);
```

## module:measure(name, \"sizes\", conf) {#measure_sizes}

Creates a \'sizes\' metric.

Returns a function that you can call to measure the size of an item in a
series of items.

The `conf` table accepts an additional key:

- `buckets` (optional): If given, must be a sorted (ascendingly) array of
  bucket thresholds. The `"sizes"` metric is internally a histogram. The
  buckets represent the upper bounds of each bucket.

  The default implementation is suitable for measuring things of various
  orders of magnitude with rather low precision. It offers less than a dozen
  buckets between 1kiB and 1GiB.

  Customization may be necessary depending on what is being measured.

The `conf.unit` argument is ignored and the unit is forced to be `"bytes"`.

Use cases:

- Record payload sizes (HTTP requests, stanzas (or their bodies), ...)

Anti use cases:

- Record the amount of data sent over a link; use a `"rate"` or `"counter"`
  instead.

**Example:**

``` {.code .lua}
   local measure_stanza_size = module:measure("stanza_sizes", "sizes");

   function handle_stanza(stanza)
       measure_stanza_size(#stanza); -- Record the size (in bytes) of this stanza
   end
```

## module:measure(name, \"distribution\", conf) {#measure_distribution}

Creates a \'distribution\' metric.

Returns a function that you can call to measure a value in a series of
values.

`conf` may also be a string instead of a table; in that case, the string is
taken as the `conf.unit`. This exists for backward compatibility reasons.

**Example:**

``` {.code .lua}
   local measure_resources = module:measure("resources", "distribution", "resources");

   -- Record how many connected resources a user has
   measure_resources(#user_info.resources);
```

## module:metric(type, name, unit, description, labels, extra)  {#metric}

Declare and return a metric family for use within this module.

- `type_`: The type of the metric. Must be either `"gauge"`, `"counter"`,
  `"histogram"` or `"summary"`.
- `name`: The name of the metric. Note that the OpenMetrics suffixes for the
  unit and type *must not* be included here, as they will be added by
  the backend.
- `unit` (optional): The unit of the thing the metric measures (e.g.
  `seconds`). Set to `nil` or `""` if no unit should be declared.
- `description` (optional): Human-readable, tooltip-like text which
  describes what the metric is about.
- `labels` (optional): Array of label keys which should be part of the
  metric family. Label keys are scoped to each metric. Defaults to the empty
  array.

  ::: {.alert .alert-warning}
  *Warning:* Each label **value** will create a new metric internally and in
  downstream monitoring systems. You MUST NOT use attacker controlled strings
  (such as Service Discovery features, xml namespaces, ...) or
  high-cardinality values (such as user names) as label values, unless you
  like resource exhaustion denial of service attacks.
  :::

- `extra`: Additional options to pass to the metric type or the backend.

  Histogram metrics require a key `buckets` inside extra containing a sorted
  list of thresholds (without the `+Inf` threshold, which is added
  automatically).

If the module is not *global*, an implicit `"host"` label will be added to the
`labels`. Its value will be fixed to the name of the current host.

If the module is *global* and you want to expose per-host metrics, you have
to add the `"host"` label to the `labels` argument yourself and forward the
host name to `:with_labels()` accordingly.

The `name` of the metric is prefixed with `prosody_mod_`, followed by the
module name, followed by a `/`. This avoids metric name conflicts between
modules.

If more control over the naming is required (e.g. to comply with
specifications building on top of the OpenMetrics I-D), the
[`statsmanager` API](/doc/developers/core/statsmanager#metric) has to be used
directly.

Notes:

- Support for OpenMetrics and this function was added in Prosody 0.12+.
- See the [Prosody OpenMetrics API](/doc/developers/util/openmetrics#metric-family)
  for more information on the OpenMetrics implementation and the interface
  provided by the returned object.
- See also [Statistics](/doc/developers/statsmanager) for general developer
  information regarding measurements and metrics in Prosody.