Common Operations
- Configuration
- General
- How to initialize Go Collector?
- How to include Go Collector into a readiness probe?
- How to ensure that all trace data has been sent before shutdown?
- How to configure Go Collector to run in a serverless environment?
- How to configure the Go Collector to redact sensitive data?
- How to collect extra HTTP headers?
- Logging
- Instana AutoProfile™
- General
- Instrumentation
Configuration
General
How to initialize Go Collector?
To initialize Go Collector call instana.InitSensor()
at the beginning of your main()
function, providing an
*instana.Options
configuration object:
import instana "github.com/instana/go-sensor"
func main() {
instana.InitSensor(&instana.Options{
// ...
})
// ...
}
The Go Collector will use values of instana.DefaultOptions()
for non-initialized fields of configuration object provided to the instana.InitSensor()
function.
This would make your Go service appear in the Instana UI and report its runtime metrics. To see the trace information you'd need to instrument your app code first.
How to include Go Collector into a readiness probe?
To make sure that Go Collector has finished the announcement process and is ready to collect traces before sending the traffic to an instance, use the instana.Ready()
method:
// Every 100ms whether Go Collector is ready and return on success
func awaitInstanaReady(ctx context.Context) error {
ticker := time.NewTicker(100*time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if instana.Ready() {
return nil
}
case <-ctx.Done():
return ctx.Err()
}
}
return nil
}
How to ensure that all trace data has been sent before shutdown?
To make sure that all collected traces have been sent to the host agent or serverless acceptor, the Go Collector API offers the instana.Flush()
method. This
method forces the agent client to send all buffered data immediately. It is a blocking operation, and it's recommended to define a timeout or a deadline for this operation, so your service can exit gracefully:
func main() {
var srv http.Server
// Initialize and use http.Server
// ...
// Server shutdown sequence:
// 1. Finalize all pending requests and close the server
shutdownCtx, _ := context.WithTimeout(ctx, 5 * time.Second)
if err := srv.Shutdown(shutdownCtx); err != nil {
// ...
}
// 2. Flush all buffered traces to the agent
flushCtx, _ := context.WithTimeout(ctx, 5 * time.Second)
if err := instana.Flush(flushCtx); err != nil {
// ...
}
// ...
}
When a service is running on AWS Lambda instana.Flush()
is called automatically by Go Collector once handler is done, so there is no need to do this manually.
instana.Flush()
may interfere with Go Collector's data transmission cycle and it's not recommended to use it affect the trace data delivery during the service lifetime, e.g. upon finishing serving an HTTP request. There is no guarantee that Go Collector would stay operational after calling this method.
How to configure Go Collector to run in a serverless environment?
To use Go Collector for monitoring a service running in a serverless environment, such as AWS Fargate or Google Cloud Run, make sure that you have INSTANA_ENDPOINT_URL
and INSTANA_AGENT_KEY
environment variables
set in your task definition. Please refer to the respective section of Instana documentation for detailed explanation on how to do this:
- Configuring AWS Fargate task definitions
- Configuring AWS Lambda functions
- Configuring Google Cloud Run services
The INSTANA_AGENT_HOST
and INSTANA_AGENT_PORT
environment variables will be ignored when an app is running in a serverless mode.
Services running in serverless environments don't use host agent to send metrics and trace data to Instana backend, therefore the usual way of configuring the in-app collector via configuration.yaml
file is not applicable. Instead, there is a set of environment variables that can optionally be configured in service task definition:
Environment variable | Default value | Description |
---|---|---|
INSTANA_TIMEOUT |
500 |
The Instana backend connection timeout (in milliseconds) |
INSTANA_SECRETS |
contains-ignore-case:secret,key,password |
The secrets filter; also applied to process environment variables) |
INSTANA_EXTRA_HTTP_HEADERS |
none | A semicolon-separated list of HTTP headers to collect from incoming requests |
INSTANA_ENDPOINT_PROXY |
none | A proxy URL to use to connect to Instana backend |
INSTANA_TAGS |
none | A comma-separated list of tags with optional values to associate with the ECS task |
INSTANA_ZONE |
<Current AWS availability zone> |
A custom Instana zone name for this service |
Please refer to Instana documentation for more detailed description of these variables and their value format.
How to configure the Go Collector to redact sensitive data?
Certain instrumentation wrappers provided by Instana, e.g. the HTTP server and client wrappers, collect data that may contain sensitive information, such as passwords, keys and secrets. To avoid leaking these values the Go Collector replaces
them with <redacted>
before sending to the agent. The list of parameter name matchers is defined in com.instana.secrets
section of the Host Agent Configuration file and will be sent to the in-app tracer during the announcement phase (requires agent Go trace plugin com.instana.sensor-golang-trace
v1.3.0 and above).
The default setting for the secrets matcher is contains-ignore-case
with following list of terms: key
, password
, secret
. This would redact the value of a parameter which name contains any of these strings ignoring the case.
How to collect extra HTTP headers?
The HTTP instrumentation wrappers can collect HTTP headers, and send them along with the incoming or outgoing request spans. The list of case-insensitive header names can be provided both within (instana.Options).Tracer.CollectableHTTPHeaders
field of the options object that is passed to instana.InitSensor()
and in the Host Agent Configuration file.
The latter setting takes precedence, and requires the agent Go trace plugin com.instana.sensor-golang-trace
v1.3.0 or later versions:
instana.InitSensor(&instana.Options{
// ...
Tracer: instana.TracerOptions{
// ...
CollectableHTTPHeaders: []string{"x-request-id", "x-loadtest-id"},
},
})
This configuration is an equivalent of following settings in the Host Agent Configuration file:
com.instana.tracing:
extra-http-headers:
- 'x-request-id'
- 'x-loadtest-id'
By default, the HTTP instrumentation does not collect any headers.
Logging
The Go Collector uses a leveled logger to log internal errors and diagnostic information. The default github.com/instana/go-sensor/logger.Logger
uses log.Logger
configured with log.Lstdflags
as a backend
and writes messages to os.Stderr
.
How to use a 3rd-party logger to print out Go Collector logs?
You can configure Go Collector to use a third-party logger conforming to the instana.LeveledLogger
interface. For example, for github.com/sirupsen/logrus
or go.uber.org/zap
, use the [
instana.SetLogger()
][instana.SetLogger] method:
import (
"github.com/uber-go/zap"
instana "github.com/instana/go-sensor"
)
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
instana.SetLogger(logger)
}
The INSTANA_DEBUG
environment variable has no effect when using a 3rd-party logger. Please consult the documentation for your 3rd-party logger module to learn how to set the debug level via an environment variable or configuration
file.
How to enable debug logs for Go Collector without changing the app code?
To enable the Go Collector writing debug logs without changing the app code, set the INSTANA_DEBUG
environment variable to a non-empty value, for example:
export INSTANA_DEBUG=true
./your-app
The amount of debug logs that Go Collector produces depends on your application and can be significant. You are recommended to turn the debug logs on temporarily to gather information before you report an issue to Instana Support.
This setting works only if Go Collector uses logger.Logger
to output internal log messages, and overrides any attempt to change the log level from within
the app code.
How to collect logged errors and warnings associated with erroneous calls?
Instana Go Collector offers an instrumentation wrapper for github.com/sirupsen/logrus
to automatically collect and send error and warning log records to the agent. github.com/instana/go-sensor/instrumentation/instalogrus
provides an implementation of logrus.Hook
that handles any error or warning records, and associates them with the span found in the record context.Context
.
Here is an example on how to instrument the global logrus.Logger
, and use it inside of instrumented http.Handler
:
func main() {
// ...
// Register instalogrus hook within the global logger
logrus.AddHook(instalogrus.NewHook(sensor))
}
func myMethod(w http.ResponseWriter, req *http.Request) {
// ...
// When logging an error, make sure to provide the context.Context containing
// an active span, so that instrumentation hook could associate this message
// with a call.
logrus.WithContext(req.Context()).Error("something went wrong")
// ...
}
Refer to the package documentation for more detailed examples.
Instana AutoProfile™
Instana AutoProfile™ generates and reports process profiles to Instana. Unlike development-time and on-demand profilers, where a user must manually initiate profiling, it automatically schedules and continuously performs profiling appropriate for critical production environments.
How to permanently enable continuous profiling from within the app code?
To permanently enable continuous profiling for your service provide EnableAutoProfile: true
while initializing the sensor:
func main() {
instana.InitSensor(&instana.Options{
EnableAutoProfile: true,
// ... other options
})
// ...
}
How to enable/disable continuous profiling temporarily from within the app code?
To temporarily turn AutoProfile™ on and off from your code, call autoprofile.Enable()
and
autoprofile.Disable()
:
func expensiveCalculation() {
autoprofile.Enable()
defer autoprofile.Disable()
// ...
}
How to enable continuous profiling without changing the app code?
To enable AutoProfile™ for an app without code changes, set the INSTANA_AUTO_PROFILE=true
environment variable:
export INSTANA_AUTO_PROFILE=true
./your-app
Similar to enabling debug logging with INSTANA_DEBUG
that this value takes precedence and overrides any attempt to disable profiling from inside the application code.
Instrumentation
Your app code requires modifications in order to collect and send the tracing data to Instana. This process is called code instrumentation.
The Go Collector offers two options of instrumenting the code:
- Using provided code wrappers. This is the easiest way to instrument your code that requires very little changes. Instana aims to provide instrumentation modules for the most popular 3rd-party libraries, such as database drivers, HTTP frameworks, etc. If you're using a library that does not have corresponding instrumentation module yet, consider filing a feature request by using Instana Idea submission.
- Using the OpenTracing API. A more low-level approach that may require substantial amount of changes to your code base. However, it also provides a great flexibility when it comes to instrumenting your business-specific code parts.
Please note, that Instana uses a very specific set of span tags to ensure correct infrastructure correlation and representation of calls. Prefer using provided code wrappers over the OpenTracing API approach to make the most of your tracing data. When necessary, you might consider mixing code wrappers with the low-level approach, to benefit from leveraging the already existing and maintained code, while staying flexible to instrument your business-specific code parts.
HTTP services
The Go Collector provides instrumentation for clients and servers that use net/http
package. Once activated (see as follows) this instrumentation automatically collects information about incoming and outgoing requests and sends
it to the Instana agent.
Instrumentation modules for 3rd-party HTTP frameworks are provided as separate Go modules and provide more convenient way of instrumenting an HTTP service.
How to instrument an HTTP server handler?
With support to wrap a http.HandlerFunc
, Instana quickly adds the possibility to trace requests and collect child spans, executed in the context of the request span.
Minimal changes are required for Instana to be able to capture the necessary information. By simply wrapping the currently existing http.HandlerFunc
Instana collects and injects necessary information automatically.
That said, a simple handler function like the following will simple be wrapped and registered like normal.
The following example code demonstrates how to instrument an HTTP handler using instana.TracingHandlerFunc()
:
sensor := instana.NewSensor("my-http-server")
http.HandleFunc("/", instana.TracingHandlerFunc(sensor, "/", func(w http.ResponseWriter, req *http.Request) {
// ...
}))
In case your handler is implemented as a http.Handler
, pass its ServeHTTP
method instead:
h := http.FileServer(http.Dir("./"))
http.HandleFunc("/files", instana.TracingHandlerFunc(sensor, "index", h.ServeHTTP))
See the example/http-database-greeter
for full example.
How to instrument an HTTP client?
Requesting data or information from other, often external systems, is commonly implemented through HTTP requests. To make sure traces contain all spans, especially over all the different systems, certain span information have to be injected into the HTTP request headers before sending it out. Instana's Go Collector provides support to automate this process as much as possible.
To have Instana inject information into the request headers, create the http.Client
, wrap its Transport
with instana.RoundTripper()
and use it as in the following example.
req, err := http.NewRequest("GET", url, nil)
client := &http.Client{
Transport: instana.RoundTripper(sensor, nil),
}
ctx := instana.ContextWithSpan(context.Background(), parentSpan)
resp, err := client.Do(req.WithContext(ctx))
The provided parentSpan
is the incoming request from the request handler (see above) and provides the necessary tracing and span information to create a child span and inject it into the request.
Database clients
The Go Collector provides instana.InstrumentSQLDriver()
and instana.WrapSQLConnector()
(since Go v1.10+) to instrument SQL database calls made with database/sql
. The tracer will then automatically capture
the Query
and Exec
calls, gather information about the query, such as statement, execution time, etc. and forward them to be displayed as a part of the trace.
To instrument a noSQL database client, such as MongoDB, check whether there is an instrumentation module provided for your driver.
How to instrument a database connection created with sql.Open()?
To instrument a database driver, register it using instana.InstrumentSQLDriver()
first and replace the call to sql.Open()
with instana.SQLOpen()
. Here is an example on how to do this for github.com/lib/pq
PostgreSQL driver:
// Create a new instana.Sensor instance
sensor := instana.NewSensor("my-database-app")
// Instrument the driver
instana.InstrumentSQLDriver(sensor, "postgres", &pq.Driver{})
// Create an instance of *sql.DB to use for database queries
db, err := instana.SQLOpen("postgres", "postgres://...")
The instrumented driver is registered with the name <original_name>_with_instana
, e.g. in the example above the name would be postgres_with_instana
.
See the example/http-database-greeter
for full example.
How to instrument a database client created with sql.OpenDB()?
Starting from Go v1.10 database/sql
provides a new way to initialize *sql.DB
that does not require the use of global driver registry. If the database driver library provides a type that satisfies the database/sql/driver.Connector
interface, it can be used to create a database connection.
To instrument a driver.Connector
instance, wrap it using instana.WrapSQLConnector()
. Here is an example on how this can be done for github.com/go-sql-driver/mysql/
MySQL driver:
// Create a new instana.Sensor instance
sensor := instana.NewSensor("my-database-app")
// Initialize a new connector
connector, err := mysql.NewConnector(cfg)
// ...
// Wrap the connector before passing it to sql.OpenDB()
db, err := sql.OpenDB(instana.WrapSQLConnector(sensor, "mysql://...", connector))
How to instrument a MongoDB client?
The github.com/instana/go-sensor/instrumentation/instamongo
module provides constructor methods that create a new instance of go.mongodb.org/mongo-driver
client and instrument it with Instana.
See the package documentation for detailed examples and usage instructions.
How to instrument a Redis client?
-
The
github.com/instana/go-sensor/instrumentation/instaredis
module offers function wrappers forgo-redis
that instrument an instance ofredis.Client
orredis.ClusterClient
by adding hooks to the redis client.See the package documentation for detailed examples and usage instructions.
-
The
github.com/instana/go-sensor/instrumentation/instaredigo
module offers wrappers forredigo
that instrument an instance ofredis.Conn
.See the package documentation for detailed examples and usage instructions.
gRPC services
The github.com/instana/go-sensor/instrumentation/instagrpc
module provides both unary and stream interceptors to instrument gRPC servers and
clients that use google.golang.org/grpc
.
How to instrument a gRPC server?
The github.com/instana/go-sensor/instrumentation/instagrpc
module provides both stream and
unary call interceptors. The module documentation provides detailed explanation on how to use them to instrument a gRPC server implemented with google.golang.org/grpc
.
See the example/grpc-client-server
for full example.
How to instrument a gRPC client?
The github.com/instana/go-sensor/instrumentation/instagrpc
module provides both stream and
unary call interceptors. The module documentation provides detailed explanation on how to use them to instrument a gRPC client implemented with google.golang.org/grpc
.
See the example/grpc-client-server
for full example.
Messaging services
How to instrument a Kafka producer/consumer?
The github.com/instana/go-sensor/instrumentation/instasarama
module provides constructor methods that create a new instance of producer/consumer
clients and instrument it with Instana.
See the example/kafka-producer-consumer
for full example.
How to instrument a RabbitMQ producer/consumer?
The github.com/instana/go-sensor/instrumentation/instaamqp
module offers a function wrapper around amqp.Channel
that returns an instaamqp.AmqpChannel
instance. This Instana object provides instrumentation for the amqp.Channel.Publish
and amqp.Channel.Consume
methods that are responsible for tracing data from
messages sent and received.
See the full example at github.com/instana/go-sensor/blob/main/instrumentation/instaamqp
.
Renaming a service
To change the service name for a call in Instana, add the service
tag to its span as shown in the following example:
span.SetTag("service", "name")
Manual instrumentation with OpenTracing API
Instana Go Collector provides an interface that is compatible with github.com/opentracing/opentracing-go
, and thus can be used as a global tracer. However, the recommended
approach is to use the provided Instana code wrappers. They set up a lot of semantic information, which helps Instana get the best picture of the application possible.
Sending proper tags is especially important when you correlate calls to infrastructure. This is because most tags are strings, and thus you might make mistakes.
How to instrument a method?
A minimal instrumentation of a method with Go Collector consists of two steps:
- Start a new span with
instana.Sensor
. - Finalize the span before the method is returned.
Optional steps include collecting the details, relevant to this call to attach them to the active span, and injecting the active span into context.Context
to ensure trace continuation.
func MyMethod(sensor *instana.Sensor) {
// Start a new span associated with this method execution
span := sensor.Tracer().StartSpan("my-method")
// Schedule span finalization to send it to Instana agent
defer span.Finish()
// Optionally attach any relevant information that would help you to identify this call later
span.SetTag("key1", "value1")
span.SetTag("answer", 42)
// ...
}
See the example/opentracing
for more detailed example.
How to continue the trace inside of a subcall?
To associate a subcall with an active span, provide its context via the opentracing.ChildOf()
start span option when starting the subcall span:
var spanOpts []opentracing.StartSpanOption
// Check whether there is an active trace by fetching the currently active span from context
if parent, ok := instana.SpanFromContext(ctx); ok {
spanOpts = append(spanOpts, opentracing.ChildOf(parent.Context()))
}
// Start the subcall span
span := tracer.StartSpan("my-func", spanOpts...)
It's the caller's responsibility to ensure that an instance of context.Context
provided to the callee contains the active span injected with
instana.ContextWithSpan()
:
// And inject it into context, so any subcalls could use it as a parent
SubCall(instana.ContextWithSpan(ctx, span))
How to obtain the parent span inside of wrapped method?
The code wrappers that are provided by Instana utilize context.Context
to inject the parent span into a subcall. That is, inside of an instrumented handler/callback function, you can assume that instana.SpanFromContext()
returns the span that is started by the wrapper method:
parentSpan, ok := instana.SpanFromContext(ctx)
If a 3rd-party does not support passing context.Context
to the handler method, the wrapper code may use alternative methods of injecting the active span context. Please consult the documentation for respective wrapper module
to learn how to obtain the parent span context in this case.