notification

package
v1.18.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const (
	LevelInfo  = "INFO"
	LevelWarn  = "WARN"
	LevelError = "ERROR"
)

Severity levels for an alert. They exist because not every notification is a fault: a workflow an operator stopped, or a worker draining during a deploy, is routine. UINotificationProvider wrote every alert at ERROR, so adding lifecycle notifications would have filed routine events as errors in the log table and in the UI's error view.

View Source
const DeadLetterDigestWindow = time.Minute

DeadLetterDigestWindow is how long a workflow's dead letters gather before they are mailed. The first dead letter opens the window and the mail goes when it closes, so a workflow parking a thousand messages a second sends one mail a minute, not a thousand.

Variables

This section is empty.

Functions

func Literal added in v1.18.4

func Literal(_ context.Context, value string) (string, error)

Literal resolves nothing: every value is used as written.

func SampleMessage added in v1.18.4

func SampleMessage(msg hermod.Message) (payload string, truncated bool)

SampleMessage reads what a digest shows of msg: its payload, its after image, or its data, as indented JSON when it is JSON, capped.

Types

type DeadLetterSample added in v1.18.4

type DeadLetterSample struct {
	MessageID string
	NodeID    string
	SinkID    string
	Error     string
	FailedAt  time.Time
	// Payload is the message as text, already capped by payloadText.
	Payload   string
	Truncated bool
}

DeadLetterSample is one dead-lettered message as a digest shows it.

func (DeadLetterSample) When added in v1.18.4

func (d DeadLetterSample) When() string

When is the failure time as the mail shows it.

func (DeadLetterSample) Where added in v1.18.4

func (d DeadLetterSample) Where() string

Where names the step that gave up on a message.

type DiscordNotificationProvider

type DiscordNotificationProvider struct {
	// contains filtered or unexported fields
}

func NewDiscordNotificationProvider

func NewDiscordNotificationProvider(s storage.Storage) *DiscordNotificationProvider

func (*DiscordNotificationProvider) Send

func (p *DiscordNotificationProvider) Send(ctx context.Context, title, message string, wf storage.Workflow) error

func (*DiscordNotificationProvider) SetStorage

func (p *DiscordNotificationProvider) SetStorage(s storage.Storage)

func (*DiscordNotificationProvider) Type

type EmailNotificationProvider

type EmailNotificationProvider struct {
	// contains filtered or unexported fields
}

func NewEmailNotificationProvider

func NewEmailNotificationProvider(s storage.Storage) *EmailNotificationProvider

func (*EmailNotificationProvider) Send

func (p *EmailNotificationProvider) Send(ctx context.Context, title, message string, wf storage.Workflow) error

func (*EmailNotificationProvider) SetStorage

func (p *EmailNotificationProvider) SetStorage(s storage.Storage)

func (*EmailNotificationProvider) Type

type GenericWebhookProvider

type GenericWebhookProvider struct {
	// contains filtered or unexported fields
}

func NewGenericWebhookProvider

func NewGenericWebhookProvider(s storage.Storage) *GenericWebhookProvider

func (*GenericWebhookProvider) Send

func (p *GenericWebhookProvider) Send(ctx context.Context, title, message string, wf storage.Workflow) error

func (*GenericWebhookProvider) SetStorage

func (p *GenericWebhookProvider) SetStorage(s storage.Storage)

func (*GenericWebhookProvider) Type

func (p *GenericWebhookProvider) Type() string

type LeveledProvider added in v1.11.0

type LeveledProvider interface {
	SendLeveled(ctx context.Context, level, title, message string, wf storage.Workflow) error
}

LeveledProvider is the optional interface for a provider that records a severity. Providers that push to a human (Telegram, Slack, email) do not need one — the title carries the meaning there — so the interface stays optional rather than widening Provider for a single implementation.

type NotificationSettings

