# Log Color

## **Step 1: Add Dependencies**

First, make sure you have the necessary dependencies in your `pubspec.yaml` file. Add the following dependencies:

```yaml
dependencies:
  flutter:
    sdk: flutter
  logger: ^2.3.0
```

## **Step 2: Create a Logging Class**

Use the provided code snippet to create a logging class. This class will handle colored logs and save logs to the device storage.

```dart
import 'dart:async';
import 'dart:convert';
import 'dart:developer' as developer;
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';

bool enviIsDevelop = true;

class MyLog {
  bool isLogging = true;

  Logger logger = Logger(
    filter: kReleaseMode ? PermissiveFilter() : DevelopmentFilter(),
    printer: PrettyPrinter(
      methodCount: 3,
      printEmojis: false,
      printTime: true, // Log sẽ chứa thời gian
    ),
  );

  Future<void> setUp({
    String? path,
    bool printTime = true,
    bool isLogging = true,
    String? noteInfoFileLog,
  }) async {
    if (this.isLogging != isLogging) this.isLogging = isLogging;
    if (path != null) {
      final file = File(path);
      if (noteInfoFileLog != null) {
        await file.writeAsString(
          noteInfoFileLog,
          mode: FileMode.writeOnlyAppend,
        );
      }

      final FileOutput2 fileOutPut = FileOutput2(
        file: file,
      );
      final ConsoleOutput consoleOutput = ConsoleOutput();
      final List<LogOutput> multiOutput = [fileOutPut, consoleOutput];
      logger = Logger(
        printer: PrettyPrinter(
          methodCount: 3,
          errorMethodCount: 12,
          lineLength: 150,
          printEmojis: false,
          printTime: printTime,
        ),
        filter: kReleaseMode ? PermissiveFilter() : DevelopmentFilter(),
        output: MultiOutput(multiOutput),
      );
    } else {
      logger = Logger(
        printer: PrettyPrinter(
          methodCount: 3,
          errorMethodCount: 12,
          lineLength: 150,
          printEmojis: false,
          printTime: printTime,
        ),
        filter: kReleaseMode ? PermissiveFilter() : DevelopmentFilter(),
      );
    }
  }

  void trace(Object? object, {String? tag, String? flag, Object? error}) {
    if (kDebugMode || isLogging) {
      logger.t(
        _print(
          object,
          tag: tag ?? _getParentMethodName(),
          flag: flag,
        ),
        time: DateTime.now(),
        error: error,
      );
    }
  }

  void debug(Object? object, {String? tag, String? flag, Object? error}) {
    if (kDebugMode || isLogging) {
      logger.d(
        _print(
          object,
          tag: tag ?? _getParentMethodName(),
          flag: flag,
        ),
        time: DateTime.now(),
        error: error,
      );
    }
  }

  void info(Object? object, {String? tag, String? flag, Object? error}) {
    if (kDebugMode || isLogging) {
      logger.i(
        _print(
          object,
          tag: tag ?? _getParentMethodName(),
          flag: flag,
        ),
        time: DateTime.now(),
        error: error,
      );
    }
  }

  void warning(Object? object, {String? tag, String? flag, Object? error}) {
    if (kDebugMode || isLogging) {
      logger.w(
        _print(
          object,
          tag: tag ?? _getParentMethodName(),
          flag: flag,
        ),
        time: DateTime.now(),
        error: error,
      );
    }
  }

  void error(Object? object, {String? tag, String? flag}) {
    if (kDebugMode || isLogging) {
      logger.e(
        _print(
          object,
          tag: tag ?? _getParentMethodName(),
          flag: flag,
        ),
        time: DateTime.now(),
        error: object,
      );
    }
  }

  void fatal(Object? object, {String? tag, String? flag, required Object error, StackTrace? stackTrace}) {
    if (kDebugMode || isLogging) {
      logger.f(
        _print(
          object,
          tag: tag ?? _getParentMethodName(),
          flag: flag,
        ),
        time: DateTime.now(),
        error: error,
        stackTrace: stackTrace,
      );
    }
  }
}

String? _getParentMethodName() {
  try {
    throw Exception();
  } catch (e, stackTrace) {
    final frames = stackTrace.toString().split('\n');

    var father = "";
    var grandfather = "";

    try {
      final fatherFrame = frames[3];
      father = fatherFrame.split(' ').last;
    } catch (_) {}
    try {
      final grandfatherFrame = frames[4];
      grandfather = grandfatherFrame.split(' ').last;
    } catch (_) {}

    return father == "" && grandfather == "" ? null : "$father${grandfather == "" ? grandfather : "\n$grandfather"}\n";
  }
}

String _print(Object? object, {String? tag, String? flag}) =>
    (tag == null || tag == "") && (flag == null || flag == "")
        ? object.toString()
        : tag != null && tag != "" && flag != null && flag != ""
            ? "$tag | ${flag.toUpperCase()} | $object"
            : (tag == null || tag == "") && flag != null && flag != ""
                ? "${flag.toUpperCase()} | $object"
                : "$tag | $object";

class FileOutput2 extends LogOutput {
  final File file;
  final bool overrideExisting;
  final Encoding encoding;
  late IOSink _sink;

  FileOutput2({
    required this.file,
    this.overrideExisting = false,
    this.encoding = utf8,
  });

  @override
  Future<void> init() async {
    _sink = file.openWrite(
      mode: overrideExisting ? FileMode.writeOnly : FileMode.writeOnlyAppend,
      encoding: encoding,
    );
  }

  @override
  void output(OutputEvent event) {
    try {
      if (event.origin.level != Level.trace) _sink.writeAll(["${event.origin.level.name} | ${event.origin.time} | ${event.origin.error} \n${event.origin.message} \n\n"], '\n');
    } catch (e) {
      print(e);
    }
  }

  @override
  Future<void> destroy() async {
    await _sink.flush();
    await _sink.close();
  }
}

void myDebugger(Object? object, {String? tag, String? flag, bool when = true}) {
  developer.debugger(
    when: when,
    message: _print(
      object,
      tag: tag ?? _getParentMethodName(),
      flag: flag,
    ),
  );
}

```

