Sftpd emits :telemetry events for server lifecycle and SFTP operations. The package depends on :telemetry directly, so applications can attach handlers without adding another dependency.

Installing Sftpd

def deps do
  [
    {:sftpd, "~> 0.1.1"}
  ]
end

Event Families

Sftpd emits three event families:

  • [:sftpd, :server, :start]
  • [:sftpd, :server, :stop]
  • [:sftpd, :sftp, operation]

operation is one of:

  • :open
  • :close
  • :read
  • :write
  • :list_dir
  • :read_file_info
  • :read_link_info
  • :read_link
  • :rename
  • :delete
  • :make_dir
  • :del_dir
  • :position
  • :is_dir
  • :get_cwd
  • :make_symlink
  • :write_file_info

Measurements

Every event includes:

  • %{duration: native_time}

Additional measurements:

  • :read adds :bytes
  • :write adds :bytes

duration is measured with System.monotonic_time/0 native units. Convert it with System.convert_time_unit/3 before exporting or logging human-readable durations.

Metadata

Common SFTP Metadata

All [:sftpd, :sftp, operation] events include:

  • :backend
  • :backend_kind
  • :result
  • :reason when an error reason is available

backend_kind is one of:

  • :module
  • :genserver

For {:genserver, server} backends, :backend is inspect(server) rather than a module name.

Result Values

Most operations use:

  • :ok
  • :error

Special cases:

  • :read may emit :eof
  • :is_dir emits :directory or :not_directory
  • exceptions inside Sftpd.Telemetry.span/4 emit result: :exception plus :kind and :reason, then are reraised

Operation-Specific Metadata

[:sftpd, :sftp, :open]

  • :path
  • :requested_modes
  • :mode
  • :open_timeout

requested_modes contains the raw mode list passed into the SFTP file handler. mode is the resolved value :read or :write after Sftpd normalizes it.

[:sftpd, :sftp, :close]

  • :io_device
  • :close_timeout
  • :close_shutdown_grace

[:sftpd, :sftp, :read]

  • :io_device
  • :bytes_requested

The :read event does not include :path. If you need path-level context for reads, correlate the :io_device back to the earlier [:sftpd, :sftp, :open] event for that handle.

[:sftpd, :sftp, :write]

  • :io_device

[:sftpd, :sftp, :position]

  • :io_device
  • :offset

[:sftpd, :sftp, :rename]

  • :src_path
  • :dst_path

Path-based operations such as :list_dir, :read_file_info, :read_link_info, :delete, :make_dir, and :del_dir add:

  • :path

Server Metadata

[:sftpd, :server, :start]

  • :port
  • :max_sessions
  • :backend
  • :backend_kind
  • :result
  • :server_ref on success

[:sftpd, :server, :stop]

  • :server_ref
  • :result

Examples

Attach a single handler:

:telemetry.attach(
  "sftpd-read-logger",
  [:sftpd, :sftp, :read],
  fn _event, measurements, metadata, _config ->
    Logger.info(
      "sftp read io_device=#{inspect(metadata.io_device)} bytes=#{measurements.bytes} result=#{metadata.result}"
    )
  end,
  nil
)

Attach one handler to multiple events:

:telemetry.attach_many(
  "sftpd-audit",
  [
    [:sftpd, :server, :start],
    [:sftpd, :server, :stop],
    [:sftpd, :sftp, :write],
    [:sftpd, :sftp, :delete]
  ],
  fn event, measurements, metadata, _config ->
    Logger.info("""
    event=#{inspect(event)}
    duration_native=#{measurements.duration}
    result=#{metadata.result}
    backend=#{inspect(metadata.backend)}
    """)
  end,
  nil
)

Convert durations before exporting metrics:

:telemetry.attach(
  "sftpd-read-metrics",
  [:sftpd, :sftp, :read],
  fn _event, measurements, metadata, _config ->
    duration_us =
      System.convert_time_unit(measurements.duration, :native, :microsecond)

    Logger.info(
      "read io_device=#{inspect(metadata.io_device)} bytes=#{measurements.bytes} duration_us=#{duration_us}"
    )
  end,
  nil
)

Caveats

  • OTP's built-in :ssh_sftpd implementation always reports close success to the client, even if final close-time flushing fails. Telemetry still records those server-side close failures, but the client may not see them.
  • Telemetry is emitted from Sftpd and Sftpd.FileHandler, so event timings reflect the library's wrapper and backend call boundaries rather than network round-trip timings observed by the SFTP client.
  • Handlers run in the emitting process, so avoid slow handler work.