From c18341d2f0adeaf39635cbc8162a16687ae12938 Mon Sep 17 00:00:00 2001 From: Meetkumar Shah Date: Fri, 22 Nov 2024 16:20:20 +0530 Subject: [PATCH 1/8] Added: Datadog content added to datadog_logging_service --- .../3._cloud/4.datadog_logging_service.md | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/4.datadog_logging_service.md diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/4.datadog_logging_service.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/4.datadog_logging_service.md new file mode 100644 index 00000000..2bd8b345 --- /dev/null +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/4.datadog_logging_service.md @@ -0,0 +1,191 @@ +--- +toc: true +title: DataDog Logging Service +--- + + + +::alert{type="warning" class="p-4 mb-4 text-sm text-yellow-800 rounded-lg bg-yellow-50 dark:bg-gray-800 dark:text-yellow-300" role="alert"} +Dependencies + +- [datadog_flutter_plugin](https://pub.dev/packages/datadog_flutter_plugin) +- [datadog_tracking_http_client](https://pub.dev/packages/datadog_tracking_http_client) + +- implements LoggingService + +:: + +::alert{type="danger" class="p-4 mb-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400" role="alert"} +Developer Guide + +- Developer should never use this service directly, use [Logging Library](../logging_library.md) instead. + +:: + + + + +## DataDogLoggingService + +- Implements [LoggingService](./logging_service.md). + +- Provides methods which extends datadog services for Action, Error, Attribute and SetUserInfo. + +## Configuration + +- To configure datadog for your project you just have to pass `datadogConfig` in [environment config](../../../env.md). + + - You will have to add datadog `clientToken` and `applicationId` in `datadogConfig`. + - Adding `site` to send information to. It determines the server for uploading RUM(Real User Monitoring) events + + - And you can configure SampleRate to trace uncaught errors. Value of `tracesSampleRate` (in `datadogConfig`) can be between 0 and 100. if value of `tracesSampleRate` is `20.0` then it will record 20% of uncaught errors, if it's `100.0` it will record 100% of uncaught errors. + - You can configure `reportFlutterPerformance` and `nativeCrashReportEnabled` to trace performance of application and enable native crash reporting. + - A list of `firstPartyHosts` and the types of tracing headers Datadog should automatically inject on resource calls. This is used in conjunction with Datadog network tracking packages like `datadog_tracking_http_client` + +- Check [this](https://docs.datadoghq.com/real_user_monitoring/mobile_and_tv_monitoring/advanced_configuration/flutter/) datadog blog for, how to get config values. + +## Source code +```dart +import 'dart:ui'; +import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart'; +import 'package:datadog_tracking_http_client/datadog_tracking_http_client.dart'; +import 'package:flutter/material.dart'; +import '../../../env/env.dart'; +import '../_local/console_service.dart'; +import '../models/log.dart'; +import 'logging_service.dart'; + +class DataDogLoggingService implements LoggingService { + static DatadogSdk? datadogSdk; + + static EnvironmentConfig get _config => EnvironmentConfig.getConfig; + + @override + Future init({ + required Widget app, + }) async { + // Initialize DataDog + datadogSdk ??= DatadogSdk.instance; + + // Initialize the logger with network info enabled + final configuration = DatadogLoggerConfiguration(networkInfoEnabled: true); + datadogSdk?.logs?.createLogger(configuration); + + // Initialize DataDog with the configuration and consent + datadogSdk?.initialize(await _generateConfig(), TrackingConsent.granted); + + // Initialize error handling + _initializeErrorHandling(); + + return app; + } + +// Generate config for DataDog + static Future _generateConfig() async { + return DatadogConfiguration( + clientToken: _config.datadogConfig?.clientToken ?? '', + env: _config.envType.toLowerCase(), + site: _config.datadogConfig?.site ?? DatadogSite.us5, + nativeCrashReportEnabled: _config.datadogConfig?.nativeCrashReportEnabled ?? false, + loggingConfiguration: DatadogLoggingConfiguration(), + rumConfiguration: DatadogRumConfiguration( + applicationId: _config.datadogConfig?.applicationId ?? '', + sessionSamplingRate: 50, + reportFlutterPerformance: _config.datadogConfig?.reportFlutterPerformance ?? false, + traceSampleRate: _config.datadogConfig?.tracesSampleRate ?? 20.0, + ), + version: _config.version, + firstPartyHosts: _config.datadogConfig?.firstPartyHosts ?? [], + )..enableHttpTracking(); + } + +// Initialize error handling + static void _initializeErrorHandling() { + final originalOnError = FlutterError.onError; + FlutterError.onError = (details) { + FlutterError.presentError(details); + datadogSdk?.rum?.handleFlutterError(details); + originalOnError?.call(details); + }; + final platformOriginalOnError = PlatformDispatcher.instance.onError; + PlatformDispatcher.instance.onError = (e, st) { + datadogSdk?.rum?.addErrorInfo( + e.toString(), + RumErrorSource.source, + stackTrace: st, + ); + return platformOriginalOnError?.call(e, st) ?? false; + }; + } + + @override + Future logException(throwable, {source, stackTrace, hint}) async { + datadogSdk?.rum?.addError( + throwable, + source ?? RumErrorSource.custom, + stackTrace: stackTrace, + attributes: hint, + ); + } + + @override + Future setUserInfoLogger({ + String? id, + String? name, + String? email, + Map? extraInfo, + }) async { + datadogSdk?.setUserInfo( + id: id, + name: name, + email: email, + extraInfo: extraInfo ?? {}, + ); + } + + @override + Future logEvent({ + required String message, + required EventType type, + Object? data, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } + + @override + Future addEventsLogger({ + required RumActionType rumActionType, + required String eventName, + Map? eventProperties, + }) async { + datadogSdk?.rum?.addAction( + rumActionType, + eventName, + eventProperties ?? {}, + ); + } + + @override + Future addSectionLogger({ + required String sectionName, + required sectionValue, + }) async { + datadogSdk?.rum?.addAttribute(sectionName, sectionValue); + } + + @override + Future logTransaction({ + required Function execute, + required TransactionDetails details, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } +} + + + +``` From 831ee7142fff92c8271f55f98ccb0b09d539cf2d Mon Sep 17 00:00:00 2001 From: Meetkumar Shah Date: Fri, 22 Nov 2024 17:20:58 +0530 Subject: [PATCH 2/8] Updated: logging library document --- .../logging_library/logging_library.md | 399 ++++++++++++------ 1 file changed, 267 insertions(+), 132 deletions(-) diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/logging_library.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/logging_library.md index 6afbe6e2..de4c8bb0 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/logging_library.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/logging_library.md @@ -12,16 +12,17 @@ Dependencies ## Overview -- This is the central logging library which handles cloud (e.g. sentry, crashanalytics) and local (e.g. Console/Terminal and Local Files For Device) Logs. +- This is the central logging library which handles cloud (e.g. sentry, datadog and crash analytics) and local (e.g. Console/Terminal and Local Files For Device) Logs. ::alert{type="info" class="p-4 mb-4 text-sm text-blue-800 border border-blue-300 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400 dark:border-blue-800" role="alert"} Cloud logging services -- As of now we have implemented [Sentry](./_cloud/sentry_logging_service.md) to log and measure performance. Any other service is not available yet. To setup Sentry please check [this](./_cloud/sentry_logging_service.md#configuration). -- You can check out more details about it here: [Sentry](./_cloud/sentry_logging_service.md) and [Performance](../performance_monitoring.md) -- Also to enable sentry; developer will have to pass `sentryConfig` in [environment](../../env.md). +- We have implemented [Sentry](./_cloud/sentry_logging_service.md) and [DataDog](./_cloud/datadog_logging_service.md) to log and measure performance. +- To enable sentry, will required `sentryConfig` in [environment](../../env.md). +- To enable datadog, will required `datadogConfig` in [environment](../../env.md). +- For more details check: [Sentry](./_cloud/sentry_logging_service.md), [DataDog](./_cloud/datadog_logging_service.md) and [Performance](../performance_monitoring.md) :: -- So developer should never use individual service in their application, they should always use Logging library for logging any kind of content. +- We should never use individual service in their application, they should always use Logging library for logging any kind of content. hierarchy @@ -30,7 +31,7 @@ Dependencies ## Use cases ::alert{type="info" class="p-4 mb-4 text-sm text-blue-800 border border-blue-300 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400 dark:border-blue-800" role="alert"} -use cases +Use cases: - To easily log content on cloud - To easily add another cloud logging service - To log transactions. e.g. I want to measure and log time of an api call @@ -46,6 +47,9 @@ Currently, our logs are of six types. 4. warning 5. exception 6. transaction +7. action +8. attributes +9. setUserInfo For Local Logs: Different types will print logs in different colors. the log will be in grey, info in blue, success in green, warning in yellow, and exception will be in red. @@ -129,120 +133,208 @@ catch (error, stackTrace){ ### How to add new cloud service? -#### Step 1: create a service which implements [LoggingService](./_cloud/logging_service.md), example of implemented service: [Sentry](./_cloud/sentry_logging_service.md) +#### Step 1: Create a service which implements [LoggingService](./_cloud/logging_service.md), example of implemented service: [Sentry](./_cloud/sentry_logging_service.md) and [DataDog](./_cloud/datadog_logging_service.md) -#### Step 2: Add that service in _services array, in [logging_library.dart](#logging_librarydart-source-code) +#### Step 2: Initialize service in init method, in [logging_library.dart](#logging_librarydart-source-code) which return updated app child. ```dart - static final List _services = [ - SentryLoggingService, - FirebaseLoggingService, - ]; + static Future init({ + required ErrorLoggingType errorLogging, + required Widget app, + }) async { + if (errorLogging.isSentry) { + _loggingService = SentryLoggingService(); + } else if (errorLogging.isDatadog) { + _loggingService = DataDogLoggingService(); + } else if (errorLogging.isFirebase) { + _loggingService = FirebaseLoggingService(); + } + + final updatedApp = await _loggingService?.init(app: app); + + return updatedApp ?? app; + } ``` -#### Step 3: Call relavent functions of that new service in `_logEvent` and `exception`, [switch cases](#logging-library-dart-source-code) +#### Step 3: Call relavent functions of that service in `_logEvent`, `exception`, `logUserInfo`,`logActions` and `logSections` (#logging-library-dart-source-code) -```dart{15-21} -static void _logEvent( +```dart +static Future _logEvent( String text, { Object? data, EventType? type, - }) { - for (var service in _services) { - switch (service) { - case SentryLoggingService: - SentryLoggingService.logEvent( - message: text, - level: type?.toSentryLevel, - data: data, - ); - return; - case FirebaseLoggingService: - FirebaseLoggingService.logEvent( - message: text, - type: type, - data: data, - ); - return; - default: - return; - } - } + }) async { + await _loggingService?.logEvent( + message: text, + type: type!, + data: data, + ); } ``` -```dart{27-33} -static void exception( +```dart +static Future exception( dynamic throwable, { Object? data, + dynamic source, dynamic stackTrace, dynamic hint, + RumErrorSource? rumErrorSource, bool disableLocalLogging = false, bool disableCloudLogging = false, - }) { - EnvironmentConfig config = EnvironmentConfig.getEnvConfig(); - if (config.enableLocalLogs && !disableLocalLogging) { - Console.danger(throwable.toString(), data); + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.danger('$throwable\n$hint', data); } - if (config.enableCloudLogs && !disableCloudLogging) { + if (_config.enableCloudLogs && !disableCloudLogging) { final hintWithData = { 'hint': hint, 'data': data, }; - for (var service in _services) { - switch (service) { - case SentryLoggingService: - SentryLoggingService.logException( - throwable, - stackTrace: stackTrace, - hint: hintWithData, - ); - return; - case FirebaseLoggingService: - FirebaseLoggingService.logException( - throwable, - stackTrace: stackTrace, - hint: hintWithData, - ); - return; - default: - return; - } - } + await _loggingService?.logException( + throwable, + stackTrace: stackTrace, + hint: hintWithData, + ); } } ``` +```dart +static Future logUserInfo({ + bool disableLocalLogging = false, + bool disableCloudLogging = false, + required String id, + required String name, + required String email, + Map extraInfo = const {}, + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.info(name, extraInfo); + } + if (_config.enableCloudLogs && !disableCloudLogging) { + await _loggingService?.setUserInfoLogger( + id: id, + name: name, + email: email, + extraInfo: extraInfo, + ); + } + } + ``` + + ```dart +static Future logActions({ + bool disableLocalLogging = false, + bool disableCloudLogging = false, + required RumActionType rumActionType, + required String eventName, + Map? eventProperties, + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.success(eventName, eventProperties); + } + if (_config.enableCloudLogs && !disableCloudLogging) { + await _loggingService?.addEventsLogger( + eventName: eventName, + eventProperties: eventProperties ?? {}, + rumActionType: rumActionType, + ); + } + } + ``` + + ```dart +static Future logSections({ + bool disableLocalLogging = false, + bool disableCloudLogging = false, + required String sectionName, + required dynamic sectionValue, + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.success(sectionName, sectionValue); + } + if (_config.enableCloudLogs && !disableCloudLogging) { + await _loggingService?.addSectionLogger( + sectionName: sectionName, + sectionValue: sectionValue, + ); + } + } + ``` + ## Enviroment variables which control logging -- depending on environment variables `enableLocalLogs` and `enableCloudLogs`, the content is logged. e.g. if `enableLocalLogs` in the [environment](../../env.md) is set to `false` then no local logs will be printed. if `enableCloudLogs` is set to `false` then no local logs will be printed. +- Depending on environment variables `enableLocalLogs` and `enableCloudLogs`, the content is logged. e.g. if `enableLocalLogs` in the [environment](../../env.md) is set to `false` then no local logs will be printed. if `enableCloudLogs` is set to `false` then no local logs will be printed. ## `logging_library.dart` Source code: ```dart -import './_cloud/firebase_logging_service.dart'; -import './_cloud/logging_service.dart'; -import './_cloud/sentry_logging_service.dart'; -import './_local/console_service.dart'; -import '../../env.dart'; +import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart'; +import 'package:flutter/material.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; + +import '../../env/env.dart'; +import '_cloud/datadog_logging_service.dart'; +import '_cloud/firebase_logging_service.dart'; +import '_cloud/logging_service.dart'; +import '_cloud/sentry_logging_service.dart'; +import '_local/console_service.dart'; +import 'models/log.dart'; class Log { - static final List _services = [ - SentryLoggingService, - // FirebaseLoggingService, + static EnvironmentConfig get _config => EnvironmentConfig.getConfig; + static LoggingService? _loggingService; + + static List loggingServiceObserver = [ + if (_config.errorLoggingTypeService(isFirebaseEnabled: false).isSentry) + SentryNavigatorObserver() + else if (_config.errorLoggingTypeService(isFirebaseEnabled: false).isDatadog) + DatadogNavigationObserver( + datadogSdk: DataDogLoggingService.datadogSdk ?? DatadogSdk.instance, + ) ]; + static Future init({ + required ErrorLoggingType errorLogging, + required Widget app, + }) async { + if (errorLogging.isSentry) { + _loggingService = SentryLoggingService(); + } else if (errorLogging.isDatadog) { + _loggingService = DataDogLoggingService(); + } else if (errorLogging.isFirebase) { + _loggingService = FirebaseLoggingService(); + } + + final updatedApp = await _loggingService?.init(app: app); + + return updatedApp ?? app; + } + + static Future logEvent({ + required String message, + required EventType type, + Object? data, + bool disableLocalLogging = false, + bool disableCloudLogging = false, + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.log(message.toString(), data); + } + await _loggingService?.logEvent(message: message, type: type, data: data); + } + static void log( dynamic text, { Object? data, bool disableLocalLogging = false, bool disableCloudLogging = false, }) { - EnvironmentConfig config = EnvironmentConfig.getEnvConfig(); - if (config.enableLocalLogs && !disableLocalLogging) { + if (_config.enableLocalLogs && !disableLocalLogging) { Console.log(text.toString(), data); } - if (config.enableCloudLogs && !disableCloudLogging) { + if (_config.enableCloudLogs && !disableCloudLogging) { _logEvent(text.toString(), data: data, type: EventType.log); } } @@ -253,11 +345,10 @@ class Log { bool disableLocalLogging = false, bool disableCloudLogging = false, }) { - EnvironmentConfig config = EnvironmentConfig.getEnvConfig(); - if (config.enableLocalLogs && !disableLocalLogging) { + if (_config.enableLocalLogs && !disableLocalLogging) { Console.info(text.toString(), data); } - if (config.enableCloudLogs && !disableCloudLogging) { + if (_config.enableCloudLogs && !disableCloudLogging) { _logEvent(text.toString(), data: data, type: EventType.info); } } @@ -268,11 +359,10 @@ class Log { bool disableLocalLogging = false, bool disableCloudLogging = false, }) { - EnvironmentConfig config = EnvironmentConfig.getEnvConfig(); - if (config.enableLocalLogs && !disableLocalLogging) { + if (_config.enableLocalLogs && !disableLocalLogging) { Console.success(text.toString(), data); } - if (config.enableCloudLogs && !disableCloudLogging) { + if (_config.enableCloudLogs && !disableCloudLogging) { _logEvent(text.toString(), data: data, type: EventType.success); } } @@ -283,80 +373,125 @@ class Log { bool disableLocalLogging = false, bool disableCloudLogging = false, }) { - EnvironmentConfig config = EnvironmentConfig.getEnvConfig(); - if (config.enableLocalLogs && !disableLocalLogging) { + if (_config.enableLocalLogs && !disableLocalLogging) { Console.warning(text.toString(), data); } - if (config.enableCloudLogs && !disableCloudLogging) { + if (_config.enableCloudLogs && !disableCloudLogging) { _logEvent(text.toString(), data: data, type: EventType.warning); } } - static void exception( + static Future exception( dynamic throwable, { Object? data, + dynamic source, dynamic stackTrace, dynamic hint, + RumErrorSource? rumErrorSource, bool disableLocalLogging = false, bool disableCloudLogging = false, - }) { - EnvironmentConfig config = EnvironmentConfig.getEnvConfig(); - if (config.enableLocalLogs && !disableLocalLogging) { - Console.danger(throwable.toString(), data); + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.danger('$throwable\n$hint', data); } - if (config.enableCloudLogs && !disableCloudLogging) { + if (_config.enableCloudLogs && !disableCloudLogging) { final hintWithData = { 'hint': hint, 'data': data, }; - for (var service in _services) { - switch (service) { - case SentryLoggingService: - SentryLoggingService.logException( - throwable, - stackTrace: stackTrace, - hint: hintWithData, - ); - return; - case FirebaseLoggingService: - FirebaseLoggingService.logException( - throwable, - stackTrace: stackTrace, - hint: hintWithData, - ); - return; - default: - return; - } - } + await _loggingService?.logException( + throwable, + stackTrace: stackTrace, + hint: hintWithData, + ); + } + } + + static Future logUserInfo({ + bool disableLocalLogging = false, + bool disableCloudLogging = false, + required String id, + required String name, + required String email, + Map extraInfo = const {}, + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.info(name, extraInfo); + } + if (_config.enableCloudLogs && !disableCloudLogging) { + await _loggingService?.setUserInfoLogger( + id: id, + name: name, + email: email, + extraInfo: extraInfo, + ); } } - static void _logEvent( + static Future logActions({ + bool disableLocalLogging = false, + bool disableCloudLogging = false, + required RumActionType rumActionType, + required String eventName, + Map? eventProperties, + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.success(eventName, eventProperties); + } + if (_config.enableCloudLogs && !disableCloudLogging) { + await _loggingService?.addEventsLogger( + eventName: eventName, + eventProperties: eventProperties ?? {}, + rumActionType: rumActionType, + ); + } + } + + static Future logSections({ + bool disableLocalLogging = false, + bool disableCloudLogging = false, + required String sectionName, + required dynamic sectionValue, + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.success(sectionName, sectionValue); + } + if (_config.enableCloudLogs && !disableCloudLogging) { + await _loggingService?.addSectionLogger( + sectionName: sectionName, + sectionValue: sectionValue, + ); + } + } + + static Future logTransaction({ + required Function execute, + required TransactionDetails details, + bool disableLocalLogging = false, + bool disableCloudLogging = false, + }) async { + if (_config.enableLocalLogs && !disableLocalLogging) { + Console.logTransaction(execute: execute, details: details); + } + if (_config.enableCloudLogs && !disableCloudLogging) { + await _loggingService?.logTransaction( + execute: execute, + details: details, + ); + } + } + + static Future _logEvent( String text, { Object? data, EventType? type, - }) { - for (var service in _services) { - switch (service) { - case SentryLoggingService: - SentryLoggingService.logEvent( - message: text, - level: type?.toSentryLevel, - data: data, - ); - return; - case FirebaseLoggingService: - FirebaseLoggingService.logEvent( - message: text, - type: type, - data: data, - ); - return; - default: - return; - } - } + }) async { + await _loggingService?.logEvent( + message: text, + type: type!, + data: data, + ); } } + ``` From acc0b11ada943a3f5b58b126d503480a333d05e6 Mon Sep 17 00:00:00 2001 From: Meetkumar Shah Date: Fri, 22 Nov 2024 17:31:37 +0530 Subject: [PATCH 3/8] Updated: Logging service document --- .../3._cloud/logging_service.md | 57 ++++++++++++++----- .../logging_library/logging_library.md | 2 +- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/logging_service.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/logging_service.md index 7706b77a..0f4b1722 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/logging_service.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/logging_service.md @@ -14,7 +14,7 @@ None ::alert{type="danger" class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert"} Developer Guide -- Developer should never use this service directly, use [Logging Library](../logging_library.md) instead. +- We should never use this service directly, use [Logging Library](../logging_library.md) instead. :: @@ -22,11 +22,14 @@ Developer Guide ## Log types -Currently, three types of logging functions are there in abstract class `LoggingService`. +Currently, six types of logging functions are there in abstract class `LoggingService`. 1. event 2. exception 3. transaction +4. action +5. attributes +6. setUserInfo - event is used to for log, info, success, and warning. - exception is used to for logging exceptions. @@ -34,34 +37,58 @@ Currently, three types of logging functions are there in abstract class `Logging ## LoggingService -- This service is abstract class and is used to implement different cloud services. e.g. Sentry, Google CrashAnalytics, etc. +- This service is abstract class and is used to implement different cloud services. e.g. Sentry, DataDog, Google CrashAnalytics, etc. - Check [here](../logging_library.md#how-to-add-new-cloud-service) how can you add new cloud logging service. - This service defines basic structure for services which implements `LoggingService` so when different services are used in [logging_library](../logging_library.md), they do provide basic functions needed. ```dart +import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart'; +import 'package:flutter/material.dart'; + import '../models/log.dart'; abstract class LoggingService { - static logEvent({ - required String message, - required EventType type, - Object? data, - }) => - UnimplementedError(); + Future init({ + required Widget app, + }); - static logException( + Future logException( dynamic throwable, { + dynamic source, dynamic stackTrace, dynamic hint, - }) => - UnimplementedError(); + }); + + Future setUserInfoLogger({ + String? id, + String? name, + String? email, + Map? extraInfo, + }); + + Future logEvent({ + required String message, + required EventType type, + Object? data, + }); - static logTransaction({ + Future addEventsLogger({ + required RumActionType rumActionType, + required String eventName, + Map? eventProperties, + }); + + Future addSectionLogger({ + required String sectionName, + required dynamic sectionValue, + }); + + Future logTransaction({ required Function execute, required TransactionDetails details, - }) async => - UnimplementedError(); + }); } + ``` diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/logging_library.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/logging_library.md index de4c8bb0..23a40562 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/logging_library.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/logging_library.md @@ -40,7 +40,7 @@ Use cases: :: ## Log types -Currently, our logs are of six types. +Currently, our logs are of nine types. 1. log 2. info 3. success From e3c8fdbdd006c212d9fc762120565532ba5de178 Mon Sep 17 00:00:00 2001 From: Meetkumar Shah Date: Fri, 22 Nov 2024 17:38:53 +0530 Subject: [PATCH 4/8] Updated: Sentry logging service doc --- .../3._cloud/2.sentry_logging_service.md | 112 +++++++++++++++--- 1 file changed, 98 insertions(+), 14 deletions(-) diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/2.sentry_logging_service.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/2.sentry_logging_service.md index 8b37b6e0..5ffbf515 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/2.sentry_logging_service.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/2.sentry_logging_service.md @@ -45,39 +45,123 @@ Developer Guide ## Source code ```dart +import 'package:flutter/material.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import './logging_service.dart'; +import '../../../env/env.dart'; +import '../_local/console_service.dart'; import '../models/log.dart'; +import 'logging_service.dart'; -abstract class SentryLoggingService implements LoggingService { - static logEvent({ +class SentryLoggingService implements LoggingService { + static EnvironmentConfig get _config => EnvironmentConfig.getConfig; + + @override + Future init({ + required Widget app, + }) async { + Widget widget = app; + await SentryFlutter.init( + (options) => options + ..dsn = _config.sentryConfig!.dsn + ..autoAppStart = _config.sentryConfig!.autoAppStart + ..tracesSampleRate = _config.sentryConfig!.tracesSampleRate + ..enableAutoPerformanceTracing = _config.sentryConfig!.enableAutoPerformanceTracing + ..enableUserInteractionTracing = _config.sentryConfig!.enableUserInteractionTracing + ..environment = _config.envType, + ); + + if (_config.sentryConfig!.enableUserInteractionTracing) { + widget = SentryUserInteractionWidget( + child: widget, + ); + } + if (_config.sentryConfig!.enableAssetsInstrumentation) { + widget = DefaultAssetBundle( + bundle: SentryAssetBundle( + enableStructuredDataTracing: true, + ), + child: widget, + ); + } + return widget; + } + + @override + Future addEventsLogger({ + required dynamic rumActionType, + required String eventName, + Map? eventProperties, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } + + @override + Future addSectionLogger({ + required String sectionName, + required sectionValue, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } + + @override + Future logEvent({ required String message, - SentryLevel? level, + EventType? type, Object? data, - }) { - final SentryEvent event = SentryEvent(message: SentryMessage(message), level: level); + }) async { + final SentryEvent event = SentryEvent( + message: SentryMessage(message), + level: type!.toSentryLevel, + ); Sentry.captureEvent( event, hint: data == null ? null : Hint.withMap({'data': data}), ); } - static logException( - dynamic throwable, { - dynamic stackTrace, - dynamic hint, - }) { - Sentry.captureException(throwable, stackTrace: stackTrace, hint: hint); + @override + Future logException( + throwable, { + source, + stackTrace, + hint, + }) async { + Sentry.captureException( + throwable, + stackTrace: stackTrace, + hint: hint, + ); } - static logTransaction({ + @override + Future logTransaction({ required Function execute, required TransactionDetails details, }) async { - final ISentrySpan transaction = Sentry.startTransaction(details.name, details.operation); + final ISentrySpan transaction = Sentry.startTransaction( + details.name, + details.operation, + ); await execute(); await transaction.finish(); } + + @override + Future setUserInfoLogger({ + String? id, + String? name, + String? email, + Map? extraInfo, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } } + ``` From 57e2a631f05635173e218398dea1e2c9bf853b66 Mon Sep 17 00:00:00 2001 From: Meetkumar Shah Date: Fri, 22 Nov 2024 17:39:45 +0530 Subject: [PATCH 5/8] Updated: Firebase Logging service doc --- .../3._cloud/3.firebase_logging_service.md | 87 +++++++++++++++---- .../3._cloud/logging_service.md | 3 + 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/3.firebase_logging_service.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/3.firebase_logging_service.md index ba070989..1d819c87 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/3.firebase_logging_service.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/3.firebase_logging_service.md @@ -26,28 +26,85 @@ Unimplemented error :: ```dart -import './logging_service.dart'; +import 'package:flutter/widgets.dart'; + +import '../_local/console_service.dart'; import '../models/log.dart'; +import 'logging_service.dart'; + +class FirebaseLoggingService implements LoggingService { + @override + Future init({ + required Widget app, + }) async { + return app; + } + + @override + Future addEventsLogger({ + required dynamic rumActionType, + required String eventName, + Map? eventProperties, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } -abstract class FirebaseLoggingService implements LoggingService { - static logEvent({ + @override + Future addSectionLogger({ + required String sectionName, + required sectionValue, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } + + @override + Future logEvent({ required String message, - EventType? type, + required EventType type, Object? data, - }) => - throw UnimplementedError(); + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } - static logException( - dynamic throwable, { - dynamic stackTrace, - dynamic hint, - }) => - throw UnimplementedError(); + @override + Future logException( + throwable, { + source, + stackTrace, + hint, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } - static logTransaction({ + @override + Future logTransaction({ required Function execute, required TransactionDetails details, - }) async => - UnimplementedError(); + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } + + @override + Future setUserInfoLogger({ + String? id, + String? name, + String? email, + Map? extraInfo, + }) async { + Console.log( + "~~~~~~> This method should be avoided with this logging service, as it doesn't do anything", + ); + } } + ``` diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/logging_service.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/logging_service.md index 0f4b1722..7631da1f 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/logging_service.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/logging_library/3._cloud/logging_service.md @@ -34,6 +34,9 @@ Currently, six types of logging functions are there in abstract class `LoggingSe - event is used to for log, info, success, and warning. - exception is used to for logging exceptions. - transaction is used to for logging transactions. +- action are used for logging events. +- attributes are used to add sections to datadog. +- setUserInfo are used to set default information of user. ## LoggingService From b91f99634d52d5b3f720096b7183eb42f20239f6 Mon Sep 17 00:00:00 2001 From: Meetkumar Shah Date: Fri, 22 Nov 2024 18:10:25 +0530 Subject: [PATCH 6/8] Updated: Base controller doc --- .../1.base/1.controller.md | 87 +++++++++---------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/1.base/1.controller.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/1.base/1.controller.md index bbb3ab42..157c3aba 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/1.base/1.controller.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/1.base/1.controller.md @@ -11,61 +11,54 @@ package `get_storage` :: ```dart{4,5,7,10,14,22,26,51,54} -... +class BaseController extends GetxController { + Future init({ + required Widget app, + required Widget errorApp, + FirebaseOptions? firebaseOptions, + }) async { + try { + // Storage initialization to store some properties locally + await GetStorage.init(); -Future init({ - required Widget app, - FirebaseOptions? firebaseOptions, -}) async { - // Storage initialization to store some properties locally - await GetStorage.init(); + // Environment initialization + final envController = Get.put(EnvController()); + await envController.initialize(); + final EnvironmentConfig config = EnvironmentConfig.getConfig; - // Environment initialization - EnvironmentConfig.setEnvConfig(); - final EnvironmentConfig config = EnvironmentConfig.getEnvConfig(); + // Initialization of Firebase and Services + bool isFirebaseEnabled = firebaseOptions != null; + if (isFirebaseEnabled) { + await Firebase.initializeApp( + options: firebaseOptions, + ); + } - // Initialization of Firebase and Services - if (firebaseOptions != null) { - await Firebase.initializeApp( - options: firebaseOptions, - ); - DynamicLinks.init(); - } + // Other Local Initializations (Depends on your app) + AppTheme.init(); + Api.init(); - // Other Local Initializations (Depends on your app) - AppTheme.init(); - Api.init(); + // RootAssets + Get.put(RootAssetsController()); - // Sentry Initialization (And/ Or) Running main app - if (null != config.sentryConfig && config.sentryConfig!.dsn.isNotEmpty) { - await SentryFlutter.init( - (options) => options - ..dsn = config.sentryConfig!.dsn - ..autoAppStart = config.sentryConfig!.autoAppStart - ..tracesSampleRate = config.sentryConfig!.tracesSampleRate - ..enableAutoPerformanceTracing = config.sentryConfig!.enableAutoPerformanceTracing - ..enableUserInteractionTracing = config.sentryConfig!.enableUserInteractionTracing - ..environment = config.envType, - ); - Widget child = app; - if (config.sentryConfig!.enableUserInteractionTracing) { - child = SentryUserInteractionWidget( - child: child, - ); - } - if (config.sentryConfig!.enableAssetsInstrumentation) { - child = DefaultAssetBundle( - bundle: SentryAssetBundle( - enableStructuredDataTracing: true, + // Other Core Services + await PushNotifications.init(); + await InternalNotifications.init(); + PushNotifications.askPermission(); + + // Error Monitoring & Logging + Widget child = await Log.init( + app: app, + errorLogging: config.errorLoggingTypeService( + isFirebaseEnabled: isFirebaseEnabled, ), - child: child, ); + runApp(child); + } catch (error, stackTrace) { + debugPrint(error.toString()); + debugPrintStack(stackTrace: stackTrace); + runApp(errorApp); } - // Running main app - runApp(child); - } else { - // Running main app when sentry config is not there - runApp(app); } } ``` From 82af119b429f10e4080be899cd5face4cbce9277 Mon Sep 17 00:00:00 2001 From: Meetkumar Shah Date: Fri, 22 Nov 2024 18:46:01 +0530 Subject: [PATCH 7/8] Updated: Performance monitoring doc --- .../5.services/3.performance_monitoring.md | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/3.performance_monitoring.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/3.performance_monitoring.md index c0e74ba2..96c839d8 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/3.performance_monitoring.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/3.performance_monitoring.md @@ -81,39 +81,52 @@ Just toogle environment flags to enable/ disable monitoring. ## Where is the code that handles this? -base_controller handles everything. So if developer is building on our app or has similar code of `base_controller` in their `core_controller or main` then they are good to go. +As we have [SentryLoggingService](./_cloud/sentry_logging_service.md). So if developer is building on our app or has similar code of `service` in their `services` then they are good to go. Just need to initialize the [LoggingLibrary](./logging_library.md) into the BaseController, it will return the updated child and which can parsed to `runApp(updatedChild)`. +```dart +static EnvironmentConfig get _config => EnvironmentConfig.getConfig; +``` + +``` +Widget child = await Log.init( + app: app, + errorLogging: config.errorLoggingTypeService( + isFirebaseEnabled: isFirebaseEnabled, + ), +); +runApp(child); +``` ```dart -final EnvironmentConfig config = EnvironmentConfig.getEnvConfig(); - -if (null != config.sentryConfig && config.sentryConfig!.dsn.isNotEmpty) { - await SentryFlutter.init( - (options) => options - ..dsn = config.sentryConfig!.dsn - ..autoAppStart = config.sentryConfig!.autoAppStart - ..tracesSampleRate = config.sentryConfig!.tracesSampleRate - ..enableAutoPerformanceTracing = config.sentryConfig!.enableAutoPerformanceTracing - ..enableUserInteractionTracing = config.sentryConfig!.enableUserInteractionTracing - ..environment = config.envType, + @override + Future init({ + required Widget app, + }) async { + Widget widget = app; + await SentryFlutter.init( + (options) => options + ..dsn = _config.sentryConfig!.dsn + ..autoAppStart = _config.sentryConfig!.autoAppStart + ..tracesSampleRate = _config.sentryConfig!.tracesSampleRate + ..enableAutoPerformanceTracing = _config.sentryConfig!.enableAutoPerformanceTracing + ..enableUserInteractionTracing = _config.sentryConfig!.enableUserInteractionTracing + ..environment = _config.envType, ); - Widget child = app; // Main (Material) App - if (config.sentryConfig!.enableUserInteractionTracing) { - child = SentryUserInteractionWidget( - child: child, - ); + + if (_config.sentryConfig!.enableUserInteractionTracing) { + widget = SentryUserInteractionWidget( + child: widget, + ); } - if (config.sentryConfig!.enableAssetsInstrumentation) { - child = DefaultAssetBundle( - bundle: SentryAssetBundle( - enableStructuredDataTracing: true, - ), - child: child, - ); + if (_config.sentryConfig!.enableAssetsInstrumentation) { + widget = DefaultAssetBundle( + bundle: SentryAssetBundle( + enableStructuredDataTracing: true, + ), + child: widget, + ); } - runApp(child); -} else { - runApp(app); -} + return widget; + } ``` As you can see depending on environment variables we apply different performance monitoring mechanisms. From 21dc871f29aa1c22b65f5e011a57da43ed5ce0b5 Mon Sep 17 00:00:00 2001 From: Meetkumar Shah Date: Mon, 25 Nov 2024 10:09:34 +0530 Subject: [PATCH 8/8] Updated: env file doc for datadog section --- .../3.vaahextendflutter/2.env.md | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/2.env.md b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/2.env.md index e7669b46..3f3afa70 100644 --- a/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/2.env.md +++ b/content/7.vaahflutter/5.directory_structure/3.vaahextendflutter/2.env.md @@ -51,11 +51,12 @@ Environment config contains below properties. | backendUrl | No use | | apiUrl | Represents root endpoint of url | | timeoutLimit | Represents timeout limit for requests (in milliseconds) | -| firebaseId | Represents firebase id of app | | enableLocalLogs | Used for enabling/ disabling Local Logs | | enableCloudLogs | Used for enabling/ disabling Cloud Logs | | enableApiLogInterceptor | Used for enabling/ disabling API Request and Response Local logs | +| errorLoggingType | Represent which logging service we want to use | | sentryConfig | Contains all essential values for sentry | +| datadogConfig | Contains all essential values for datadog | | pushNotificationsServiceType | Used to set the Push Notifications Service Type | | oneSignalConfig | Used to set the One signal config **(needed/ works only when push notification service type is remote)** | | internalNotificationsServiceType | Used to set the Internal Notifications Service Type | @@ -69,13 +70,27 @@ check more [here](../directory_structure/vaahextendflutter/services/performance_ | **Property Name** | **Description** | | --- | --- | -| dsn | Data Source Name (which is unique per project amd developer can obtain that by creating a new project in sentry) | +| dsn | Data Source Name (which is unique per project and developer can obtain that by creating a new project in sentry) | | enableAutoPerformanceTracing | if set to false nothing will be monitored | | autoAppStart | if enabled will monitor cold and warm start up time | | enableUserInteractionTracing | if enabled will monitor User Interaction | | enableAssetsInstrumentation | if enabled will monitor Asset Performance | | tracesSampleRate | will report uncaught errors as per rate is set, i.e. if it's 0.4 then 40% of all uncaught error will be reported | +#### DataDog Config (datadogConfig) + +Check out more [here](../directory_structure/vaahextendflutter/services/logging_library/logging_library.md) + +| **Property Name** | **Description** | +| --- | --- | +| clientToken | Client token for Logging and APM(Application Performance Monitoring) which is unique per project and developer can obtain that by creating a new project in datadog.| +| applicationId | A RUM(Real User Monitoring) Application Id. Obtained on the Datadog website.| +| site | The [DatadogSite] to send information.| +| nativeCrashReportEnabled | Whether or not to enable native crash reporting.| +| firstPartyHosts | This is used in conjunction with Datadog network tracking packages like `datadog_tracking_http_client`.| +| tracesSampleRate | Will report uncaught errors as per rate is set, i.e. if it's 20.0 then 20% of all uncaught error will be reported.| +| reportFlutterPerformance | Whether or not to enable Flutter Performance reporting.| + #### Push Notifications Service Type (pushNotificationsServiceType) Check [this](../directory_structure/vaahextendflutter/services/notification/push/notification.md). @@ -125,7 +140,7 @@ final EnvironmentConfig developConfig = EnvironmentConfig( backendUrl: '', apiUrl: '', timeoutLimit: 20 * 1000, // 20 seconds - firebaseId: '', + errorLoggingType: ErrorLoggingType.noService, sentryConfig: const SentryConfig( dsn: '', enableAutoPerformanceTracing: true, @@ -134,6 +149,15 @@ final EnvironmentConfig developConfig = EnvironmentConfig( enableAssetsInstrumentation: true, tracesSampleRate: 0.6, ), + datadogConfig: DatadogConfig( + clientToken: '', + applicationId: '', + site: DatadogSite.us1, + firstPartyHosts: ['com.webreinvent.vaahflutter'], + nativeCrashReportEnabled: true, + reportFlutterPerformance: true, + tracesSampleRate: 30.0 + ), enableLocalLogs: true, enableCloudLogs: true, enableApiLogInterceptor: true,