#### **Explanation**

**Function: `_getParentMethodName()`**

<figure><img src="/files/mWI79d3adQ0iBGhWxhha" alt=""><figcaption><p>_getParentMethodName</p></figcaption></figure>

This function retrieves the location of the log within the project, similar to how error logs display stack traces. This makes it easier to navigate directly to the log’s source in the code, reducing search time.

```dart
String? _getParentMethodName() {
  try {
    throw Exception();
  } catch (e, stackTrace) {
    final frames = stackTrace.toString().split('\n');

    var father = "";
    var grandfather = "";

    try {
      final fatherFrame = frames[3];
      father = fatherFrame.split(' ').last;
    } catch (_) {}
    try {
      final grandfatherFrame = frames[4];
      grandfather = grandfatherFrame.split(' ').last;
    } catch (_) {}

    return father == "" && grandfather == "" ? null : "$father${grandfather == "" ? grandfather : "\n$grandfather"}\n";
  }
}
```

**How It Works:**

1. It generates an exception and captures the stack trace.
2. It extracts the method names from specific frames in the stack trace.
3. It returns the method names in a structured format, making it easier to locate the log’s origin.

This helps in debugging by directly pinpointing where the log was recorded.

***

**Function: `_print()`**

This function customizes log display by adding **tags** and **flags** for better classification.

```dart
String _print(Object? object, {String? tag, String? flag}) =>
    (tag == null || tag == "") && (flag == null || flag == "")
        ? object.toString()
        : tag != null && tag != "" && flag != null && flag != ""
            ? "$tag | ${flag.toUpperCase()} | $object"
            : (tag == null || tag == "") && flag != null && flag != ""
                ? "${flag.toUpperCase()} | $object"
                : "$tag | $object";
```

**Usage Example:**

If the log is related to an **Activity Diagram**, you might format it as:

```dart
print(_print("get image screenshot", tag: "ADS 4.4.6.0", flag: "ACTIVITY DIAGRAM"));
```

**Output:**

```
ADS 4.4.6.0 | ACTIVITY DIAGRAM | get image screenshot
```

**How It Works:**

* If **both tag and flag** are provided → Outputs **`tag | FLAG | message`**
* If **only flag** is provided → Outputs **`FLAG | message`**
* If **only tag** is provided → Outputs **`tag | message`**
* If neither is provided → Retrieves the **parent method’s name**

## **Step 3: Initialize Logging in Your App**

Set up the logging system in your main application file. Ensure you call the `setUp` method with the desired parameters.

```dart
const ourLogDiagram = "Activity Diagram";

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  MyLog myLog = MyLog();
  await myLog.setUp(
    path: 'path/to/your/logfile.txt', // Specify the path where you want to save the log file
    printTime: true,
    isLogging: true,
    noteInfoFileLog: 'This is the log file for my Flutter app.',
  );

  // Example log usage
  myLog.info('App started');
  myLog.debug('Debugging info');
  myLog.error('An error occurred');
  myLog.info("ADS 4.4.7 | save image", flag: ourLogDiagram, tag: "Write log");

  runApp(MyApp());
}
```

***

## **Step 4: Use Logging Methods**

Use the logging methods provided in the `MyLog` class throughout your application to record messages at different levels (trace, debug, info, warning, error, fatal).

```dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    MyLog myLog = MyLog();
    
    myLog.info('Building MyApp');
    
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Logging Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              myLog.debug('Button pressed');
              myLog.warning('This is a warning');
              myLog.error('This is an error');
            },
            child: Text('Press Me'),
          ),
        ),
      ),
    );
  }
}
```

#### **Check Logs on the Device**

After running the app and generating logs, you can find the log file at the specified path (e.g., `path/to/your/logfile.txt`). Open the file to see the stored logs.

\
If you want to skip the complex parts and use it right away, check out my package: <https://pub.dev/packages/my_log>.

[Buy Me a Coffee](https://buymeacoffee.com/ducmng12g) | [Support Me on Ko-fi](https://ko-fi.com/I2I81AEJG8)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wong-coupon.gitbook.io/flutter/easy-code/log-color.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
