https://github.com/posthog/posthog-go
Official PostHog Go library
https://github.com/posthog/posthog-go
event-tracking experimentation feature-flags go golang official posthog
Last synced: 5 months ago
JSON representation
Official PostHog Go library
- Host: GitHub
- URL: https://github.com/posthog/posthog-go
- Owner: PostHog
- License: mit
- Created: 2020-02-21T22:05:23.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2025-04-02T20:42:12.000Z (about 1 year ago)
- Last Synced: 2025-04-08T12:50:51.879Z (about 1 year ago)
- Topics: event-tracking, experimentation, feature-flags, go, golang, official, posthog
- Language: Go
- Homepage:
- Size: 216 KB
- Stars: 27
- Watchers: 3
- Forks: 22
- Open Issues: 19
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# PostHog Go
[](https://pkg.go.dev/github.com/posthog/posthog-go)

Please see the main [PostHog docs](https://posthog.com/docs). See the [Go SDK Docs](https://posthog.com/docs/libraries/go).
## Quickstart
Install posthog to your gopath
```bash
$ go get github.com/posthog/posthog-go
```
Go 🦔!
```go
package main
import (
"os"
"github.com/posthog/posthog-go"
)
func main() {
client := posthog.New(os.Getenv("POSTHOG_API_KEY")) // This value must be set to the project API key in PostHog
// alternatively, you can do
// client, _ := posthog.NewWithConfig(
// os.Getenv("POSTHOG_API_KEY"),
// posthog.Config{
// PersonalApiKey: "your personal API key", // Set this to your personal API token you want feature flag evaluation to be more performant. This will incur more costs, though
// Endpoint: "https://us.i.posthog.com",
// },
// )
defer client.Close()
// Capture an event
client.Enqueue(posthog.Capture{
DistinctId: "test-user",
Event: "test-snippet",
Properties: posthog.NewProperties().
Set("plan", "Enterprise").
Set("friends", 42),
})
// Add context for a user
client.Enqueue(posthog.Identify{
DistinctId: "user:123",
Properties: posthog.NewProperties().
Set("email", "john@doe.com").
Set("proUser", false),
})
// Link user contexts
client.Enqueue(posthog.Alias{
DistinctId: "user:123",
Alias: "user:12345",
})
// Capture a pageview
client.Enqueue(posthog.Capture{
DistinctId: "test-user",
Event: "$pageview",
Properties: posthog.NewProperties().
Set("$current_url", "https://example.com"),
})
// Capture an error / exception
client.Enqueue(posthog.NewDefaultException(
time.Now(),
"distinct-id",
"Error title",
"Error Description",
))
// Create a logger which automatically captures warning logs and above
baseLogHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})
logger := slog.New(posthog.NewSlogCaptureHandler(baseLogHandler, client,
posthog.WithMinCaptureLevel(slog.LevelWarn),
posthog.WithDistinctIDFn(func(ctx context.Context, r slog.Record) string {
// for demo purposes, real applications should likely pull this value from the context.
return "my-user-id"
}),
})
logger.Warn("Log that something broke", "error", fmt.Errorf("this is a dummy scenario"))
// Capture event with calculated uuid to deduplicate repeated events.
// The library github.com/google/uuid is used
key := myEvent.Id + myEvent.Project
uid := uuid.NewSHA1(uuid.NameSpaceX500, []byte(key)).String()
client.Enqueue(posthog.Capture{
Uuid: uid,
DistinctId: "test-user",
Event: "$pageview",
Properties: posthog.NewProperties().
Set("$current_url", "https://example.com"),
})
// Check if a feature flag is enabled
isMyFlagEnabled, err := client.IsFeatureEnabled(
FeatureFlagPayload{
Key: "flag-key",
DistinctId: "distinct_id_of_your_user",
})
if isMyFlagEnabled == true {
// Do something differently for this user
}
}
```
## Development
Make sure you have Go installed (macOS: `brew install go`, Linx / Windows: https://go.dev/doc/install).
To build the project:
```bash
# Install dependencies
make dependencies
# Run tests and build
make build
# Just run tests
make test
```
## Testing Locally
You can run your Go app against a local build of `posthog-go` by making the following change to your `go.mod` file for whichever your app, e.g.
```Go
module example/posthog-go-app
go 1.22.5
require github.com/posthog/posthog-go v0.0.0-20240327112532-87b23fe11103
require github.com/google/uuid v1.3.0 // indirect
replace github.com/posthog/posthog-go => /path-to-your-local/posthog-go
```
## Examples
Check out the [examples](examples/README.md) for more detailed examples of how to use the PostHog Go client.
## Running the examples
The examples demonstrate different features of the PostHog Go client. To run all examples:
### Option 1: Using .env file (Recommended)
```bash
# Copy the example .env file and fill in your credentials
cd examples
cp .env.example .env
# Edit .env with your actual API keys
# Run all examples
go run *.go
```
### Option 2: Using environment variables
```bash
# Set your PostHog API keys and endpoint (optional)
export POSTHOG_PROJECT_API_KEY="your-project-api-key"
export POSTHOG_PERSONAL_API_KEY="your-personal-api-key"
export POSTHOG_ENDPOINT="https://app.posthog.com" # Optional, defaults to http://localhost:8000
# Run all examples
go run examples/*.go
```
This will run:
- Feature flags example
- Capture events example
- Capture events with feature flag options example
### Prerequisites
Before running the examples, you'll need to:
1. Have a PostHog instance running (default: http://localhost:8000)
- You can modify the endpoint by setting the `POSTHOG_ENDPOINT` environment variable
- If not set, it defaults to "http://localhost:8000"
2. Set up the following feature flags in your PostHog instance:
- `multivariate-test` (a multivariate flag)
- `simple-test` (a simple boolean flag)
- `multivariate-simple-test` (a multivariate flag)
- `my_secret_flag_value` (a remote config flag with string payload)
- `my_secret_flag_json_object_value` (a remote config flag with JSON object payload)
- `my_secret_flag_json_array_value` (a remote config flag with JSON array payload)
3. Set your PostHog API keys as environment variables:
- `POSTHOG_PROJECT_API_KEY`: Your project API key (starts with `phc_...`)
- `POSTHOG_PERSONAL_API_KEY`: Your personal API key (starts with `phx_...`)
## Releasing
Releases are semi-automated via GitHub Actions. When a PR with the `release` and a version bump label is merged to `master`, the release workflow is triggered.
You'll need an approval from a PostHog engineer. If you're an employee, you can see the request in the [#approvals-client-libraries](https://app.slack.com/client/TSS5W8YQZ/C0A3UEVDDNF) channel.
### Release Process
1. **Create your PR** with the changes you want to release
2. **Add the `release` label** to the PR
3. **Add a version bump label** that should be either `bump-patch`, `bump-minor` or `bump-major`
4. **Merge the PR** to `master`
Once merged, the following happens automatically:
1. A Slack notification is sent to the client libraries channel requesting approval
2. A maintainer approves the release in the GitHub `Release` environment
3. The version is bumped in `version.go` based on the version label (`patch`, `minor`, or `major`, extracted from the label)
4. The `CHANGELOG.md` is updated with a link to the full changelog
5. Changes are committed and pushed to `master`
6. A git tag is created (e.g., `v1.8.0`)
7. A GitHub release is created with the changelog content
8. Slack is notified of the successful release
Releases are installed directly from GitHub.
## Event Delivery and Retry Behavior
The PostHog Go client includes automatic retry logic for handling transient network failures. Understanding when events are delivered vs dropped helps ensure reliable analytics.
### Events Are Delivered (Not Dropped)
The client automatically retries on network errors and will successfully deliver events when:
- **Transient network failures** - EOF errors, connection resets, TCP drops that recover within retry attempts
- **Server temporarily unavailable** - If the server starts responding before max retries are exhausted
- **Connection drops at any stage** - Whether after connect, during headers, or while sending body
Example scenarios that recover successfully:
- Server closes connection without response (EOF) but succeeds on retry
- TCP connection dropped after partial body read
- Temporary network interruption lasting a few seconds
### Events Are Dropped
Events will be permanently lost in these scenarios:
| Scenario | Behavior |
| ------------------------------ | ------------------------------------------------------------------------------ |
| **Max retries exceeded** | After 10 failed attempts, events are dropped and `Failure` callback is invoked |
| **Client closed during retry** | If `client.Close()` is called while retrying, pending events are dropped |
| **Non-retryable errors** | JSON marshalling failures cause immediate drop (no retry) |
| **HTTP 4xx responses** | Client errors (e.g., invalid API key) are not retried |
### Configuring Retry Behavior
You can customize retry timing via the `RetryAfter` config option:
```go
client, _ := posthog.NewWithConfig(
"api-key",
posthog.Config{
RetryAfter: func(attempt int) time.Duration {
// Custom backoff: 100ms, 200ms, 400ms, ...
return time.Duration(100<