format method
Builds the JSON-encodable payload to deliver to the remote endpoint.
error is the log record that triggered the alert.
contextLogs are recently buffered log records that preceded the error.
metadata contains arbitrary key-value pairs (e.g. app name, version).
Implementation
@override
Map<String, dynamic> format(
LogRecord error,
List<LogRecord> contextLogs,
Map<String, String> metadata,
) {
final blocks = <Map<String, dynamic>>[];
// Header
blocks.add({
'type': 'header',
'text': {
'type': 'plain_text',
'text': ':rotating_light: Error: ${error.className}',
},
});
// App metadata (e.g. app name, version, environment)
if (metadata.isNotEmpty) {
final metaText = metadata.entries
.map((e) => '*${e.key}:* ${e.value}')
.join(' | ');
blocks.add({
'type': 'context',
'elements': [
{'type': 'mrkdwn', 'text': metaText},
],
});
}
// Error message
blocks.add({
'type': 'section',
'text': {'type': 'mrkdwn', 'text': '*Message:* ${error.message}'},
});
// Attrs
if (error.attrs != null && error.attrs!.isNotEmpty) {
final attrsText = error.attrs!.entries
.map((e) => '${e.key}: ${e.value}')
.join(' | ')
.replaceAll('\n', ' ');
blocks.add({
'type': 'section',
'text': {'type': 'mrkdwn', 'text': '*Attrs:* $attrsText'},
});
}
// Error object
if (error.error != null) {
blocks.add({
'type': 'section',
'text': {'type': 'mrkdwn', 'text': '*Error:* `${error.error}`'},
});
}
// Stack trace
if (error.stackTrace != null) {
final st = error.stackTrace.toString();
final truncated = st.length > 2000 ? '${st.substring(0, 2000)}...' : st;
blocks.add({
'type': 'section',
'text': {'type': 'mrkdwn', 'text': '*Stack Trace:*\n```$truncated```'},
});
}
// Context logs
if (contextLogs.isNotEmpty) {
final contextText = contextLogs
.map((r) {
final base =
'[${r.timestamp.toIso8601String()}]'
' ${_icons[r.level.index]}'
' [${r.level.name.toUpperCase()}]'
' ${r.className}: ${r.message}';
if (r.attrs == null || r.attrs!.isEmpty) return base;
final attrsText = r.attrs!.entries
.map((e) => '${e.key}: ${e.value}')
.join(', ');
return '$base {$attrsText}'.replaceAll('\n', ' ');
})
.join('\n');
final truncated = contextText.length > 2000
? '${contextText.substring(0, 2000)}...'
: contextText;
blocks.add({
'type': 'section',
'text': {'type': 'mrkdwn', 'text': '*Context Logs:*\n```$truncated```'},
});
}
// Timestamp
blocks.add({
'type': 'context',
'elements': [
{
'type': 'mrkdwn',
'text': 'Occurred at: ${error.timestamp.toIso8601String()}',
},
],
});
return {'blocks': blocks};
}