API Reference¶
Types¶
Message¶
The central data type representing a homerun notification/event.
type Message struct {
Title string `json:"title,omitempty"`
Message string `json:"message,omitempty"`
Severity string `json:"severity,omitempty"`
Author string `json:"author,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
System string `json:"system,omitempty"`
Tags string `json:"tags,omitempty"`
AssigneeAddress string `json:"assigneeaddress,omitempty"`
AssigneeName string `json:"assigneename,omitempty"`
Artifacts string `json:"artifacts,omitempty"`
URL string `json:"url,omitempty"`
}
RedisConfig¶
Holds Redis connection details used by pitcher and redisearch functions.
type RedisConfig struct {
Addr string // Redis host address
Port string // Redis port
Password string // Redis password
Stream string // Redis stream name (used by EnqueueMessageInRedisStreams)
Index string // RediSearch index name (used by StoreInRediSearch)
}
Pitcher¶
Owns the Redis connection used for publishing. Every *redis.Client carries its
own connection pool, so callers that publish more than once should create one
Pitcher, reuse it, and Close it when done.
Functions¶
Constructors¶
NewMessage¶
Creates a new Message with the given author, content, severity, and an auto-generated timestamp.
Messaging¶
NewPitcher / Pitcher.Enqueue / Pitcher.Close¶
The reusable form. Opens one Redis connection, publishes through it, and hands the lifetime to the caller.
func NewPitcher(rc RedisConfig) *Pitcher
func (p *Pitcher) Enqueue(
ctx context.Context,
msg Message,
streamOverride ...string,
) (objectID, streamID string, err error)
func (p *Pitcher) Close() error
pitcher := homerun.NewPitcher(rc)
defer pitcher.Close()
for msg := range messages {
objectID, streamID, err := pitcher.Enqueue(ctx, msg)
// ...
}
Enqueue takes a context.Context, so a publish can be cancelled or bounded by
the caller. The optional variadic streamOverride publishes to a different
stream than rc.Stream; only the first non-empty value is used.
EnqueueMessageInRedisStreams¶
Stores a Message as Redis JSON and enqueues its ID into a Redis Stream.
func EnqueueMessageInRedisStreams(
msg Message,
rc RedisConfig,
streamOverride ...string,
) (objectID, streamID string, err error)
Parameters:
msg- The Message to storerc- Redis connection config (usesAddr,Port,Password,Stream)streamOverride- Optional stream name overridingrc.Stream
Returns: The generated object ID, the stream name, and an error if enqueueing failed.
This is the one-shot form: it opens a connection, publishes, and closes it
again. For repeated publishing use NewPitcher instead of paying for a new
connection pool per message.
EnqueueMessageInRedisStreamsContext¶
EnqueueMessageInRedisStreams bounded by a context, so the publish can be
cancelled, given a deadline, and carry a request-scoped trace.
func EnqueueMessageInRedisStreamsContext(
ctx context.Context,
msg Message,
rc RedisConfig,
streamOverride ...string,
) (objectID, streamID string, err error)
The context-free form is exactly this with context.Background().
StoreInRediSearch¶
Indexes a Message in RediSearch for full-text search capabilities.
func StoreInRediSearch(message Message, rc RedisConfig) error
func StoreInRediSearchContext(ctx context.Context, message Message, rc RedisConfig) error
Time-range queries over the JSON documents
Enqueue writes the event time as Unix seconds into every document, under
RediSearchTimestampField (timestamp_unix). Declare it NUMERIC in a
JSON index to query time windows. See
Migration v3 → v4.
Deprecated in v4, removal in v5
StoreInRediSearch maintains a second, hash-based copy of every message.
If you index the Redis JSON documents that Enqueue already writes — the
ON JSON case — you do not need it: those documents are indexed
automatically, with the correct event timestamp. See
Migration v3 → v4.
ctx bounds establishing the connection. redisearch-go exposes no
context-aware command API, so the individual commands are bounded by read and
write deadlines on the connection pool instead (10s each, dial 5s). Before
v3.2.0 the pool had no timeouts at all, so a Redis that accepted the
connection and then stopped answering blocked the caller forever.
Schema. timestamp is indexed as NUMERIC (so @timestamp:[x +inf] range
queries work) and carries Message.Timestamp, the time the event happened. Up
to v3 it was TEXT and held the moment of indexing. A missing or unparseable
Message.Timestamp falls back to the current time and logs a warning.
Index compatibility. The function writes hashes. If the index exists and is
defined ON JSON, it returns an error rather than writing a document that would
be stored but never indexed — which is what v3 did silently.
Changing the schema requires the index to be recreated (FT.DROPINDEX); the
library only checks whether the index exists.
Parameters:
message- The Message to indexrc- Redis connection config (usesAddr,Port,Password,Index)
Returns: An error if the index is unset, unreachable, cannot be created, or the document cannot be indexed. Every failure is returned - none of them terminates the calling process.
GetMessageJSON¶
Retrieves a Message from Redis JSON by its ID.
func GetMessageJSON(
redisJSONid string,
redisJSONHandler *rejson.Handler,
) (jsonMessage Message, err error)
Returns: The deserialized Message and an error if the object was not found or unmarshalling failed.
HTTP Sending¶
SendToHomerun¶
Sends a rendered message body to a homerun endpoint via HTTP POST.
func SendToHomerun(
destination, token string,
renderedBody []byte,
insecure bool,
) (Response, error)
Parameters:
destination- Target URLtoken- Authentication token (set asX-Auth-Tokenheader)renderedBody- JSON body to sendinsecure- Skip TLS certificate verification
Returns: A Response and an error if the request could not be made or read.
type Response struct {
StatusCode int // e.g. 200
Status string // e.g. "200 OK"
Header http.Header
Body []byte // fully read
}
func (r Response) OK() bool // 2xx
A non-2xx answer is not an error — err reports whether the request could be
made and read at all. Check resp.OK() for the endpoint's verdict.
Up to v3 this returned the raw *http.Response after already reading and
closing its body. See Migration v3 → v4.
Bounded by DefaultHTTPTimeout (30s) even without a context.
SendToHomerunContext¶
SendToHomerun bounded by a context.
func SendToHomerunContext(
ctx context.Context,
destination, token string,
renderedBody []byte,
insecure bool,
) (Response, error)
Whichever expires first — the context deadline or DefaultHTTPTimeout — ends
the call.
SetHTTPClient¶
Installs the HTTP client both send functions use, for callers that need their own transport, timeout, proxy, instrumentation or retry wrapper.
Passing nil restores the built-in clients. A custom client is used for both
secure and insecure calls, so the insecure argument becomes the caller's
responsibility. Safe to call from any goroutine.
Connection reuse. By default the library keeps one client — and therefore
one connection pool — per TLS configuration. Up to and including v3.1.9 it built
a fresh http.Transport per call, so every send paid a new TCP and TLS
handshake and no connection was ever reused. Ten sends now share one connection;
they used to open ten.
RenderBody¶
Renders a Go template string with the given data object.
Returns: The rendered string and an error if template parsing or execution failed.
Startup¶
WaitForReady¶
Retries a probe with exponential backoff until it succeeds or the context is done.
func WaitForReady(ctx context.Context, probe func(context.Context) error, perAttemptTimeout time.Duration) error
- Backoff between attempts: 1s, 2s, 4s, 8s, then 16s.
- Each attempt runs under a child context bounded by
perAttemptTimeout; a non-positive value leaves it bounded byctxalone. - Every failed attempt logs a warning, a success after retries an info record.
- When
ctxis done it returns the last probe error wrapped with the attempt count (after 7 attempts: ...), usable witherrors.Is. - It never exits the process.
ctx must eventually be done: with no deadline and a probe that never succeeds,
it waits forever.
WaitForRedis / WaitForRedisContext¶
Blocks until Redis answers PING or timeout expires.
func WaitForRedis(rc RedisConfig, timeout time.Duration) error
func WaitForRedisContext(ctx context.Context, rc RedisConfig, timeout time.Duration) error
Each PING is bounded by 5s. The context form also stops when ctx is done, so
a service receiving SIGTERM during the wait can shut down instead of sitting out
the timeout. A RedisConfig without Addr or Port fails at once - retrying
cannot fix it.
Add a startupProbe
A service that waits before opening its HTTP port needs a startupProbe
covering the timeout. Otherwise a liveness probe of 10s initial delay, 10s
period and 3 failures restarts it about 40s into the wait.
LoadRedisStartupTimeout / ParseRedisStartupTimeout¶
const DefaultRedisStartupTimeout = 120 * time.Second
func LoadRedisStartupTimeout() (time.Duration, error) // reads REDIS_STARTUP_TIMEOUT
func ParseRedisStartupTimeout(v string) (time.Duration, error)
REDIS_STARTUP_TIMEOUT is a Go duration (90s, 2m). Unset or blank means
DefaultRedisStartupTimeout. An unparsable value - including a bare number like
120 - or a non-positive one is an error, not a fallback: a typo should
fail startup loudly rather than quietly restore a budget nobody chose.
Helpers¶
GenerateUUID¶
Returns a new random UUID v4 string.
GetRandomObject¶
Returns a random element from a string slice.
EnvVarExists¶
Returns true if the environment variable exists and is non-empty.
GetEnv¶
Returns the environment variable value or a fallback default.
Output¶
PrintTableRows¶
Renders a table with any number of rows to the given writer.
PrintTable¶
Deprecated, removal in v5
Renders a table with exactly one row. Use PrintTableRows.
Logging¶
SetLogger¶
Installs the logger this package writes to. The library is silent by default: until a logger is installed, every record is discarded.
Passing nil restores the default (discard). Safe to call at any time and from
any goroutine.
Up to and including v3.1.8 the package installed a pterm logger at trace level at import
time, so every consumer got ANSI-coloured output on stdout with no way to
silence, redirect or reformat it. Services that emit structured logs or draw a
TUI should now install their own logger, or leave it unset to get nothing.
The records the library emits:
| Level | Message | Attributes |
|---|---|---|
Info |
message enqueued in redis stream |
stream, messageID |
Info |
redisearch index created |
index |
Info |
document indexed in redisearch |
index, documentID |
Warn |
failed to close redis client |
error |
Warn |
failed to close redisearch connection pool |
error |
Warn |
received multiple streamOverride values, using the first |
count |
Warn |
readiness probe failed, retrying |
attempt, error, next_sleep |
Info |
readiness probe succeeded after retries |
attempts |
Variables¶
HomeRunBodyData¶
Default JSON template string for rendering a Message body.