Mayfly.Metrics (Mayfly v1.0.0-rc.1)

Copy Markdown View Source

CloudWatch metrics without API calls, using the Embedded Metric Format: one JSON line on stdout that CloudWatch Logs turns into metrics.

Mayfly.Metrics.emit("MyApp", %{"OrdersPlaced" => 1, "CartValue" => {129.5, "None"}},
  dimensions: %{"Tenant" => tenant},
  properties: %{"orderId" => id})

Mayfly.Metrics.count("MyApp", "Retries", 2, dimensions: %{"Queue" => "orders"})
Mayfly.Metrics.timing("MyApp", "DbLatency", 42)      # Milliseconds

Invocation metrics

Attach once (in init/1) and every invocation emits Duration (Milliseconds), Errors (0/1) and ColdStart (1 on the first invocation of an execution environment, 0 afterwards) with the dimension FunctionName:

def init(_opts) do
  Mayfly.Metrics.attach_invocation_metrics("MyApp")
  {:ok, nil}
end

Requires the optional :telemetry dependency.

Why not Logger?

CloudWatch parses EMF from the raw log line, which must be a single, standalone JSON object. Mayfly.LogFormatter wraps messages in its own object, so metrics are written straight to stdout with IO.puts/2. Lambda captures stdout into the function's log stream.

Summary

Types

Metric value: a number, or {number, unit}.

Functions

Attaches a :telemetry handler to [:mayfly, :invocation, :stop] that emits Duration, Errors and ColdStart under namespace with dimension FunctionName. Safe to call once per environment; returns {:error, :already_exists} if attached twice. Returns {:error, :telemetry_not_available} without the dep.

Attaches a :telemetry handler to [:mayfly, :platform, :report] (emitted by Mayfly.Extension when the extension is enabled) that publishes Lambda's own numbers as EMF metrics under namespace, dimension FunctionName: Duration, BilledDuration, MaxMemoryUsed, MemorySize, and InitDuration when present (cold starts). Unlike attach_invocation_metrics/2, Duration here is what Lambda measured and BilledDuration is what you pay for. The report for an invocation arrives after that invocation has finished, attributed by requestId.

Builds the EMF record without writing it. Raises ArgumentError on invalid units or too many dimensions/metrics.

Emits one EMF record. metrics maps names to values or {value, unit}.

Emits a Milliseconds metric.

Types

value()

@type value() :: number() | {number(), String.t()}

Metric value: a number, or {number, unit}.

Functions

attach_invocation_metrics(namespace, opts \\ [])

@spec attach_invocation_metrics(String.t(), keyword()) :: :ok | {:error, term()}

Attaches a :telemetry handler to [:mayfly, :invocation, :stop] that emits Duration, Errors and ColdStart under namespace with dimension FunctionName. Safe to call once per environment; returns {:error, :already_exists} if attached twice. Returns {:error, :telemetry_not_available} without the dep.

attach_platform_metrics(namespace, opts \\ [])

@spec attach_platform_metrics(String.t(), keyword()) :: :ok | {:error, term()}

Attaches a :telemetry handler to [:mayfly, :platform, :report] (emitted by Mayfly.Extension when the extension is enabled) that publishes Lambda's own numbers as EMF metrics under namespace, dimension FunctionName: Duration, BilledDuration, MaxMemoryUsed, MemorySize, and InitDuration when present (cold starts). Unlike attach_invocation_metrics/2, Duration here is what Lambda measured and BilledDuration is what you pay for. The report for an invocation arrives after that invocation has finished, attributed by requestId.

build(namespace, metrics, opts \\ [])

@spec build(String.t(), %{optional(String.t()) => value()}, keyword()) :: map()

Builds the EMF record without writing it. Raises ArgumentError on invalid units or too many dimensions/metrics.

count(namespace, name, value \\ 1, opts \\ [])

@spec count(String.t(), String.t(), number(), keyword()) :: :ok

Emits a Count metric.

emit(namespace, metrics, opts \\ [])

@spec emit(String.t(), %{optional(String.t()) => value()}, keyword()) :: :ok

Emits one EMF record. metrics maps names to values or {value, unit}.

Options:

  • :dimensions – map of up to 30 string pairs; one dimension set
  • :properties – extra non-metric fields (searchable in Logs Insights)
  • :unit – default unit for plain numbers (default "Count")
  • :high_resolution – true for 1-second storage resolution
  • :timestamp – Unix ms (default now)
  • :device – IO device (default :stdio; tests pass a StringIO)

timing(namespace, name, ms, opts \\ [])

@spec timing(String.t(), String.t(), number(), keyword()) :: :ok

Emits a Milliseconds metric.