ispectify 7.0.0-dev2 copy "ispectify: ^7.0.0-dev2" to clipboard
ispectify: ^7.0.0-dev2 copied to clipboard

[pending analysis]

Pure-Dart structured logging, tracing, filtering, history, observers, export, and redaction core for the ISpect toolkit.

ispectify is the logging core of the ISpect toolkit. Pure Dart, no Flutter dependency. Use it in CLI tools, server-side Dart, and shared business-logic packages. You do not need ispect or the in-app panel to use it.

  • Typed log entries with explicit severity levels and log-type keys.
  • Filtering, bounded in-memory history, and configurable truncation.
  • Trace extensions for async, sync, and stream operations with timing and outcome tagging.
  • Observer hooks that forward selected events to your own sink.
  • A built-in redaction engine shared with the ispectify_* interceptor packages.

Install #

dependencies:
  ispectify: ^7.0.0-dev2

Logger-only quick start #

import 'package:ispectify/ispectify.dart';

final logger = ISpectLogger();

logger.info('Application started');
logger.warning('Cache miss, falling back to network');
logger.error('Payment gateway returned 502', exception, stackTrace);

ISpect is compile-time gated. Enable diagnostics explicitly when running or compiling a pure-Dart program:

dart run -DISPECT_ENABLED=true bin/main.dart
dart compile exe -DISPECT_ENABLED=true bin/main.dart

Without ISPECT_ENABLED=true, logger APIs are inert and retain no diagnostics. Do not add the flag to public production release commands.

For Flutter UI, navigator diagnostics, and session browsing, add ispect on top of this logger. Keep ispectify alone when logs are consumed by the console, an observer, or your own UI.

Custom log types:

logger.log(
  'User signed in',
  logLevel: LogLevel.info,
  type: const ISpectLogType('auth'),
);

Configuration #

final logger = ISpectLogger(
  options: ISpectLoggerOptions(
    enabled: true,
    useHistory: true,
    useConsoleLogs: true,
    maxHistoryItems: 5000,
    logTruncateLength: 4000,
  ),
);

Streaming-only, with no in-memory history. Use this when every event is forwarded to an observer:

final logger = ISpectLogger(
  options: const ISpectLoggerOptions(useHistory: false),
);

Filter by log-type key. Suppress noisy categories without changing call sites:

final logger = ISpectLogger(
  filter: ISpectFilter(logTypeKeys: {'analytics', 'route'}),
);

Filter by level. Drop debug and verbose, keep info and above:

final logger = ISpectLogger(
  logger: ISpectBaseLogger(
    filter: LogLevelRangeFilter(minLevel: LogLevel.info),
  ),
);

Rolling file history #

On Dart IO platforms, opt into bounded JSON Lines persistence by supplying a cache directory owned by your application:

final loggerOptions = ISpectLoggerOptions(maxHistoryItems: 10_000);
final history = RollingFileLogHistory(
  loggerOptions,
  directoryProvider: () async => '/path/to/application-cache',
  options: const FileLogHistoryOptions(
    maxSessionDays: 7,
    maxFileSize: 5 * 1024 * 1024,
    maxTotalSize: 50 * 1024 * 1024,
  ),
);
final logger = ISpectLogger(options: loggerOptions, history: history);

Records are redacted before they reach disk, grouped into daily rolling segments, and deduplicated by log ID. Retention can delete the oldest or largest closed segments, or GZIP old segments with SessionCleanupStrategy.archiveOldest. Legacy 4.x daily JSON arrays remain readable. The implementation stays inert unless ISPECT_ENABLED is present; web consumers should keep the default in-memory history.

Disabling the global ISpectRedaction.enabled switch is an explicit opt-out that also disables redaction before file persistence and JSON export.

The directory provider must return an existing app-private directory. On POSIX, the provider must be owner-only so its traversal permissions protect all child artifacts; managed session/date directories must not be group- or world-writable. Symbolic links and paths outside the managed root are rejected. An active process running as the same OS principal is outside the dart:io rolling-history threat model; use in-memory history or a platform-native storage service when that attacker is in scope.

Persistence does not invoke supplied Exception, Error, or StackTrace formatting methods. Exceptions and errors use a bounded safe type descriptor; stack traces use a fail-closed marker.

Console output #

Console entries use a compact, single-line format by default. Switch to a boxed format — each entry framed for visual separation in a busy console — by setting ConsoleSettings.formatter:

final logger = ISpectLogger(
  logger: ISpectBaseLogger(
    settings: ConsoleSettings(formatter: const BoxedLogEntryFormatter()),
  ),
);
┌──────────────────────────────────────────────
│ INFO    [route] | 17:20:42.910 | Push | / → /detail
└──────────────────────────────────────────────

The boxed formatter renders the same fields as the default (so redaction and network bodies carry over), and the border glyph and width follow ConsoleSettings.lineSymbol / maxLineWidth. Implement ILogEntryFormatter for a fully custom layout; the default is the compact HumanLogEntryFormatter.

By default, entries are written with print (browser console on web). To route them through dart:developer instead — so they appear in the DevTools logging view with structured metadata — pass the developerLogOutput sink:

final logger = ISpectLogger(
  logger: ISpectBaseLogger(
    output: developerLogOutput,
    settings: ConsoleSettings(formatter: const BoxedLogEntryFormatter()),
  ),
);

Each entry becomes a single log() call, so multi-line boxed output stays intact, and the log level is mapped through. Messages arrive colored when ConsoleSettings.enableColors is on; if your log viewer shows ANSI codes as raw escape sequences, pair it with enableColors: false. Flutter apps initialized via ISpect.run / ISpectFlutter.init() already use a platform-adaptive output (dart:developer on iOS/macOS, print elsewhere); developerLogOutput is for code that wires ISpectBaseLogger directly, or any custom LoggerOutput.

