Any Logger Email
An email appender extension for Any Logger that enables sending log notifications via SMTP to any email address. Perfect for critical error alerts, daily digests, system monitoring, and production incident notifications.
Features
- Universal SMTP Support - Works with any SMTP server including Gmail, Office 365, SendGrid, and more
- App Password Support - Clear API for using app-specific passwords (required by Gmail/Outlook)
- Smart Batching - Automatically batches logs to reduce email volume
- Rate Limiting - Prevents email flooding with configurable limits
- HTML & Plain Text - Beautiful HTML emails with fallback to plain text
- Priority Alerts - Immediate sending for critical errors
- Multiple Recipients - Support for TO, CC, and BCC recipients
- Flexible Templates - Customizable email templates or use built-in formats
- Service Presets - Pre-configured settings for popular email services
Installation
dependencies:
any_logger: ^x.y.z
any_logger_email: ^2.0.0 # Note: v2.0.0+ has breaking changes
To register the EMAIL appender you have to import the library:
import 'package:any_logger/any_logger.dart';
import 'package:any_logger_email/any_logger_email.dart';
and call:
AnyLoggerEmailExtension.register();
Quick Start
🚨 Authentication Requirements
Gmail, Outlook, Yahoo and most email providers no longer accept regular passwords for SMTP.
You MUST use:
- App-specific passwords for Gmail, Outlook, Yahoo Mail
- API keys for SendGrid, Mailgun, AWS SES
- SMTP credentials for dedicated email services
Simple Setup with Gmail
// ✅ CORRECT: Using app-specific password
await LoggerBuilder()
.console(level: Level.INFO)
.gmailWithAppPassword(
emailAddress: 'your.app@gmail.com',
appPassword: 'abcd-efgh-ijkl-mnop', // 16-char app password from Google
toEmails: ['admin@example.com'],
level: Level.ERROR,
)
.build();
// ❌ WRONG: This will NOT work
// .gmail(username: 'your.app@gmail.com', password: 'YourGooglePassword123!')
To get a Gmail app password:
- Enable 2-factor authentication on your Google account
- Go to Google Account → Security → App passwords
- Generate a new app password
- Use the 16-character password shown (format: xxxx-xxxx-xxxx-xxxx)
Custom SMTP Server
await LoggerBuilder()
.email(
smtpHost: 'smtp.company.com',
smtpPort: 587,
fromEmail: 'logger@company.com',
toEmails: ['dev-team@company.com'],
username: 'logger@company.com',
passwordOrApiKey: 'secure_password_or_api_key', // Note the renamed parameter
level: Level.ERROR,
sendAsHtml: true,
)
.build();
Email Service Configuration
Gmail (App Password Required)
// Using LoggerBuilder extension
await LoggerBuilder()
.gmailWithAppPassword(
emailAddress: 'your.app@gmail.com',
appPassword: 'xxxx-xxxx-xxxx-xxxx', // NOT your Google password!
toEmails: ['alerts@example.com'],
)
.build();
// Using EmailAppenderBuilder
final appender = await emailAppenderBuilder()
.withGmailAppPassword('your.app@gmail.com', 'xxxx-xxxx-xxxx-xxxx')
.withTo(['alerts@example.com'])
.withLevel(Level.ERROR)
.build();
Get your Gmail app password here: Google Account → Security → App passwords
Office 365 / Outlook (App Password Required)
// Using LoggerBuilder extension
await LoggerBuilder()
.outlookWithAppPassword(
emailAddress: 'user@company.com',
appPassword: 'generated-app-password', // NOT your Microsoft password!
toEmails: ['team@company.com'],
)
.build();
// Using EmailAppenderBuilder
final appender = await emailAppenderBuilder()
.withOutlookAppPassword('user@company.com', 'generated-app-password')
.withTo(['team@company.com'])
.build();
Get your Outlook app password here: Microsoft Account → Security → App passwords
SendGrid (API Key)
// Using LoggerBuilder extension
await LoggerBuilder()
.sendGridWithApiKey(
apiKey: 'SG.actualApiKeyHere',
fromEmail: 'noreply@yourapp.com',
toEmails: ['ops@yourapp.com'],
)
.build();
// Using EmailAppenderBuilder
final appender = await emailAppenderBuilder()
.withSendGridApiKey('SG.actualApiKeyHere', 'noreply@yourapp.com')
.withTo(['ops@yourapp.com'])
.build();
Mailgun (API Key)
// Using LoggerBuilder extension
await LoggerBuilder()
.mailgunWithApiKey(
apiKey: 'key-xxxxx',
domain: 'mg.yourdomain.com',
fromEmail: 'alerts@yourdomain.com',
toEmails: ['admin@yourdomain.com'],
)
.build();
// Using EmailAppenderBuilder
final appender = await emailAppenderBuilder()
.withMailgunApiKey('key-xxxxx', 'mg.yourdomain.com', 'alerts@yourdomain.com')
.withTo(['admin@yourdomain.com'])
.build();
AWS SES
final appender = await emailAppenderBuilder()
.withAwsSes(
region: 'us-east-1',
smtpUsername: 'AKIA...', // SMTP credentials from SES console
smtpPassword: 'BLx9...', // NOT your AWS IAM credentials!
fromEmail: 'noreply@yourdomain.com',
)
.withTo(['alerts@yourdomain.com'])
.build();
Custom SMTP
final appender = await emailAppenderBuilder()
.withCustomSmtp(
host: 'mail.server.com',
port: 465,
username: 'logger@server.com',
password: 'app-specific-password-or-api-key',
ssl: true,
fromEmail: 'logger@server.com',
)
.withTo(['admin@server.com'])
.build();
Configuration Options
Using Builder Pattern
final appender = await emailAppenderBuilder()
.withSmtp('smtp.example.com', 587)
.withAppPassword('user@example.com', 'app-specific-password') // Clear method name
.withFrom('app@example.com', 'My App')
.withTo(['dev@example.com', 'ops@example.com'])
.withCc(['manager@example.com'])
.withBcc(['archive@example.com'])
.withReplyTo('support@example.com')
.withSubjectPrefix('[PRODUCTION]')
.withLevel(Level.ERROR)
.withBatchSize(10)
.withBatchIntervalMinutes(5)
.withRateLimit(20) // Max 20 emails per hour
.withHtmlFormat(true)
.withStackTraces(true)
.withMetadata(true)
.withGroupByLevel(false) // Changed default: chronological for better debugging
.withImmediateErrors(true)
.withErrorThreshold(3)
.build();
Configuration Parameters
Parameter | Type | Default | Description |
---|---|---|---|
smtpHost |
String | Required | SMTP server hostname |
smtpPort |
int | Required | SMTP server port (25, 465, 587, etc.) |
ssl |
bool | false | Use SSL/TLS encryption |
allowInsecure |
bool | false | Allow insecure connections |
ignoreBadCertificate |
bool | false | Ignore certificate errors |
fromEmail |
String | Required | Sender email address |
fromName |
String | null | Sender display name |
toEmails |
List/String | Required | Recipient email(s) |
ccEmails |
List/String | [] | CC recipients |
bccEmails |
List/String | [] | BCC recipients |
replyTo |
String | null | Reply-to address |
username |
String | null | SMTP username |
passwordOrApiKey |
String | null | App password or API key (NOT regular password) |
level |
Level | ERROR | Minimum log level to email |
subjectPrefix |
String | 'LOG ' |
Email subject prefix |
includeHostname |
bool | true | Include hostname in emails |
includeAppInfo |
bool | true | Include app version/device ID |
batchSize |
int | 50 | Logs per batch |
batchIntervalMinutes |
int | 5 | Minutes before sending partial batch |
maxEmailsPerHour |
int | 20 | Rate limit per hour |
sendAsHtml |
bool | true | Send HTML formatted emails |
includeStackTrace |
bool | true | Include stack traces |
includeMetadata |
bool | true | Include metadata |
groupByLevel |
bool | false | Group logs by level (v2.0 default change) |
sendImmediatelyOnError |
bool | true | Send immediately on errors |
immediateErrorThreshold |
int | 10 | Error count to trigger immediate send |
Presets
Critical Alert Preset
final appender = await emailAppenderBuilder()
.withGmailAppPassword('alerts@gmail.com', 'xxxx-xxxx-xxxx-xxxx')
.withTo(['oncall@example.com'])
.withCriticalAlertPreset()
.build();
// Configures:
// - Level: ERROR
// - Immediate sending on first error
// - Include full stack traces
// - Chronological order (not grouped)
// - Subject: [CRITICAL ALERT]
Daily Digest Preset
final appender = await emailAppenderBuilder()
.withSendGridApiKey('SG.xxxxx', 'reports@example.com')
.withTo(['management@example.com'])
.withDailyDigestPreset()
.build();
// Configures:
// - Level: INFO
// - 24-hour batching
// - Group by level for summary
// - No immediate sending
// - Subject: [DAILY LOG DIGEST]
Use Cases
Production Error Monitoring
await LoggerBuilder()
.file(filePattern: 'app', level: Level.INFO)
.sendGridWithApiKey(
apiKey: env['SENDGRID_API_KEY']!, // Use environment variables!
fromEmail: 'errors@myapp.com',
toEmails: ['oncall@myapp.com'],
level: Level.ERROR,
subjectPrefix: '[PROD ERROR]',
batchSize: 5,
sendImmediatelyOnError: true,
)
.build();
Daily Summary Reports
await LoggerBuilder()
.console(level: Level.INFO)
.gmailWithAppPassword(
emailAddress: 'reports@company.com',
appPassword: env['GMAIL_APP_PASSWORD']!, // From environment
toEmails: ['management@company.com'],
level: Level.INFO,
subjectPrefix: '[Daily Report]',
batchSize: 1000,
batchIntervalMinutes: 1440, // 24 hours
sendImmediatelyOnError: false,
)
.build();
Troubleshooting
❌ Authentication Failures
Problem: "Invalid credentials" or "Authentication failed"
Solutions:
- Gmail: You MUST use an app password, not your Google password
- Outlook: You MUST use an app password if 2FA is enabled
- Yahoo: Requires an app password from Account Security settings
- SendGrid: Use API key starting with 'SG.'
- Check for spaces: Ensure no leading/trailing spaces in passwords
Common Issues
- "Less secure apps" error: This setting no longer exists. Use app passwords instead.
- Port issues: Use 587 for TLS, 465 for SSL, 25 for unencrypted (not recommended)
- Rate limiting: Reduce
maxEmailsPerHour
if hitting provider limits - Large attachments: Reduce
batchSize
or increasebatchIntervalMinutes
Enable Debug Logging
await LoggerBuilder()
.withSelfDebug(Level.DEBUG)
.email(/* config */)
.build();
Security Best Practices
-
Never commit credentials:
// ✅ GOOD appPassword: env['GMAIL_APP_PASSWORD']! // ❌ BAD appPassword: 'abcd-efgh-ijkl-mnop'
-
Use app-specific passwords: Never use your main account password
-
Rotate credentials regularly: Especially for production systems
-
Limit recipients: Use groups/aliases instead of individual emails
-
Test configuration: Use test mode during development:
final appender = await emailAppenderBuilder() .withGmailAppPassword('test@gmail.com', 'test-password') .withTo(['test@example.com']) .build(test: true); // No emails sent
License
MIT License - see LICENSE file for details.
Support
- Main Package: any_logger
- Issues: GitHub Issues
- Examples: See
/example
folder in the package
Part of the Any Logger ecosystem.
💚 Funding
Happy Logging! 🎉