SourceHorizon / logger

Small, easy to use and extensible logger which prints beautiful logs.
https://pub.dev/packages/logger
MIT License
197 stars 33 forks source link

Add path to line of log statement, in the log output #77

Closed martinsellergren closed 1 month ago

martinsellergren commented 3 months ago

To each log output, I'd like to see the line where the corresponding log statement is.

Before: ⛔ Error listening on stream

After (something like): ⛔ Error listening on stream [hello_world/my_logger.dart 58:5]

, where hello_world/my_logger.dart 58:5 is where logger.e(...) call happened. Is it possible?

Bungeefan commented 3 months ago

Hi, as you haven't specified a specific printer, I am assuming you are using the default PrettyPrinter. Have you tried the methodCount (or the errorMethodCount) parameter?

martinsellergren commented 3 months ago

Yes, PrettyPrinter is my preferred printer. Yep, I've tried methodCount and errorMethodCount but it's not precisely what I want.

I think the problem is when you log with a stack trace provided. Then you don't get the path of the log statement, since the stack trace describes something else. But if you don't provide a stack trace the PrettyPrinter uses StackTrace.current, and log path is shown, it seems, at least if you increase methodCount, errorMethodCount. But, what I'd like is for the path of log statement to always show.

Suppose you could manipulate a stack trace yourself, e.g using stack_trace package, and take the last trace from Trace.current and put it somewhere in the log output.

Bungeefan commented 3 months ago

I feel like that's a very specific requirement, and I am having trouble imagining it as a useful general addition to the PrettyPrinter as the format of the output is often very opinionated.

I would recommend that you add it for yourself and if you feel like it would be a good addition you can open a PR with it.

martinsellergren commented 3 months ago

Sounds good. I'll experiment with it then get back with any findings or pr.

BenjiFarquhar commented 2 months ago

I wanted the file name and line number in all logs except exceptions due to it being in the stack trace. I didn't have time to look into adding this feature to the package, so I've just configured it for my app. It's hacky, but works in a pinch (I am new to this package). You'll need to add the stack_trace dependency and import it:

class CustomPrinter extends LogPrinter {
  final PrettyPrinter _prettyPrinter;

  CustomPrinter({
    int methodCount = 0,
    int errorMethodCount = 8,
    int lineLength = 120,
    bool colors = true,
    bool printEmojis = true,
    bool printTime = false,
  }) : _prettyPrinter = PrettyPrinter(
          methodCount: methodCount,
          errorMethodCount: errorMethodCount,
          lineLength: lineLength,
          colors: colors,
          printEmojis: printEmojis,
          printTime: printTime,
        );

  @override
  List<String> log(LogEvent event) {
    final trace = Trace.current(2);
    final frames = trace.frames;
    final callerFrame =
        frames[1].location.contains('main') ? frames[2] : frames[1];
    final filePath = callerFrame.uri.pathSegments.last;
    final location = '$filePath:${callerFrame.line}';
    final color = PrettyPrinter.defaultLevelColors[event.level]!;
    final emoji = PrettyPrinter.defaultLevelEmojis[event.level]!;

    if (event.level == Level.error) {
      return _prettyPrinter.log(event); // I don't need the file name for errors since the stack trace will contain it. You can add it here if you like.
    } else {
      return [color('$emoji ${event.message} - $location')];
    }
  }
}

final Logger logger = Logger(
  printer: CustomPrinter(methodCount: 20, errorMethodCount: 20),
);

I only wanted the file name. If you want the full path, change location to this: final location = '${callerFrame.uri.path}:${callerFrame.line}';

This is fairly hacky, so will explain: final callerFrame = frames[1].location.contains('main') ? frames[2] : frames[1]; If the logger.d came directly from your codebase, the callerFrame will be at frames[1], but if it was intercepted by your main file, E.g you have an exception handler in main.dart that calls logger.e that catches what you didn't catch in your code base or you have debugPrint = (message, {wrapWidth}) { which calls logger.d in your main file, it will be at frames[2].

martinsellergren commented 1 month ago

Nice one, Benji. I ended up with this, seems enough for my use case:

import 'package:logger/logger.dart';
import 'package:stack_trace/stack_trace.dart';

final logger = MyLogger();

class MyLogger extends Logger {
  MyLogger() : super(printer: PrettyPrinter(errorMethodCount: 50));

  @override
  void log(Level level, message,
      {DateTime? time, Object? error, StackTrace? stackTrace}) {
    final currentLine = _currentLine();
    message = [
      if (currentLine != null) 'at: $currentLine',
      message.toString(),
    ].join('\n');
    super.log(level, message, time: time, error: error, stackTrace: stackTrace);
  }

  String? _currentLine() {
    final trace = Trace.current(3);
    return trace.frames.firstOrNull.toString();
  }
}