Skip to content

Core Logging

Fasq provides built-in logging capabilities through FasqLogger to help you debug and monitor query and mutation lifecycle events in your application.

FasqLogger implements the QueryClientObserver interface and automatically logs all query and mutation events with emoji-coded messages for easy visual scanning. It’s designed with privacy-first principles, ensuring sensitive data is not logged by default.

Add FasqLogger to your QueryClient to start logging:

import 'package:fasq/fasq.dart';
void main() {
final client = QueryClient();
// Add logger to observe all query and mutation events
client.addObserver(FasqLogger());
runApp(MyApp());
}

FasqLogger provides three configuration options:

OptionTypeDefaultDescription
enabledbooltrueEnable or disable logging entirely
showDataboolfalseInclude data in success logs (privacy-first: disabled by default)
truncateLengthint100Maximum length of data strings before truncation

By default, showData is set to false to protect sensitive information. Only enable it in development or when you’re certain the data is safe to log.

// Development: Show data for debugging
final devLogger = FasqLogger(
enabled: true,
showData: true,
truncateLength: 200,
);
// Production: Hide data for privacy
final prodLogger = FasqLogger(
enabled: true,
showData: false, // Default - data is hidden
);

Loading:

⏳ [Fetch] user:123

Success:

✅ [Success] user:123 (150ms) {"id": 123, "name": "John"}

Error:

❌ [Error] user:123: NetworkException: Connection timeout

Loading:

🚀 [Mutation] {"userId": 123, "action": "update"}

Success:

✅ [Mutation Success] {"userId": 123} (120ms) {"success": true}

Error:

❌ [Mutation Error] {"userId": 123}: ValidationError: Invalid input
final client = QueryClient();
client.addObserver(FasqLogger());

Enable data logging with truncation for development:

final logger = FasqLogger(
enabled: true,
showData: true,
truncateLength: 50, // Truncate long data strings
);
client.addObserver(logger);

Disable logging in production:

final logger = FasqLogger(
enabled: !kReleaseMode, // Only log in debug mode
showData: false,
);
client.addObserver(logger);

You can add multiple observers to the same client:

final client = QueryClient();
// Logger for console output
client.addObserver(FasqLogger());
// Custom observer for analytics
client.addObserver(AnalyticsObserver());
// Custom observer for error tracking
client.addObserver(ErrorTrackingObserver());

When a query is fetched, you’ll see:

⏳ [Fetch] todos
✅ [Success] todos (234ms)

With data enabled:

⏳ [Fetch] todos
✅ [Success] todos (234ms) [{"id": 1, "title": "Buy milk"}, {"id": 2, "title": "Walk dog"}]

When a mutation is executed:

🚀 [Mutation] {"todoId": 1, "completed": true}
✅ [Mutation Success] {"todoId": 1, "completed": true} (89ms)

Errors include the query key and error details:

⏳ [Fetch] user:123
❌ [Error] user:123: HttpException: 404 Not Found

FasqLogger supports structured error logging with rich context when used with the error tracking system:

final logger = FasqLogger();
client.addObserver(logger);
// When errors occur with error context, structured logging is used
logger.logError(
error,
stackTrace,
errorContext, // Optional FasqErrorContext
);

When error context is provided, the logger outputs structured data:

Fasq Query Error:
message: Fasq Query Error
errorType: SocketException
errorMessage: Failed host lookup
queryKey: [user-profile]
retryCount: 2
staleTimeMs: 300000
networkStatus: offline
sanitizedQueryOptions: {enabled: true, staleTime: 300000, ...}

This structured format makes it easier to parse and analyze errors in production. For more details on error tracking and context, see Error Tracking.

Long data strings are automatically truncated:

final logger = FasqLogger(
showData: true,
truncateLength: 20,
);

Output:

✅ [Success] large-data (150ms) {"very": "long data str...
  1. Development Only: Consider disabling logging in production builds:

    FasqLogger(enabled: !kReleaseMode)
  2. Privacy First: Keep showData: false in production to avoid logging sensitive user data.

  3. Performance: Logging has minimal overhead, but you can disable it entirely if needed.

  4. Multiple Loggers: Use different logger configurations for different environments (dev, staging, prod).

Constructor ParameterTypeDefaultDescription
enabledbooltrueEnable/disable all logging
showDataboolfalseInclude data in success logs
truncateLengthint100Max data string length before truncation

FasqLogger implements all methods from QueryClientObserver:

  • onQueryLoading - Logs when a query starts fetching
  • onQuerySuccess - Logs successful query completion with duration
  • onQueryError - Logs query errors with error details
  • onQuerySettled - Cleans up internal tracking (no log output)
  • onMutationLoading - Logs when a mutation starts
  • onMutationSuccess - Logs successful mutation completion with duration
  • onMutationError - Logs mutation errors with error details
  • onMutationSettled - Cleans up internal tracking (no log output)

Additionally, FasqLogger provides:

  • logError(Object error, [StackTrace? stackTrace, FasqErrorContext? context]) - Logs errors with optional structured context. When context is provided, outputs structured data including query key, retry count, network status, and sanitized query options.

FasqLogger works seamlessly with QueryClient through the observer pattern:

final client = QueryClient();
final logger = FasqLogger();
// Add observer
client.addObserver(logger);
// Remove observer if needed
client.removeObserver(logger);
// Clear all observers
client.clearObservers();