Documentation
¶
Overview ¶
Package wind provides a minimalist microservice framework following a composable (Lego-like) design philosophy. The core App manages server lifecycles, while registration, logging and instance assembly are left entirely to the caller.
This is NOT a battery-included framework. Each subsystem (transport, log) exposes only interfaces and helper types so that callers mix and match implementations as needed.
Index ¶
- Constants
- Variables
- func GetColorTag(ctx context.Context) string
- func GetMetadata(ctx context.Context, key string) string
- func GetTraceID(ctx context.Context) string
- func GetUserID(ctx context.Context) string
- func NewMetadataContext(ctx context.Context, md Metadata) context.Context
- func WithColorTag(ctx context.Context, tag string) context.Context
- func WithMetadata(ctx context.Context, key, value string) context.Context
- func WithMetadatas(ctx context.Context, extra Metadata) context.Context
- func WithTraceID(ctx context.Context, traceID string) context.Context
- func WithUserID(ctx context.Context, userID string) context.Context
- func WithoutMetadata(ctx context.Context, key string) context.Context
- type App
- func (a *App) Done() <-chan struct{}
- func (a *App) Err() error
- func (a *App) ID() string
- func (a *App) Instance(endpoints ...string) *Instance
- func (a *App) InstanceID() string
- func (a *App) Logger() log.Logger
- func (a *App) Name() string
- func (a *App) Run(ctx context.Context) error
- func (a *App) Stop(ctx context.Context) error
- func (a *App) Version() string
- type Instance
- type Metadata
- type Option
- func WithAfterStop(fn func(ctx context.Context) error) Option
- func WithBanner(enabled bool) Option
- func WithBeforeStop(fn func(ctx context.Context) error) Option
- func WithID(id string) Option
- func WithInstanceID(id string) Option
- func WithLogger(l log.Logger) Option
- func WithName(name string) Option
- func WithServer(srv ...transport.Server) Option
- func WithSignal(sigs ...os.Signal) Option
- func WithStopTimeout(d time.Duration) Option
- func WithVersion(version string) Option
Examples ¶
Constants ¶
const ( HeaderTraceID = "x-wind-trace-id" HeaderUserID = "x-wind-user-id" HeaderColorTag = "x-wind-color-tag" )
Standard header keys used to propagate request-scoped metadata across service boundaries. Callers may use these keys or define their own.
Variables ¶
var ErrAppAlreadyRunning = errors.New("wind: App.Run already called")
ErrAppAlreadyRunning is returned by App.Run when Run has already been called on the same *App instance. An *App is designed to be used once; create a new instance for each run.
Functions ¶
func GetColorTag ¶
GetColorTag returns the color tag from the context's Metadata, or an empty string if none is set.
func GetMetadata ¶
GetMetadata returns the value for key from the context's Metadata. It returns an empty string when the key is absent or no metadata exists.
func GetTraceID ¶
GetTraceID returns the trace ID from the context's Metadata, or an empty string if none is set.
func GetUserID ¶
GetUserID returns the user ID from the context's Metadata, or an empty string if none is set.
func NewMetadataContext ¶
NewMetadataContext returns a copy of ctx with the given Metadata attached.
The provided map is deep-copied so that subsequent mutations by the caller do not affect the value stored in the context.
func WithColorTag ¶
WithColorTag returns a new context with the given color tag set in its Metadata. It is a convenience wrapper around WithMetadata.
func WithMetadata ¶
WithMetadata returns a new context with the given key/value pair set in its Metadata.
The existing metadata map is deep-copied before modification to prevent concurrent write races when the parent context is shared across goroutines (BUG-2 regression guard).
Example ¶
ExampleWithMetadata shows how to attach request-scoped metadata to a context and read it back. Each call deep-copies the map, so the parent context is never mutated (BUG-2 regression guard).
package main
import (
"context"
"fmt"
"github.com/tx7do/go-wind"
)
func main() {
ctx := context.Background()
// Set a trace ID.
ctx = wind.WithTraceID(ctx, "trace-abc")
// Set a user ID on the same chain.
ctx = wind.WithUserID(ctx, "user-42")
fmt.Println(wind.GetTraceID(ctx))
fmt.Println(wind.GetUserID(ctx))
}
Output: trace-abc user-42
func WithMetadatas ¶ added in v0.0.2
WithMetadatas merges the key/value pairs from extra into the context's existing Metadata, performing a single deep-copy regardless of how many pairs are provided. This is more efficient than calling WithMetadata repeatedly when setting multiple keys at once (e.g. reconstructing context from inbound request headers).
If extra is empty and no existing metadata is present, ctx is returned unchanged.
func WithTraceID ¶
WithTraceID returns a new context with the given trace ID set in its Metadata. It is a convenience wrapper around WithMetadata.
func WithUserID ¶
WithUserID returns a new context with the given user ID set in its Metadata. It is a convenience wrapper around WithMetadata.
func WithoutMetadata ¶ added in v0.0.2
WithoutMetadata returns a new context with the given key removed from its Metadata. If the key does not exist or no metadata is present, ctx is returned unchanged.
Like WithMetadata, this operates on a private copy so the parent context is never mutated.
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is the central runtime that owns and manages the lifecycle of one or more transport.Server instances. It is intentionally free of any hard-coded integration — callers wire up servers, registries, loggers, etc. through the composable Option pattern.
func New ¶
New creates an *App with the given options. Sensible defaults are applied:
- Listens for SIGTERM, SIGINT and SIGQUIT for graceful shutdown.
- A 10-second stop timeout is enforced during shutdown.
Example ¶
ExampleNew demonstrates creating an *wind.App with composable options. The framework does not start any server until App.Run is called.
package main
import (
"fmt"
"github.com/tx7do/go-wind"
)
func main() {
app := wind.New(
wind.WithID("svc-1"),
wind.WithName("user-service"),
wind.WithVersion("v1.0.0"),
)
fmt.Println("ID:", app.ID())
fmt.Println("Name:", app.Name())
fmt.Println("Version:", app.Version())
}
Output: ID: svc-1 Name: user-service Version: v1.0.0
func (*App) Done ¶
func (a *App) Done() <-chan struct{}
Done returns a channel that is closed when [Run] finishes — either after a normal graceful shutdown or after a server crash. It allows external supervisors to wait for the app to terminate without calling [Stop] or wrapping [Run] in their own error channel. Done is provided for read-only observation; it must not be closed by the caller.
Before [Run] is called the channel is open (not closed).
func (*App) Err ¶
Err returns the error that caused [Run] to exit. It must be called after [Done] is closed; calling it before returns nil.
This complements [Done] by allowing external supervisors to observe both the termination and the outcome without wrapping [Run] in their own goroutine:
<-app.Done()
if err := app.Err(); err != nil { ... }
func (*App) Instance ¶
Instance builds an *Instance from the app's configured ID, Name and Version, plus the provided endpoint URLs. This is a convenience helper for callers who wish to register with a service registry — it does NOT perform any registration on its own (composable design: the caller chooses whether and how to register).
Example ¶
ExampleApp_Instance demonstrates building a *wind.Instance from the app's configured identity fields. This is a convenience helper — callers still choose whether and how to register the instance.
package main
import (
"fmt"
"github.com/tx7do/go-wind"
)
func main() {
app := wind.New(
wind.WithID("svc-1"),
wind.WithName("user-service"),
wind.WithVersion("v1.0.0"),
)
inst := app.Instance("grpc://0.0.0.0:9000")
fmt.Println(inst.ID, inst.Name, inst.Version)
fmt.Println(inst.Endpoints[0])
}
Output: svc-1 user-service v1.0.0 grpc://0.0.0.0:9000
func (*App) InstanceID ¶ added in v0.0.2
InstanceID returns the instance identifier. If set via WithInstanceID, that value is returned. Otherwise an auto-generated ID is returned on first access. The auto-generated format is:
"{id}-{version}@{hostname}@{randomShortHex}"
func (*App) Logger ¶
Logger returns the app-specific logger set via WithLogger. If no logger was set, it falls back to the package-level global logger (log.GetLogger).
func (*App) Run ¶
Run starts the application and blocks until all servers have stopped.
All registered servers are started concurrently inside an errgroup. The method returns when:
- A registered OS signal (SIGTERM/SIGINT/SIGQUIT) is received.
- The provided ctx is cancelled.
- Any server's Start returns an error (server crash) or nil (server self-exit).
On any of these triggers, every server receives a Stop call with a fresh context derived from context.Background() — NOT from the run context — so the configured stopTimeout is honoured even after a.cancel() fires.
If no servers are registered, Run blocks until ctx is cancelled, a signal is received, or [Stop] is called. This is useful for pure worker applications that do not expose a network server but still want graceful shutdown.
Run must be called at most once per *App instance. Calling Run a second time returns ErrAppAlreadyRunning immediately.
func (*App) Stop ¶
Stop gracefully stops the application by cancelling the main context and waiting for all registered servers to finish shutting down.
Stop does NOT call Server.Stop directly — it only triggers cancellation and lets the Stop watchers inside [Run] perform the actual shutdown. This avoids double-Stop when Stop is called concurrently with an active Run (ISSUE-1).
Stop must be called from a different goroutine than [Run]. If Run has not been started, Stop blocks until ctx is done (there is nothing to stop).
func (*App) Version ¶
Version returns the application version set via WithVersion.
type Instance ¶
type Instance struct {
ID string `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
Endpoints []string `json:"endpoints"`
Metadata map[string]string `json:"metadata"`
}
Instance describes a single service instance registered with (or discovered from) a service registry. It carries the information a client needs to connect: identity, version, network endpoints and arbitrary metadata.
App.Instance returns a populated *Instance for the caller to use with their chosen registry implementation in go-wind-plugins.
func (*Instance) FirstEndpoint ¶
FirstEndpoint returns the first endpoint URL or an empty string if the instance has no endpoints. This is a convenience for the common single-endpoint case.
type Metadata ¶
Metadata is a simple string-keyed map carried through the context chain. It is the vehicle for trace IDs, user IDs, color tags and other request-scoped attributes.
func MetadataFromContext ¶
MetadataFromContext extracts the Metadata from ctx, if present.
The returned map is shared with the context and other callers. It MUST be treated as read-only: mutating it will corrupt the context value and cause data races when the context is shared across goroutines. To modify metadata, use WithMetadata, WithMetadatas, or WithoutMetadata which always operate on a private copy.
Use GetMetadata instead when only a single value is needed — it avoids exposing the underlying map entirely.
type Option ¶
type Option func(*App)
Option configures an *App via functional options.
func WithAfterStop ¶
WithAfterStop registers a callback invoked AFTER all servers have stopped. Typical uses include closing database connections, flushing log buffers, or releasing other resources.
Multiple callbacks are executed in registration order. If any callback returns an error, the error is logged but the error is not returned from Run (the servers have already stopped successfully).
func WithBanner ¶ added in v0.0.2
WithBanner enables or disables the startup banner. When enabled, App.Run prints the application name, version, appId, instanceId, PID and hostname at startup. Disabled by default to keep output clean.
func WithBeforeStop ¶
WithBeforeStop registers a callback invoked BEFORE any server's Stop is called during graceful shutdown. Typical uses include deregistering from a service registry, draining an incoming-request queue, or writing a final health-check ping.
Multiple callbacks are executed in registration order. If any callback returns an error, the error is logged but shutdown continues.
func WithID ¶
WithID sets the unique identifier of the application. It is typically used to construct an Instance for service registration.
func WithInstanceID ¶ added in v0.0.2
WithInstanceID sets a custom instance identifier. If not set and banner is enabled, one is auto-generated in the format "{id}-{version}@{hostname}@{random}".
func WithLogger ¶
WithLogger sets an app-specific log.Logger. If not set, App.Logger falls back to the package-level global logger (log.GetLogger). This allows callers to give each *App instance its own logger without affecting the global state.
func WithServer ¶
WithServer attaches one or more transport.Server instances to the App. All servers are started concurrently when App.Run is called and stopped concurrently during graceful shutdown.
func WithSignal ¶
WithSignal overrides the default set of OS signals that trigger graceful shutdown. By default the app listens for SIGTERM, SIGINT and SIGQUIT.
func WithStopTimeout ¶
WithStopTimeout sets the maximum duration allowed for graceful shutdown. Each server's Stop call receives a context with this deadline. The default is 10 seconds.
func WithVersion ¶
WithVersion sets the semantic version of the application.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package errors provides a structured, transport-aware error model for the go-wind framework.
|
Package errors provides a structured, transport-aware error model for the go-wind framework. |
|
Package log provides a minimal, backend-agnostic logging interface for the go-wind framework.
|
Package log provides a minimal, backend-agnostic logging interface for the go-wind framework. |
|
Package transport defines the core transport-layer abstractions for the go-wind framework.
|
Package transport defines the core transport-layer abstractions for the go-wind framework. |