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.

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)

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 and status

module:log(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.

module:set_status(status_type, status_message, override)

Added in Prosody 0.12

Sets the current status of the module. This can be used to indicate persistent errors (e.g. the module isn’t working correctly), or to provide useful runtime information even if the module is healthy.

status_type can be one of the following strings: info, warn, or error.

The status_message should be a human-readable description of the module’s current status, which will be displayed to the server admin.

By default, if the provided status has a lower “severity” than the current status (e.g. if the current status is “error” and you’re trying to set an “info” status), the new status is ignored unless the override parameter is set to true.

When any module updates its status, the module-status/updated event is fired on the current host with an event object containing a name field with the module’s name.

module:log_status(level, message, ...)

Added in Prosody 0.12

The same as module:log(), except also persists the level and message as the module’s current status. This is useful when logging fatal errors so that the module can be observed as unhealthy (along with the reason).

module:get_status()

Added in Prosody 0.12

Gets the current module’s status as 3 return values: status_type, status_message and status_time. The first two correspond to the module:set_status() parameters with the same name. status_time is the timestamp when the status was last updated.

Configuration

module:get_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
  • integer
  • period
  • boolean
  • array (see util.array)
  • set (see util.set)
  • path
  • enum
  • 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

trunk additions

module:get_option_period turns strings of the form "3 weeks" into the equivalent number of seconds and returns that. The value false or the string never return infinity (i.e. math.huge).

module:get_option_integer is similar to the variant for number but removes any fractional part.

module:get_option_number, module:get_option_integer and module:get_option_period take additional parameters indicating the accepted interval for values:

local foo_limit = module:get_option_integer("foo_limit", 0, 1000);
assert(foo_limit >= 0 and foo_limit <= 1000);

module:get_option_enum(name, default, alt1, alt2, ...) accepts one of the provided arguments starting with the default as value and rejects any other, suitable when the user must choose from a limited set of values.

local conflict_handling = module:get_option_enum("on_conflict", "kick_old",
    "kick_new", "increment", "random");

Events

module:fire_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)

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.

module:hook_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)

Less-commonly needed, this variant allows you to add a hook to any 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)

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)

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

Communication

module:send(stanza, origin)

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 that is either resolved with an event payload or rejected with an 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.

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 0.12.0.

Timers

module:add_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.

In Prosody 0.11 and later, module:add_timer() returns a timer object with the following methods:

  • timer:stop() - cancel the timer
  • timer:reschedule(delay) - reschedule the timer for delay seconds from now

module:cron(task_spec)

Added in Prosody 0.12

Execute a task periodically. Although similar to timers, Prosody’s cron tasks are designed to execute just once in a given period (even if the server restarts, for example). They also run in an async context by default, making them suitable for long-running tasks that want to spread their work.

Rather than calling module:cron() directly, consider one of the convenience methods:

  • module:hourly(name, func)
  • module:daily(name, func)
  • module:weekly(name, func)

In all cases, name is a human-readable name for the task and func is a function that is called to perform your task. It will be passed two parameters - the task specification and the start time of the current task.

function my_task_func(task_spec, start_time)
    -- Do some interesting stuff
end

If you call module:cron() directly, you will need to provide a task spec object yourself. Here is an example:

{
    -- Optional fields help to identify the task
    id = "mod_clutterer/clean_up_clutter";
    name = "Clean up clutter";

    -- Required fields:
    when = "daily"; -- or "hourly" or "weekly"
    run = function (self, current_time)
        -- Do the cleaning!
    end;
}

Prosody will construct sensible values for any missing optional fields. Note that ‘id’ is scoped across all modules on the current host, and should be appropriately unique if provided.

Sharing code and data

module:require(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)

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

module:shared(...)

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

  /host/module/name

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

-- 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)

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

Service discovery

module:add_feature(var)

Adds a XEP-0030: Service Discovery 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.

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)

Adds a XEP-0030: Service Discovery 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.

module:add_extension(data)

Adds a XEP-0128: Service Discovery Extensions object to the current host. This must be a util.stanza object in the correct format. See the XEP for further details.

Publishing items

module:add_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)

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)

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)

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)

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)

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

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

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

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 when 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

Methods for reporting metrics from modules. For more information see Statistics.

In Prosody 0.12, a new, lower-level API to gather metrics was introduced. That new way is modelled closely to the OpenMetrics 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().

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

module:measure(name, type, conf)

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").

Since Prosody 0.12, this is a wrapper around the interface exposed by module:metric(). The module:metric() API offers more features at the expense of ease of use.

module:measure(name, "amount", conf)

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:

   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)

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:

   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)

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:

   -- 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)

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:

   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)

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:

   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)

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:

   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)

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.

    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 has to be used directly.

Notes:

  • Support for OpenMetrics and this function was added in Prosody 0.12+.
  • See the Prosody OpenMetrics API for more information on the OpenMetrics implementation and the interface provided by the returned object.
  • See also Statistics for general developer information regarding measurements and metrics in Prosody.