pulse_logger is a Dart-first operational event logger for apps that need lightweight process tracing, QA/production debugging, and alert-style event delivery without adopting a full analytics stack.
It gives you a small SDK-style API:
logger.track(...)logger.debug(...)logger.info(...)logger.warning(...)logger.error(...)logger.critical(...)
The first built-in transports are:
ConsoleTransportfor local development.SlackTransportfor Slack Incoming Webhooks.PulseMultiTransportfor sending the same event to multiple destinations.
dependencies:
pulse_logger: ^0.2.1import 'package:pulse_logger/pulse_logger.dart';final logger = PulseLogger(
config: PulseConfig(
environment: 'QA',
appName: 'Checkout App',
),
transport: const ConsoleTransport(),
);
await logger.info(
event: 'user_login_started',
title: 'User login started',
message: 'The user selected Google as the authentication provider.',
properties: {
'source': 'google',
},
);final logger = PulseLogger(
config: PulseConfig(
environment: 'production',
appName: 'Checkout App',
minimumLevel: LogLevel.warning,
),
transport: SlackTransport(
webhookUrl: 'https://hooks.slack.com/services/...',
),
);
await logger.error(
event: 'payment_failed',
title: 'Payment failed',
error: error,
stackTrace: stackTrace,
properties: {
'gateway': 'stripe',
'amount': 49.99,
},
);final logger = PulseLogger.multiChannel(
config: PulseConfig(
environment: 'QA',
appName: 'Checkout App',
),
transports: [
const ConsoleTransport(),
SlackTransport(webhookUrl: webhookUrl),
],
);final config = PulseConfig(
environment: 'QA',
appName: 'Checkout App',
enabled: true,
silentFailures: true,
includePlatformContext: true,
minimumLevel: LogLevel.info,
defaultProperties: {
'team': 'mobile',
},
resolveProperties: () => {
'is_logged_in': true,
'user_id': 'user-123',
},
sensitiveKeys: [
'email',
'phone',
],
redactKeysBySubstring: true,
sessionId: 'session-123',
appVersion: '1.2.3',
buildNumber: '42',
);Important defaults:
enabled: truesilentFailures: trueincludePlatformContext: trueredactKeysBySubstring: trueminimumLevel: LogLevel.debugtimeout: Duration(seconds: 5)onPulseConfig; Slack transport also accepts its own timeout.defaultProperties: const {}sensitiveKeys: const []
webhookUrl intentionally belongs to SlackTransport, not PulseConfig, so channel-specific secrets stay at the transport boundary.
resolveProperties is useful for dynamic context that changes while the app is running, such as the current user, locale, country, request id, or feature flag state. These values are merged into each event before sanitization, and explicit per-event properties can override them.
Event properties are sanitized before transport delivery. Built-in keys include:
tokenaccess_tokenrefresh_tokenauthorizationpasswordsecretwebhookcredentialcard_numbercvvapi_keyprivate_key
Custom keys can be added with PulseConfig.sensitiveKeys.
final config = PulseConfig(
environment: 'QA',
appName: 'Checkout App',
sensitiveKeys: ['email'],
);Matching is case-insensitive. By default, keys are also redacted when they contain a sensitive key fragment, so firebase_id_token is redacted because it contains token. Nested maps/lists are sanitized recursively.
Set redactKeysBySubstring: false if you need exact-key matching only.
By default, pulse_logger should never break your app when event preparation or a transport fails.
final logger = PulseLogger(
config: PulseConfig(
environment: 'QA',
appName: 'Checkout App',
silentFailures: true,
),
transport: SlackTransport(webhookUrl: webhookUrl),
onError: (error, stackTrace) {
// Optional visibility into event preparation or transport failures.
},
);For tests or debug tooling, set silentFailures: false to rethrow event preparation or transport failures.
pulse_logger stays Dart-first and does not depend on Flutter, GetX, or package_info_plus. Flutter apps should collect app-specific context in the host app and pass it into PulseConfig.
final config = PulseConfig(
environment: envName,
appName: appName,
appVersion: packageInfo.version,
buildNumber: packageInfo.buildNumber,
resolveProperties: () {
final profile = mainController.userProfile;
return {
'is_logged_in': mainController.isLoggedIn,
'user_id': profile?.user.id,
'user_email': profile?.user.email,
'country_id': profile?.countryId,
'language': mainController.currentLanguage,
};
},
);This keeps the reusable package clean while preserving the richer context that an app-specific logger can provide.
The default SlackTransport implementation uses dart:io HttpClient, which is suitable for Dart VM, mobile, and desktop targets. For Flutter Web, provide the post override with a web-compatible HTTP implementation owned by your app:
final transport = SlackTransport(
webhookUrl: webhookUrl,
post: (url, headers, body) async {
// Use your app's web-compatible HTTP client here.
final response = await client.post(url, headers: headers, body: body);
return (statusCode: response.statusCode, body: response.body);
},
);If you already have a custom SlackLogger, keep app-owned concepts in the app and map them into Pulse Logger:
- Event constants such as
SlackLogEvent.googleAuthStartedcan become app-owned string constants passed toevent:. - Environment configuration maps to
PulseConfig(environment: ..., appName: ..., minimumLevel: ...). - User context previously read from a controller should move into
resolveProperties. - Long Slack body text should use
message, whiletitlestays short and human-readable. - GetX or another DI framework can own a
PulseLoggerinstance, but the package itself stays framework-neutral.
Do not treat Slack Incoming Webhook URLs as secure secrets when embedded directly in Flutter or other mobile binaries. A webhook URL shipped inside a public app can be extracted from the binary by a motivated user.
pulse_logger helps reduce risk with sanitization, level filtering, and silent failure behavior, but it does not make embedded mobile secrets safe.
For sensitive production use, prefer this architecture:
Flutter app -> your backend/serverless proxy -> Slack Incoming Webhook
Your proxy can enforce authentication, rate limits, payload validation, environment rules, and webhook rotation without shipping the Slack webhook URL to clients.
package:logger is great for local/log formatting workflows. pulse_logger is focused on operational event delivery: named events, levels, structured metadata, sanitization, and transport routing to destinations such as Slack.
Included in 0.2.1:
- Dart-only SDK.
- Console transport.
- Slack Incoming Webhook transport.
- Multi-transport fan-out.
- Sanitized event properties.
- Dynamic property enrichment.
- Optional platform context.
- Long-form event messages.
- Level filtering.
- Silent failure handling.
Deferred:
- Offline queue/buffer.
- Rate limiting.
- Dedupe windows.
- Flutter-specific helper package.
- Additional transports such as Discord or Telegram.
