Skip to content

Slack

patrol.Slack posts one message per channel and per project in the batch. Each event in that project becomes one attachment, coloured by type: blue for Info, red for Error, yellow for Debug.

An attachment carries, in order: the fields as bold name and value lines, the message in a code block, the stacktrace with an editor link per frame, links to any uploaded files, and the mentions.

patrol.NewSlack(opts patrol.SlackOptions, clientOpts ...slack.Option) (*patrol.Slack, error)

Token and Channels are required and are checked before the client is built. The variadic slack.Option values go straight to slack-go, which is how you point it at a test server or give it your own HTTP client.

Field Type JSON YAML and TOML Required
Token string token Token yes
Channels []string channels Channels yes
AcceptChannelEvent func(channel string, e *patrol.Event) bool - - no, set in code

Channels holds channel ids (C0123456789), which is what the Slack UI calls “Copy link to channel” and what the API is happiest with. AcceptChannelEvent is tagged - in all three formats because it is a function: it cannot come from a config file. Return false and that event is left out of that channel’s message; a channel whose events are all filtered out gets no message at all.

There is no TimeZone option here, and no capture time in the message. Slack stamps its own posting time on every message and a second clock next to it reads as a bug.

slack.yml
Token: your_bot_token_token
Channels:
- your_channel_id

chat:write to post. files:write as well if any event carries a file. The bot has to be a member of every channel in Channels.

producer.Test(ctx) calls Slack’s auth.test and checks that the response carries a bot id. A token that is valid but belongs to a user rather than a bot fails here, which is the mistake worth catching at startup.

From _examples/basic/program, which loads each options struct from its own YAML file:

// source: _examples/basic/program/main.go#L61-L66
// Slack.
slackFlusher, err := patrol.NewSlack(readConfig[patrol.SlackOptions]("slack.yml"))
if err != nil {
log.Fatal(err)
}
eventFlushers = append(eventFlushers, slackFlusher)

A long message is collapsed. Past 500 bytes the message is cut at a space, ... is appended, and a “Show more” button is added whose value is the event id. The button does nothing until you run an interactive endpoint that handles it and fetches the full text. If you do not have one, keep messages short and put the detail in fields.

Files are uploaded once per channel. WithFile costs an upload plus a file-info call for every channel in Channels, for every event that carries it. Three channels and two files is six uploads. Attach files on the events that need them rather than on all of them.

A failure stops that flush. Posting is a loop over channels and project groups, and the first error returns. Channels after the failing one do not get the message in that flush. The error reaches OnError prefixed with slack:, and the next batch starts over.

Mentions are Slack ids. WithMentions("U0123456789") renders as <@U0123456789>. A display name does not work, and no other destination renders mentions at all.