Skip to content

Axiom Transport

Go ReferenceVersionSourceChangelog

Ships structured logs to Axiom using the official axiom-go SDK. The transport constructs a JSON object from each entry and sends it via Client.Ingest() as NDJSON.

Import path: go.loglayer.dev/transports/axiom/v3. Package name: axiom.

sh
go get go.loglayer.dev/transports/axiom/v3

Authenticating

Axiom authenticates with an API token. The transport takes a caller-supplied *axiom.Client (required; New panics with ErrClientRequired when it is nil). You construct the client yourself, and the axiom-go SDK reads the token from the environment for you:

Env varRead byPurpose
AXIOM_TOKENaxiom-go SDKAPI token with ingest permission.
AXIOM_ORG_IDaxiom-go SDKOrganization ID (required for personal tokens).

The dataset is set on the transport via Config.DatasetName, not an env var.

Using environment variables

go
import (
    axiomgo "github.com/axiomhq/axiom-go/axiom"
    "go.loglayer.dev/v3"
    "go.loglayer.dev/transports/axiom/v3"
)

// The client picks up AXIOM_TOKEN (and AXIOM_ORG_ID for personal tokens)
// from the environment.
client, err := axiomgo.NewClient()
if err != nil {
    panic(err)
}

log := loglayer.New(loglayer.Config{
    Transport: axiom.New(axiom.Config{
        Client:      client,
        DatasetName: "my-logs",
    }),
})

Basic Usage

go
import (
    axiomgo "github.com/axiomhq/axiom-go/axiom"
    "go.loglayer.dev/v3"
    "go.loglayer.dev/transports/axiom/v3"
)

client, err := axiomgo.NewClient(
    axiomgo.SetAPITokenConfig("your-api-token"),
)
if err != nil {
    panic(err)
}

log := loglayer.New(loglayer.Config{
    Transport: axiom.New(axiom.Config{
        Client:      client,
        DatasetName: "my-logs",
    }),
})

log.Info("user signed in")
log.WithMetadata(loglayer.Metadata{"userId": 42}).Warn("retry exhausted")

Config

go
type Config struct {
    transport.BaseConfig

    Client       *axiom.Client
    DatasetName  string
    MessageField string
    OnError      func(error)
}
FieldTypeDefaultDescription
Client*axiom.Client(required)Constructed via axiom.NewClient() with authentication options.
DatasetNamestring(required)Axiom dataset ID or name to ingest logs into.
MessageFieldstring"msg"The key under which the joined message text is placed in the JSON object.
OnErrorfunc(error)stderrCalled when Client.Ingest() returns an error.

Payload Shape

Each log entry is ingested as a JSON object:

  • msg: the joined message text (configurable via MessageField)
  • Persistent fields from WithFields(), merged at root
  • The serialized error from WithError()
  • Metadata nested under the core's MetadataFieldName key (default "metadata"; map metadata flattens at root only when the core runs with FlattenMetadata: true, the v2 shape)
go
log.WithFields(loglayer.Fields{"requestId": "abc"}).
    WithError(errors.New("timeout")).
    WithMetadata(loglayer.Metadata{"durationMs": 42}).
    Info("served")

results in:

json
{
  "msg": "served",
  "requestId": "abc",
  "err": { "message": "timeout" },
  "metadata": { "durationMs": 42 }
}

Fatal Behavior

The transport never calls os.Exit or panic itself. The Axiom SDK is called synchronously per entry (Client.Ingest), so the fatal entry reaches Axiom before the log call returns. Whether the process terminates afterward is the LogLayer core's decision via Config.DisableFatalExit (default: exit). See Fatal Exits the Process.

Metadata Handling

Metadata follows the core placement rules: when Config.MetadataFieldName is empty, the core resolves it to "metadata" and the whole metadata value (map or non-map) nests under that key; with Config.FlattenMetadata: true (the v2 opt-out), map metadata merges at the root and non-map metadata nests under metadata.

Set Config.MetadataFieldName on the core to nest all metadata under a fixed key.

Level Mapping

LogLayer levels map directly to Axiom's expected level strings:

LogLayer LevelAxiom Level
LogLevelTrace"trace"
LogLevelDebug"debug"
LogLevelInfo"info"
LogLevelWarn"warn"
LogLevelError"error"
LogLevelFatal"fatal"
LogLevelPanic"panic"

GetLoggerInstance

Transport.GetLoggerInstance() returns the underlying *axiom.Client, useful for SDK features not exposed by the transport.

go
underlying := log.GetLoggerInstance(transportID).(*axiom.Client)