Documentation
¶
Overview ¶
Package telegram is a log/slog handler that sends records to Telegram chats, ported from the maintainer's TypeScript telegram-logger. Messages are queued and sent in the background, so logging never blocks or fails a request; Handler.SendSync and Handler.ContactSync deliver right away and return the error, for jobs that retry.
tg := telegram.NewHandler(telegram.Config{BotToken: token, ChatID: "-100123", MinLevel: slog.LevelError})
log := slog.New(slog.NewMultiHandler(base, tg))
defer tg.Close(ctx)
Without a bot token and chat id the Handler is disabled, so apps can always install it; the bot token is a config.Secret and never appears in errors.
See https://github.com/AlexGalhardo/vukem/blob/main/docs/subsystems.md#telegram-logging.
Index ¶
- Constants
- Variables
- type Config
- type Handler
- func (h *Handler) Close(ctx context.Context) error
- func (h *Handler) Configured() bool
- func (h *Handler) Contact(_ context.Context, fields map[string]string)
- func (h *Handler) ContactSync(ctx context.Context, fields map[string]string) error
- func (h *Handler) Enabled(_ context.Context, l slog.Level) bool
- func (h *Handler) Handle(ctx context.Context, r slog.Record) error
- func (h *Handler) Send(_ context.Context, htmlText string)
- func (h *Handler) SendSync(ctx context.Context, htmlText string) error
- func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *Handler) WithGroup(name string) slog.Handler
Examples ¶
Constants ¶
const ( LevelSuccess slog.Level = 2 LevelFatal slog.Level = 12 )
Custom levels for Success and Fatal helpers.
const MaxLength = 4096
MaxLength is Telegram's message limit.
Variables ¶
var ErrDisabled = errors.New("telegram: not configured")
ErrDisabled is returned by the synchronous senders when the bot token or chat id is missing.
var ErrQueueFull = errors.New("telegram: queue full, message dropped")
ErrQueueFull is reported when a message is dropped.
var ErrRateLimited = errors.New("telegram: rate limit reached, log records dropped")
ErrRateLimited is reported when log records are dropped by RateLimit.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
BotToken config.Secret
// ChatID is the default chat; ChatIDs routes levels to other chats.
ChatID string
ChatIDs map[slog.Level]string
// MinLevel filters records (default: error). slog.LevelInfo is 0, the
// zero value, so set MinLevelSet to forward info and above.
MinLevel slog.Level
// MinLevelSet marks MinLevel as explicit, so a zero MinLevel means info
// rather than the error default.
MinLevelSet bool
// Filter, when set, forwards only the log records it returns true for
// (Send and Contact are never filtered). It runs on the logging
// goroutine; call r.Clone() before keeping r beyond the call.
Filter func(ctx context.Context, r slog.Record) bool
// RateLimit caps the log records forwarded per RatePeriod (0: no cap);
// the excess is dropped and ErrRateLimited reported to OnError once per
// period. Send and Contact are never rate limited.
RateLimit int
// RatePeriod is the RateLimit window (default 1 minute).
RatePeriod time.Duration
AppName string
Env string
// ThreadID posts into a forum topic.
ThreadID int
// Silent sends without notification sound.
Silent bool
// QueueSize bounds pending messages (default 100); when full, new
// messages are dropped and reported to OnError.
QueueSize int
// Timeout bounds each request (default 5s).
Timeout time.Duration
// MaxRetryAfter caps the wait for one retry after HTTP 429 (default 10s).
MaxRetryAfter time.Duration
// OnError is told about delivery failures (never contains the token).
// Calls never overlap: a report raised while OnError runs is dropped.
// Records logged while OnError runs, or carrying an error it was given,
// are not forwarded to Telegram, so logging the error through a logger
// that includes this handler cannot loop.
OnError func(error)
// BaseURL defaults to https://api.telegram.org; vukem.New reads it from
// TELEGRAM_API_URL (a self-hosted Bot API server, or a fake in tests).
BaseURL string
// HTTP is the client (default: Timeout per request).
HTTP *http.Client
}
Config configures the handler. Without BotToken and ChatID the handler is disabled (Enabled reports false), so it can always be installed.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler implements slog.Handler.
func NewHandler ¶
NewHandler returns a handler and starts its sender goroutine (when enabled); call Close to drain it.
Example ¶
package main
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"net/http/httptest"
"github.com/AlexGalhardo/vukem/config"
"github.com/AlexGalhardo/vukem/logging/telegram"
)
func main() {
// A fake Bot API stands in for https://api.telegram.org.
api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body struct {
ChatID string `json:"chat_id"`
}
_ = json.NewDecoder(r.Body).Decode(&body)
fmt.Println("sent to chat", body.ChatID)
_, _ = w.Write([]byte(`{"ok":true}`))
}))
defer api.Close()
tg := telegram.NewHandler(telegram.Config{
BotToken: config.NewSecret("123:TOKEN"),
ChatID: "-100123",
MinLevel: slog.LevelError,
BaseURL: api.URL,
})
log := slog.New(tg)
log.Info("ignored: below MinLevel")
log.Error("payment failed", "order_id", 42)
// Close flushes queued messages before shutdown.
_ = tg.Close(context.Background())
}
Output: sent to chat -100123
func (*Handler) Configured ¶ added in v0.28.0
Configured reports whether the bot token and a chat are set; a handler that is not configured drops every message and its synchronous senders return ErrDisabled.
func (*Handler) Contact ¶
Contact queues a contact-form notification (never filtered by level); every field is escaped. Delivery errors go to OnError; use ContactSync to get them.
func (*Handler) ContactSync ¶ added in v0.25.0
ContactSync sends a contact-form notification now, like SendSync.
func (*Handler) Send ¶
Send queues a pre-formatted HTML message for the default chat (the caller escapes user content). A message over MaxLength is truncated outside tags and entities, with the open tags closed. Delivery errors go to OnError; use SendSync to get them.
func (*Handler) SendSync ¶ added in v0.25.0
SendSync sends a pre-formatted HTML message to the default chat now, bypassing the queue, and returns the delivery error (ErrDisabled when not configured). Each request is bounded by Timeout and the whole call by ctx; the error never contains the bot token.