type NotificationSettings struct {
	SMTPHost     string `json:"smtp_host"`
	SMTPPort     int    `json:"smtp_port"`
	SMTPUser     string `json:"smtp_user"`
	SMTPPassword string `json:"smtp_password"`
	SMTPFrom     string `json:"smtp_from"`
	SMTPSSL      bool   `json:"smtp_ssl"`
	DefaultEmail string `json:"default_email"`

	TelegramToken  string `json:"telegram_token"`
	TelegramChatID string `json:"telegram_chat_id"`

	SlackWebhook   string `json:"slack_webhook"`
	DiscordWebhook string `json:"discord_webhook"`
	WebhookURL     string `json:"webhook_url"`
	BaseURL        string `json:"base_url"`

	// Panmail sends alerts through a panmail gateway. Each value may hold
	// {{secret.NAME}} or {{env.NAME}} references, resolved at send time.
	PanmailURL        string `json:"panmail_url"`
	PanmailAPIKey     string `json:"panmail_api_key"`
	PanmailProviderID string `json:"panmail_provider_id"`
	PanmailFrom       string `json:"panmail_from"`
	// PanmailTo is a comma-separated list. Empty means DefaultEmail.
	PanmailTo string `json:"panmail_to"`
	// PanmailDeadLetters mails a digest of every dead-lettered message: the
	// workflow, the node or sink, the error and the message, batched per
	// workflow over DeadLetterDigestWindow.
	PanmailDeadLetters bool `json:"panmail_dead_letters"`
	// PanmailHidePayload leaves message contents out of those digests.
	PanmailHidePayload bool `json:"panmail_hide_payload"`

	// EmailDeadLetters and TelegramDeadLetters send the same dead-letter
	// digest through SMTP and Telegram.
	EmailDeadLetters    bool `json:"email_dead_letters"`
	TelegramDeadLetters bool `json:"telegram_dead_letters"`
	// DeadLetterHidePayload leaves message contents out of the digest on
	// every channel.
	DeadLetterHidePayload bool `json:"dead_letter_hide_payload"`
}

func (NotificationSettings) SendDiscord

func (ns NotificationSettings) SendDiscord(ctx context.Context, title, message string, wf storage.Workflow) error

func (NotificationSettings) SendEmail

func (ns NotificationSettings) SendEmail(ctx context.Context, title, message string, wf storage.Workflow) error

func (NotificationSettings) SendGenericWebhook

func (ns NotificationSettings) SendGenericWebhook(ctx context.Context, title, message string, wf storage.Workflow) error

func (NotificationSettings) SendPanmail added in v1.18.4

func (ns NotificationSettings) SendPanmail(ctx context.Context, resolve Resolver, mail PanmailMail) error

SendPanmail sends mail through the panmail gateway. Every setting may hold {{secret.NAME}} or {{env.NAME}} references, which resolve here, at send time, so the stored settings never need to hold the api key itself.

func (NotificationSettings) SendSlack

func (ns NotificationSettings) SendSlack(ctx context.Context, title, message string, wf storage.Workflow) error

func (NotificationSettings) SendTelegram

func (ns NotificationSettings) SendTelegram(ctx context.Context, title, message string, wf storage.Workflow) error

func (NotificationSettings) Test

func (NotificationSettings) TestWith added in v1.18.4

func (ns NotificationSettings) TestWith(ctx context.Context, resolve Resolver) []TestResult

TestWith is Test with the resolver panmail's references are read through.

type PanmailMail added in v1.18.4

type PanmailMail struct {
	Subject string
	Text    string
	HTML    string
	// IdempotencyKey lets the gateway answer a repeated send with the mail it
	// already took, which is what makes a retry after a timeout safe.
	IdempotencyKey string
}

PanmailMail is one mail to the configured recipients.

type PanmailNotificationProvider added in v1.18.4

type PanmailNotificationProvider struct {
	// contains filtered or unexported fields
}

PanmailNotificationProvider sends alerts through the panmail gateway.

func NewPanmailNotificationProvider added in v1.18.4

func NewPanmailNotificationProvider(s storage.Storage) *PanmailNotificationProvider

func (*PanmailNotificationProvider) Send added in v1.18.4

func (p *PanmailNotificationProvider) Send(ctx context.Context, title, message string, wf storage.Workflow) error

func (*PanmailNotificationProvider) SetResolver added in v1.18.4

func (p *PanmailNotificationProvider) SetResolver(r Resolver)

SetResolver is how Service.SetResolver reaches this provider.

func (*PanmailNotificationProvider) SetStorage added in v1.18.4

func (p *PanmailNotificationProvider) SetStorage(s storage.Storage)

func (*PanmailNotificationProvider) Type added in v1.18.4

type Provider

type Provider interface {
	Send(ctx context.Context, title, message string, wf storage.Workflow) error
	Type() string
	SetStorage(s storage.Storage)
}

type Resolver added in v1.18.4

type Resolver func(ctx context.Context, value string) (string, error)

Resolver expands the secret and env references in one settings value. The registry supplies one that reads the global secret manager; without one, only env references (HERMOD_ENV_*) resolve.

The settings are global and only an administrator writes them, so they are resolved without a vhost on purpose: a vhost secret of the same name must not be able to redirect the gateway, or the key, that every alert uses.

type Service

type Service struct {
	// contains filtered or unexported fields
}

func NewService

