elastic/elastic-transport-go
GitHub: elastic/elastic-transport-go
Stars: 14 | Forks: 21
# elastic-transport-go
This library was lifted from elasticsearch-net and then transformed to be used across all Elastic services rather than
only Elasticsearch.
It provides the Transport interface used by `go-elasticsearch`, connection pool, cluster discovery, and multiple loggers.
## Installation
Add the package to your go.mod file:
`require github.com/elastic/elastic-transport-go/v8 main`
## Usage
### Transport
The transport provides the basic layer to access Elasticsearch APIs. Create a
client with `NewClient` and functional options:
package main
import (
"context"
"log"
"net/http"
"net/url"
"time"
"github.com/elastic/elastic-transport-go/v8/elastictransport"
)
func main() {
u, _ := url.Parse("http://127.0.0.1:9200")
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
)
if err != nil {
log.Fatalln(err)
}
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_ = transport.Close(ctx)
}()
req, _ := http.NewRequest("GET", "/", nil)
res, err := transport.Perform(req)
if err != nil {
log.Fatalln(err)
}
defer res.Body.Close()
log.Println(res)
}
Options are applied in order; when the same setting is specified more than once
the last value wins. See the `With*` functions in the
[package documentation](https://pkg.go.dev/github.com/elastic/elastic-transport-go/v8/elastictransport)
for the full list of available options.
Common examples:
// Multiple nodes with basic auth, custom retries, and compression
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u1, u2, u3),
elastictransport.WithBasicAuth("elastic", "changeme"),
elastictransport.WithRetry(5, 429, 502, 503, 504),
elastictransport.WithRetryBackoff(func(attempt int) time.Duration {
return time.Duration(attempt) * 100 * time.Millisecond
}),
elastictransport.WithCompression(gzip.BestSpeed),
)
### Discovery
Discovery module calls the cluster to retrieve its complete list of nodes.
Once your transport has been set up, you can easily trigger this behavior like so:
err := transport.DiscoverNodes()
Or configure automatic periodic discovery when creating the client:
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithDiscoverNodesInterval(5 * time.Minute),
)
### Metrics
Allows you to retrieve metrics directly from the transport. Enable metrics when
creating the client:
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithMetrics(),
)
### Leveled Logging
`WithLeveledLogger` sets a structured, leveled logger for transport-internal
events (connection management, node discovery). The `LeveledLogger` interface
uses the same `(msg, keysAndValues...)` convention as `log/slog`:
type LeveledLogger interface {
Debug(msg string, keysAndValues ...any)
Info(msg string, keysAndValues ...any)
Warn(msg string, keysAndValues ...any)
Error(msg string, keysAndValues ...any)
}
Add `LoggingInterceptor` to also log request/response round-trips through the
same logger. This is the recommended way to get full logging:
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithLeveledLogger(&elastictransport.SlogLogger{
Logger: slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
Level: slog.LevelDebug,
})),
}),
elastictransport.WithInterceptors(
elastictransport.LoggingInterceptor(false, false),
),
)
Successful round-trips are logged at **Info** level; errors at **Error** level;
connection-management events at **Debug**, **Warn**, or **Error** depending on
severity.
#### Body Logging
`LoggingInterceptor` accepts two booleans to enable request and/or response
body capture:
elastictransport.WithInterceptors(
elastictransport.LoggingInterceptor(true, true), // request body, response body
)
#### Built-in slog Handlers
The `sloghandler` sub-package provides drop-in `slog.Handler` replacements
for every deprecated logger:
| Deprecated Logger | sloghandler Replacement |
| ----------------- | ---------------------------------- |
| `TextLogger` | `sloghandler.NewTextHandler(w)` |
| `ColorLogger` | `sloghandler.NewColorHandler(w)` |
| `CurlLogger` | `sloghandler.NewCurlHandler(w)` |
| `JSONLogger` | `sloghandler.NewJSONECSHandler(w)` |
import "github.com/elastic/elastic-transport-go/v8/elastictransport/sloghandler"
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithLeveledLogger(&elastictransport.SlogLogger{
Logger: slog.New(sloghandler.NewColorHandler(os.Stderr)),
}),
elastictransport.WithInterceptors(
elastictransport.LoggingInterceptor(false, false),
),
)
#### Custom Logger Implementations
Ready-made adapters for zap, zerolog, logrus, and logr are in
[`_examples/logging/adapters/`](./_examples/logging/adapters/).
To use a different logging library, implement the four methods on a thin wrapper:
type ZapLeveledLogger struct{ Logger *zap.SugaredLogger }
func (l *ZapLeveledLogger) Debug(ctx context.Context, msg string, kv ...any) { l.Logger.Debugw(msg, kv...) }
func (l *ZapLeveledLogger) Info(ctx context.Context, msg string, kv ...any) { l.Logger.Infow(msg, kv...) }
func (l *ZapLeveledLogger) Warn(ctx context.Context, msg string, kv ...any) { l.Logger.Warnw(msg, kv...) }
func (l *ZapLeveledLogger) Error(ctx context.Context, msg string, kv ...any) { l.Logger.Errorw(msg, kv...) }
#### Context Integration
The logger is injected into the request context during `Perform`, making it
available to custom interceptors via `LoggerFromContext`. Callers can override
the logger per-request using `ContextWithLogger`:
interceptor := func(next elastictransport.RoundTripFunc) elastictransport.RoundTripFunc {
return func(req *http.Request) (*http.Response, error) {
if logger := elastictransport.LoggerFromContext(req.Context()); logger != nil {
logger.Debug("before request", "method", req.Method)
}
return next(req)
}
}
#### Migrating from WithDebugLogger / WithLogger
`WithDebugLogger()` and `WithLogger()` still work but are deprecated. Under the
hood `WithDebugLogger` now creates a `SlogLogger` wrapping `slog.Default()`.
| | `WithDebugLogger()` | `WithLogger()` | `WithLeveledLogger()` + `LoggingInterceptor` |
| --------------------- | ------------------- | --------------- | -------------------------------------------- |
| Round-trip logging | No | Yes | Yes (via interceptor) |
| Connection events | Yes (Debug only) | No | Yes |
| Output destination | stdout | User-controlled | User-controlled |
| Log levels | Debug only | None | Debug/Info/Warn/Error |
| Structured data | No | No | Yes (key-value pairs) |
| Custom logger support | No | Yes | Yes |
| Per-client isolation | Yes | Yes | Yes |
| Context injection | No | No | Yes |
| Composable ordering | No | No | Yes (interceptor chain) |
Replace either legacy option:
// Before (WithDebugLogger)
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithDebugLogger(),
)
// Before (WithLogger)
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithLogger(&elastictransport.TextLogger{Output: os.Stdout}),
)
// After
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithLeveledLogger(&elastictransport.SlogLogger{
Logger: slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Level: slog.LevelDebug,
})),
}),
elastictransport.WithInterceptors(
elastictransport.LoggingInterceptor(false, false),
),
)
### Request/Response Loggers (Deprecated)
A logger can be provided via the `WithLogger` option. Several bundled loggers
are available:
#### TextLogger
config:
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithLogger(&elastictransport.TextLogger{Output: os.Stdout, EnableRequestBody: true, EnableResponseBody: true}),
)
output:
< {
< "name" : "es",
< "cluster_name" : "elasticsearch",
< "cluster_uuid" : "RxB1iqTNT9q3LlIkTsmWRA",
< "version" : {
< "number" : "8.0.0-SNAPSHOT",
< "build_flavor" : "default",
< "build_type" : "docker",
< "build_hash" : "0564e027dc6c69236937b1edcc04c207b4cd8128",
< "build_date" : "2021-11-25T00:23:33.139514432Z",
< "build_snapshot" : true,
< "lucene_version" : "9.0.0",
< "minimum_wire_compatibility_version" : "7.16.0",
< "minimum_index_compatibility_version" : "7.0.0"
< },
< "tagline" : "You Know, for Search"
< }
#### JSONLogger
config:
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithLogger(&elastictransport.JSONLogger{Output: os.Stdout, EnableRequestBody: true, EnableResponseBody: true}),
)
output:
{
"@timestamp": "2021-11-25T16:33:51Z",
"event": {
"duration": 2892269
},
"url": {
"scheme": "http",
"domain": "127.0.0.1",
"port": 9200,
"path": "/",
"query": ""
},
"http": {
"request": {
"method": "GET"
},
"response": {
"status_code": 200,
"body": "{\n \"name\" : \"es1\",\n \"cluster_name\" : \"go-elasticsearch\",\n \"cluster_uuid\" : \"RxB1iqTNT9q3LlIkTsmWRA\",\n \"version\" : {\n \"number\" : \"8.0.0-SNAPSHOT\",\n \"build_flavor\" : \"default\",\n \"build_type\" : \"docker\",\n \"build_hash\" : \"0564e027dc6c69236937b1edcc04c207b4cd8128\",\n \"build_date\" : \"2021-11-25T00:23:33.139514432Z\",\n \"build_snapshot\" : true,\n \"lucene_version\" : \"9.0.0\",\n \"minimum_wire_compatibility_version\" : \"8.0.0\",\n \"minimum_index_compatibility_version\" : \"7.0.0\"\n },\n \"tagline\" : \"You Know, for Search\"\n}\n"
}
}
}
#### ColorLogger
config:
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithLogger(&elastictransport.ColorLogger{Output: os.Stdout, EnableRequestBody: true, EnableResponseBody: true}),
)
output:
GET http://127.0.0.1:9200/ 200 OK 2ms
« {
« "name" : "es1",
« "cluster_name" : "go-elasticsearch",
« "cluster_uuid" : "RxB1iqTNT9q3LlIkTsmWRA",
« "version" : {
« "number" : "8.0.0-SNAPSHOT",
« "build_flavor" : "default",
« "build_type" : "docker",
« "build_hash" : "0564e027dc6c69236937b1edcc04c207b4cd8128",
« "build_date" : "2021-11-25T00:23:33.139514432Z",
« "build_snapshot" : true,
« "lucene_version" : "9.0.0",
« "minimum_wire_compatibility_version" : "7.16.0",
« "minimum_index_compatibility_version" : "7.0.0"
« },
« "tagline" : "You Know, for Search"
« }
────────────────────────────────────────────────────────────────────────────────
#### CurlLogger
config:
transport, err := elastictransport.NewClient(
elastictransport.WithURLs(u),
elastictransport.WithLogger(&elastictransport.CurlLogger{Output: os.Stdout, EnableRequestBody: true, EnableResponseBody: true}),
)
output:
curl -X GET 'http://localhost:9200/?pretty'
# => 2021-11-25T16:40:11Z [200 OK] 3ms
# {
# "name": "es1",
# "cluster_name": "go-elasticsearch",
# "cluster_uuid": "RxB1iqTNT9q3LlIkTsmWRA",
# "version": {
# "number": "8.0.0-SNAPSHOT",
# "build_flavor": "default",
# "build_type": "docker",
# "build_hash": "0564e027dc6c69236937b1edcc04c207b4cd8128",
# "build_date": "2021-11-25T00:23:33.139514432Z",
# "build_snapshot": true,
# "lucene_version": "9.0.0",
# "minimum_wire_compatibility_version": "7.16.0",
# "minimum_index_compatibility_version": "7.0.0"
# },
# "tagline": "You Know, for Search"
# }
# License
Licensed under the Apache License, Version 2.0.
标签:EVTX分析