Go SDK Gobeta

Installation

The Blackfire Go SDK is part of the Blackfire Go Module. You need to install the agent first.

Blackfire Go SDK

To profile a CLI program with the Blackfire Go SDK, use this one liner at the top of the main() function:

Caution

Note that if you are calling os.Exit(), the defer will not be executed. You would have to call blackfire.End() before.

1
2
3
4
5
6
7
import "github.com/blackfireio/go-blackfire"

func main() {
    defer blackfire.Enable().End()

    // ...
}

This code does not profile anything unless you turn profiling on by using the run command of the Blackfire CLI:

1
2
3
4
5
# Code is not profiled, and all Blackfire function calls are no-ops
$ ./go_bin

# Code is profiled
$ blackfire run ./go_bin

Configuring the Blackfire Probe Manually

The Blackfire Probe is usually configured via environment variables, but you can customize the configuration when calling the blackfire.Configure() function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import "github.com/blackfireio/go-blackfire"

// Create a manual configuration object
// All values set will take precedence on other configuration mechanism
configuration := &blackfire.Configuration{
    // Client ID to authenticate with the Blackfire API
    ClientId: "",

    // Client Token to authenticate with the Blackfire API
    ClientToken: "",

    // A zerolog Logger (default stderr)
    Logger: nil,

    // The maximum duration of a profile. A profile operation can never exceed
    // this duration (default 10 minutes).
    // This guards against runaway profile operations.
    MaxProfileDuration: 10 * time.Minute,

    // Time before dropping an unresponsive agent connection (default 250ms)
    AgentTimeout: 250 * time.Millisecond,

    // The socket to use when connecting to the Blackfire agent (default depends on OS)
    AgentSocket: "unix:///var/run/blackfire/agent.sock",

    // The configuration path to the Blackfire CLI ini file
    // Defaults to ~/.blackfire.ini
    ConfigFile string
}

// Configure the Blackfire probe with manual settings
// and from the environment variables or via the $HOME/.blackfire.ini file
blackfire.Configure(configuration)

Configuring the Logger

The Logger is automatically configured via the environment variables and defaults to stderr. You can explicitly set a zerolog Logger.

As a convenience, you can use the blackfire.NewLogger() function to log to a file:

1
2
3
4
5
6
7
8
9
import "github.com/blackfireio/go-blackfire"

// Creates a logger to a file with level 4
// Blackfire log levels: 4: debug, 3: info, 2: warning, 1: error
logger := blackfire.NewLogger("/tmp/blackfire.log", 4)
configuration := &blackfire.Configuration{
    Logger: &logger,
}
blackfire.Configure(configuration)

Use the special stderr or stdout strings to log on the console.

Always-On Profiling

If you instrument the code like shown in the first example, the profiling is only enabled when running the code via blackfire run. When debugging, you might prefer to turn profiling on by default:

Start profiling the code by calling EnableNow():

1
2
3
4
import "github.com/blackfireio/go-blackfire"

// Start the profiling
blackfire.EnableNow()

Stop profiling the code by calling Disable():

1
2
// Stop the profiling
blackfire.Disable()

You can call Enable() and Disable() as many times as needed in your code.

Calling End() instead of Disable() stops the profiling and forces the collected data to be sent to Blackfire servers:

1
2
3
// Stop the profiling
// Send the result to Blackfire servers
blackfire.End()

End() blocks until the profile is sent to the Blackfire’s servers. You can also avoid the wait by using by using the EndNoWait() function instead:

1
2
3
4
// Stop the profiling
// Send the result to Blackfire servers
// Don't wait the profile upload
blackfire.EndNoWait()

On-Demand Profiling via an HTTP API

The Blackfire Go SDK exposes a profiling HTTP API through several Go HTTP handlers:

  • blackfire.EnableHandler();
  • blackfire.DisableHandler();
  • blackfire.EndHandler().

The SDK also exposes a blackfire.DashboardHandler() that provides a Web interface from which you can start and stops profiling with a click.

To ease the integration into an existing Go web server, use the blackfire.NewServeMux() serve mux instead. It takes a prefix as an argument. For instance, if you pass _blackfire as a prefix, here is the list of exposed endpoints:

  • /_blackfire/dashboard gives access to the Web interface;
  • /_blackfire/enable starts profiling for 30 seconds;
  • /_blackfire/enable?duration=1.5 starts profiling for 1.5 seconds. The duration parameter accepts a float;
  • /_blackfire/disable stops the current profiling session;
  • /_blackfire/end stops the profiling session and send the profiling data to Blackfire servers.

The HTTP response returns a status code depending on the outcome:

  • 200 if everything is ok;
  • 400 if the payload has an issue (a duration that is not a float for instance);
  • 500 if the server is not able to handle the command (enabling the probe when the agent is not running for instane).

The HTTP response body is a JSON file (application/problem+json) containing the following fields: status, title, and detail.

Note

Don’t forget to secure these endpoints with some HTTP authentication.

On-Demand Profiling via Signals

The SDK allows you to control profiling with IPC Signals:

1
2
3
4
5
6
7
8
9
import "github.com/blackfireio/go-blackfire/signal"

func installProfilerSignalHandlers() {
    // Starts profiling the current process for 5 seconds when receiving the SIGUSR1 signal.
    signal.EnableOnSignal(syscall.SIGUSR1, 5*time.Second)

    // Stops profiling and send profiling data to Blackfire when receiving the SIGUSR2 signal.
    signal.EndOnSignal(syscall.SIGUSR2)
}

Please note that a DisableOnSignal() function is also available.

Profiling For A Duration

The Blackfire SDK exposes the EnableNowFor() function that lets you profile for a given duration:

1
2
3
4
5
6
import "github.com/blackfireio/go-blackfire"

// Profile for 200ms and send the data to Blackfire
blackfire.EnableNowFor(200 * time.Millisecond)

blackfire.End()

Generating Sub-Profiles

Thanks to the Distributed Profiling feature, you can embed sub-profiles in a main profile.

For instance, when profiling command line programs that call sub-processes, you might want to trigger profiles for them as well. You can do so by generating a sub-profile request:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import "github.com/blackfireio/go-blackfire"

cmd := exec.Command("some_bin")
cmd.Env = env
if env == nil {
    cmd.Env = os.Environ()
}

// Profile the command and attach it to the main profile
// when on-demand profiling is enabled, this is only enable profiling
// if the main program is profiled
if id, err := blackfire.GenerateSubProfileQuery(); err == nil {
    cmd.Env = append(cmd.Env, "BLACKFIRE_QUERY="+id)
}

if err := cmd.Run(); err != nil {
    // ...
}

Sub-profiles also work for HTTP requests by adding a X-Blackfire-Query HTTP header (note that this is not supported by Go, but will work if the target server is running Python or PHP).

1
2
3
4
5
import "github.com/blackfireio/go-blackfire"

if id, err := blackfire.GenerateSubProfileQuery(); err == nil {
    w.Header().Set("X-Blackfire-Query", id)
}