Packages
gRPC client implementation for Elixir
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/grpc/telemetry.ex
defmodule GRPC.Telemetry do
@moduledoc """
Events published by GRPC
These can be divided in client-side events and server-side events.
## Client-side Events
* `[:grpc, :client, :rpc, :start]` - Published before all interceptors are executed.
* `[:grpc, :client, :rpc, :stop]` - Published after all interceptors executed successfully.
* `:duration` - the duration as measured through `System.monotonic_time()`
for the whole interceptor pipeline.
* `[:grpc, :client, :rpc, :exception]` - Published if any exception occurs while receiving a message.
* `:duration` - the duration as measured through `System.monotonic_time()`
for the execution since the start of the pipeline until the exception happened.
| event | measurements | metadata |
|--------------|--------------|----------|
| `[:rpc, :start]` | `:count` | `:stream` |
| `[:rpc, :stop]` | `:duration` | `:stream` |
| `[:rpc, :exception]` | `:duration` | `:stream`, `:kind`, `:reason`, `:stacktrace` |
### Metadata
* `:stream` - the `%GRPC.Server.Stream{}` for the request
* `:function_name` - the name of the function called
* `:server` - the server module name
* `:endpoint` - the endpoint module name
* `:request` - the client request
* `:result` - the result returned from the interceptor pipeline.
`:exception` events also include some error metadata:
* `:reason` is the error value in case of `catch` or the actual exception in case of `rescue`.
* `:kind` can be one of:
* `:error` — from an `{:error, error}` return value. Some Erlang functions may also throw an
`:error` tuple, which will be reported as `:error`.
* `:exit` — from a caught process exit.
* `:throw` — from a caught value, this doesn't necessarily mean that an error occurred.
## Server-side Events
* `[:grpc, :server, :rpc, :start]` - Published before all interceptors are executed.
* `[:grpc, :server, :rpc, :stop]` - Published after all interceptors executed successfully.
* `:duration` - the duration as measured through `System.monotonic_time()`
for the whole interceptor pipeline.
* `[:grpc, :server, :rpc, :exception]` - Published if any exception occurs while receiving a message.
* `:duration` - the duration as measured through `System.monotonic_time()`
for the execution since the start of the pipeline until the exception happened.
| event | measurements | metadata |
|--------------|--------------|----------|
| `[:rpc, :start]` | `:count` | `:stream`, `:server`, `:endpoint`, `:function_name` |
| `[:rpc, :stop]` | `:duration` | `:stream`, `:server`, `:endpoint`, `:function_name` , `:result` |
| `[:rpc, :exception]` | `:duration` | `:stream`, `:server`, `:endpoint`, `:function_name`, `:kind`, `:reason`, `:stacktrace` |
### Metadata
* `:stream` - the `%GRPC.Server.Stream{}` for the request.
* `:function_name` - the name of the function called.
* `:server` - the server module name.
* `:endpoint` - the endpoint module name.
* `:result` - the result returned from the interceptor pipeline.
`:exception` events also include some error metadata:
* `:reason` is the error value in case of `catch` or the actual exception in case of `rescue`.
* `:kind` can be one of:
* `:error` — from an `{:error, error}` return value. Some Erlang functions may also throw an
`:error` tuple, which will be reported as `:error`.
* `:exit` — from a caught process exit.
* `:throw` — from a caught value, this doesn't necessarily mean that an error occurred.
"""
require Logger
@server_rpc [:grpc, :server, :rpc]
@client_rpc [:grpc, :client, :rpc]
@doc "The server telemetry event prefix."
def server_rpc_prefix, do: @server_rpc
@doc "The client telemetry event prefix."
def client_rpc_prefix, do: @client_rpc
@doc false
def server_span(server, endpoint, func_name, stream, span_fn) do
start_metadata = %{
server: server,
endpoint: endpoint,
function_name: func_name,
stream: stream
}
:telemetry.span(@server_rpc, start_metadata, fn ->
result = span_fn.()
{result, Map.put(start_metadata, :result, result)}
end)
rescue
e in GRPC.RPCError ->
{:error, e}
catch
kind, reason ->
stacktrace = __STACKTRACE__
Logger.error(Exception.format(kind, reason, stacktrace))
{:error, %{kind: kind, reason: reason, stack: stacktrace}}
end
@client_rpc_start_name @client_rpc ++ [:start]
@doc false
def client_rpc_start_name, do: @client_rpc_start_name
@doc false
def client_span(stream, request, span_fn) do
start_metadata = %{stream: stream, request: request}
:telemetry.span(@client_rpc, start_metadata, fn ->
try do
result = span_fn.()
{result, Map.put(start_metadata, :result, result)}
rescue
e ->
:erlang.error(Exception.normalize(:error, e, __STACKTRACE__))
end
end)
catch
kind, reason ->
stacktrace = __STACKTRACE__
Logger.error(Exception.format(kind, reason, stacktrace))
:erlang.raise(kind, reason, stacktrace)
end
@client_rpc_stop_name @client_rpc ++ [:stop]
@doc false
def client_rpc_stop_name, do: @client_rpc_stop_name
@doc false
def client_rpc_stop(stream, duration) do
:telemetry.execute(@client_rpc_stop_name, %{duration: duration}, %{stream: stream})
end
@client_rpc_exception_name @client_rpc ++ [:exception]
@doc false
def client_rpc_exception_name, do: @client_rpc_exception_name
@doc false
def client_rpc_exception(
stream,
kind,
reason,
stacktrace,
duration
) do
:telemetry.execute(@client_rpc_exception_name, %{duration: duration}, %{
stream: stream,
kind: kind,
reason: reason,
stacktrace: stacktrace
})
end
end