func NewService(s storage.Storage) *Service

func (*Service) AddProvider

func (s *Service) AddProvider(p Provider)

func (*Service) DeadLettered added in v1.18.4

func (s *Service) DeadLettered(wf storage.Workflow, sample func() DeadLetterSample)

DeadLettered records that wf parked a message. sample builds what the mail shows of it and is called only while the digest still has room to show it, so a burst costs a counter increment per message past the first few.

It runs on the engine's write path and never blocks on the network.

func (*Service) FlushDeadLetters added in v1.18.4

func (s *Service) FlushDeadLetters()

FlushDeadLetters mails every open window now. Shutdown calls it so the dead letters of the last minute are not lost with the process; Wait or WaitFor then waits for the sends.

func (*Service) Notify

func (s *Service) Notify(ctx context.Context, title, message string, wf storage.Workflow)

Notify raises an alert at ERROR. Most alerts are faults; the ones that are not go through NotifyLevel.

func (*Service) NotifyLevel added in v1.11.0

func (s *Service) NotifyLevel(ctx context.Context, level, title, message string, wf storage.Workflow)

NotifyLevel raises an alert at an explicit severity.

func (*Service) SetLogger added in v1.11.0

func (s *Service) SetLogger(l hermod.Logger)

SetLogger routes provider failures to the process log.

They went to fmt.Printf, so a rejected Telegram token or a webhook returning 500 appeared on stdout and nowhere else: not in the structured log, not in the log table, not in the UI. The one thing an operator needs to know about an alerting channel is that it has stopped working.

func (*Service) SetResolver added in v1.18.4

func (s *Service) SetResolver(r Resolver)

SetResolver sets how secret and env references in the settings resolve. Call it while wiring up, before alerts flow: providers read it unlocked.

func (*Service) SetStorage

func (s *Service) SetStorage(s2 storage.Storage)

func (*Service) Wait added in v1.11.0

func (s *Service) Wait()

Wait blocks until every dispatched notification has finished. It exists for shutdown and for tests; the send path itself never waits.

func (*Service) WaitFor added in v1.11.0

func (s *Service) WaitFor(d time.Duration) bool

WaitFor blocks until in-flight notifications finish or d elapses, and reports whether they finished.

Shutdown needs this. The fan-out runs on its own goroutine, so the alert saying a worker is going down was raced by the process exiting — dispatched, never sent. Waiting without a bound is the opposite mistake: an unreachable channel would hold the shutdown open for the full send timeout.

type SlackNotificationProvider

type SlackNotificationProvider struct {
	// contains filtered or unexported fields
}

func NewSlackNotificationProvider

func NewSlackNotificationProvider(s storage.Storage) *SlackNotificationProvider

func (*SlackNotificationProvider) Send

func (p *SlackNotificationProvider) Send(ctx context.Context, title, message string, wf storage.Workflow) error

func (*SlackNotificationProvider) SetStorage

func (p *SlackNotificationProvider) SetStorage(s storage.Storage)

func (*SlackNotificationProvider) Type

type TelegramNotificationProvider

type TelegramNotificationProvider struct {
	// contains filtered or unexported fields
}

func NewTelegramNotificationProvider

func NewTelegramNotificationProvider(s storage.Storage) *TelegramNotificationProvider

func (*TelegramNotificationProvider) Send

func (p *TelegramNotificationProvider) Send(ctx context.Context, title, message string, wf storage.Workflow) error

func (*TelegramNotificationProvider) SetStorage

func (p *TelegramNotificationProvider) SetStorage(s storage.Storage)

func (*TelegramNotificationProvider) Type

type TestResult

type TestResult struct {
	Channel string `json:"channel"`
	Status  string `json:"status"` // ok | skipped | error
	Error   string `json:"error,omitempty"`
}

type UINotificationProvider

type UINotificationProvider struct {
	// contains filtered or unexported fields
}

func NewUINotificationProvider

func NewUINotificationProvider(s storage.Storage) *UINotificationProvider

func (*UINotificationProvider) Send

func (p *UINotificationProvider) Send(ctx context.Context, title, message string, wf storage.Workflow) error

func (*UINotificationProvider) SendLeveled added in v1.11.0

func (p *UINotificationProvider) SendLeveled(ctx context.Context, level, title, message string, wf storage.Workflow) error

SendLeveled records the alert in the log table at the severity it was raised with, so a routine stop does not read as a failure.

func (*UINotificationProvider) SetStorage

func (p *UINotificationProvider) SetStorage(s storage.Storage)

func (*UINotificationProvider) Type

func (p *UINotificationProvider) Type() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL