Skip to content

Naming spans and semantic attributes

Span names

Uptrace uses span names and some attributes to group similar spans together. To group spans properly, give them short and concise names. The total number of unique span names should be way lower than 1000. Otherwise you will have too many span groups.

Good span names are:

Span name Comment
GET /projects/:id A route name with placeholders instead of params.
select_project A function name without arguments.
SELECT * FROM projects WHERE id = ? A database query with placeholders.

These are bad span names because spans of a similar type have different names and Uptrace will not group them together:

Span name Comment
GET /projects/42 Contains variable param 42.
select_project(42) Contains variable 42.
SELECT * FROM projects WHERE id = 42 Contains user data 42.


To record any additional information, you annotate spans with attributes specific to the operation. For example, an HTTP endpoint may have such attributes as http.method = GET and http.route = /projects/:id.

For common attributes there a list of attribute names called semantic attributes convention. For the other attributes you can use arbitrary names, but Uptrace reserves names with span. prefix for built-in attributes (for example,

As for the attribute values, Uptrace supports following types:

  • string.
  • int64 and float64.
  • Arrays of the types above.
  • JSON string.

Semantic attributes


Semantic attributes translates as attributes with meaning. Semantics is the study of meaning.

For common span attributes there is a semantic convention that contains a list of predefined attribute names and their meaning. Use attributes from that list whenever possible.

Span types

Uptrace organizes spans into categories (or types) depending on the presense of some semantic attributes.

  • http:service_name category for spans representing HTTP requests.
  • db:postgresql for PostgreSQL queries.
  • log:error for log messages with ERROR severity.
  • exception for exceptions.

By making sure that spans have the right semantic attributes, you ensure that they are categorized and grouped properly. Which in turn makes filtering and aggregation simpler.


To monitor HTTP clients and servers, use HTTP semantic convention.

A minimal HTTP server example:

SpanName = "GET /users/:id"
SpanKind = "server"

http.method = "GET"
http.route = "/users/:id"

A minimal HTTP client example:

SpanName = "GET /users/:id"
SpanKind = "client"

http.method = "GET"
http.route = "/users/:id"


To monitor remote procedure calls, use RPC semantic convention.

A minimal RPC server example:

SpanName = "AuthService/Auth"
SpanKind = "server"

rpc.system = "grpc"
rpc.service = "AuthService.Auth"
rpc.method = "Auth"


To monitor database queries and Redis/memcached commands, use DB semantic convention.

A minimal DB example:

SpanName = "pg.query"
SpanKind = "client"

db.system = "postgresql"
db.statement = "SELECT * FROM users WHERE id = 123"

A minimal Redis command example:

SpanName = "GET"
SpanKind = "client"

db.system = "redis"
db.statement = "GET foo"


To monitor producers and consumers (queues), use Messaging semantic convention.

A minimal producer example:

SpanName = "send MyQueue"
SpanKind = "producer"

messaging.system = "rabbitmq"
messaging.destination = "MyQueue"
messaging.destination_kind = "queue"

A minimal consumer example:

SpanName = "process MyQueue"
SpanKind = "consumer"

messaging.system = "rabbitmq"
messaging.operation = "process"
messaging.destination = "MyQueue"
messaging.destination_kind = "queue"

Functions as a Service

To monitor serverless functions, use FaaS semantic convention.

A minimal server example:

SpanName = "my-lambda-function"
SpanKind = "server"

faas.trigger = "http" = "my-lambda-function"

A minimal client example:

SpanName = "my-lambda-function"
SpanKind = "client"

faas.trigger = "http"
faas.invoked_name = "my-lambda-function"

Plain functions

To monitor plain functions, use Source code attributes.

A minimal example:

SpanName = "org.FetchUser" = "myservice"
code.function = "org.FetchUser"
code.filepath = "org/user.go"
code.lineno = "123"


To monitor errors and exceptions, use Exceptions semantic convention and events API.

A minimal example:

EventName = "exception"

exception.type = "*exec.ExitError"
exception.message = "exit status 1"
exception.stack = "<exception stack>"

The same with Go programming language:

span := trace.SpanFromContext(ctx)

span.AddEvent(ctx, "exception",
    // Exception type and message.
    label.String("exception.type", "*exec.ExitError"),
    label.String("exception.message", "exit status 1"),
    label.String("exception.stack", string(runtime.Stack())),


A minimal example:

EventName = "log"

log.severity = "info"
log.message = "something failed"
log.params.key1 = "value1"
log.params.key2 = "value2"

Also see Logging.

Internal pseudo type

internal is a pseudo type for the spans that can't be categorized using the rules above and that have SpanKind = "internal".

If for some reason you want to move a particular group of spans from internal type, you can either:

  • Add proper type-specific semantic attributes.
  • Change SpanKind to any other valid value (for example, server or client).