Tracing #

Trace extensions wrap work in a paired start/end log entry with duration, outcome, and an optional result projection. You get one-line "did this domain action succeed?" entries in the log viewer instead of a flood of unrelated logs.

Each trace call takes an ISpectTraceCategory. Pre-built ones (networkCategory, dbCategory, authCategory, storageCategory, paymentCategory, and the rest) live in package:ispectify/ispectify.dart, or you can declare your own.

const userRepoCategory = ISpectTraceCategory(
  id: 'user-repo',
  successKey: 'user-repo-ok',
  errorKey: 'user-repo-error',
);

final users = await logger.traceAsync<List<User>>(
  category: userRepoCategory,
  source: 'user_repository',
  operation: 'fetch_list',
  run: () => userRepository.fetchAll(),
  projectResult: (list) => {'count': list.length},
);

traceSync and traceStream are also available. Each one records the duration, the exception, and the stack trace on failure.

Observers #

Observers receive a redacted copy of every log event in real time. Attach one per external sink. The local history keeps the original diagnostic entry; setting ISpectRedaction.enabled = false is the explicit opt-out that also makes observer delivery raw.

class GrafanaObserver extends ISpectObserver {
  const GrafanaObserver();

  @override
  void onLog(ISpectLogData data) { /* ship to Loki */ }

  @override
  void onError(ISpectLogData err) { /* ship to Loki */ }

  @override
  void onException(ISpectLogData err) { /* ship to Loki */ }
}

logger.addObserver(const GrafanaObserver());

HTTP log entries expose curlCommand/curlCommandWith(). Generated commands redact URLs, headers, and bodies by default and use --data-raw. Pass enableRedaction: false only for isolated local debugging.

Data redaction #

Sensitive data is masked before it reaches logs or observers. Redaction is on by default. The built-in rules cover auth headers, tokens, passwords, API keys, cookies, common PII (SSN, passport, driver's license), financial data (credit cards, IBAN), and phone numbers.

The default policy is a single source of truth. Configure it once and core logs, traces, persistence, network and database adapters, BLoC and Riverpod observers, supported exports, clipboard helpers, and cURL generation resolve it when each diagnostic operation runs.

Redaction works best paired with focused capture. Keep body and header logging off unless you actually need the payload, and register project-specific keys for the business identifiers only your application understands.

Global configuration #

import 'package:ispectify/ispectify.dart';

ISpectRedaction.configure(
  service: RedactionService(
    additionalSensitiveKeys: {
      'x-custom-secret',
      'internal_token',
    },
    additionalSensitiveKeyPatterns: [
      RegExp(r'my_app_secret_\w+', caseSensitive: false),
    ],
    fullyMaskedKeys: {'filename'},
    placeholder: '***',
    visibleEdgeLength: 3,
  ),
);

additionalSensitiveKeys and additionalSensitiveKeyPatterns extend the built-in policy. Use sensitiveKeys or sensitiveKeyPatterns only when you intentionally want to replace the corresponding defaults:

final replacementPolicy = RedactionService(
  sensitiveKeys: {
    'x-custom-secret',
    'internal_token',
  },
  sensitiveKeyPatterns: [
    RegExp(r'my_app_secret_\w+', caseSensitive: false),
  ],
);

Flutter apps can pass the same policy as ISpect.run(redactionService: ...); ISpect.dispose() restores the policy that was active before that run. An explicit RedactionService supplied to one integration stays local and takes precedence over the global policy. Existing integrations without an explicit service pick up later global reconfiguration. The policy is scoped to the current Dart isolate.

Local exceptions #

final redactor = RedactionService(
  ignoredKeys: {'mobile', 'platform_token'},
  ignoredValues: {'<test-token>', 'public-api-key'},
);

Disabling #

ISpectRedaction.configure(enabled: false) is the global content-masking opt-out. Each interceptor also accepts enableRedaction: false on its settings object for a local opt-out. Size limits, non-executing snapshots, private-storage checks, and the compile-time ISPECT_ENABLED gate remain enforced.

Only disable redaction in isolated local or deterministic test environments. Exported sessions and observer events should be handled according to the data they contain.

Security #

Exported logs are plain-text JSON. Do not write PII (emails, phone numbers, tokens) directly through logger.info(...). Rely on the redaction engine when values flow through network interceptors, and sanitize user input before passing it to a manual log call. See docs/SECURITY.md for the data-handling policy.

The ISpect toolkit #

ISpect is a modular monorepo. Pick the packages your project needs. Each one works on its own.

Package What it does
ispect Flutter UI: debug panel, log viewer, navigation observer, inspector integration.
ispect_layout Visual layout inspector with sizes, constraints, decorations, compare mode, and a color picker.
ispectify Pure-Dart logging core: typed log entries, filtering, tracing, observers.
ispectify_dio Dio HTTP interceptor with automatic redaction.
ispectify_http http package interceptor with automatic redaction.
ispectify_ws Provider-agnostic WebSocket capture (any client) with automatic redaction.
ispectify_db Database operation tracing for SQL, ORMs, and KV stores.
ispectify_bloc BLoC event, state, transition, and error observer.
ispectify_riverpod Riverpod provider add, update, dispose, and failure observer.

Contributing #

Contributions are welcome. See CONTRIBUTING.md for guidelines, and open issues or pull requests at the ISpect repository.

License #

MIT. See LICENSE.


7
likes
0
points
4.76k
downloads

Publisher

verified publishershodev.live

Weekly Downloads

Pure-Dart structured logging, tracing, filtering, history, observers, export, and redaction core for the ISpect toolkit.

Repository (GitHub)
View/report issues

License

(pending) (license)

Dependencies

ansicolor, collection, meta, web

More

Packages that depend on ispectify