flutter-template icon indicating copy to clipboard operation
flutter-template copied to clipboard

Add a better `logger` library

Open johsoe opened this issue 3 years ago • 2 comments

We've looked at:

  • https://pub.dev/packages/logger
  • https://pub.dev/packages/loggy

Logger looks best and pretty extensible. The problem is initialization, but @nivisi had an idea of using mixins + configuration via a global function.

johsoe avatar Jul 05 '22 13:07 johsoe

Speaking of logger.

We can use a global method to get a preconfigured logger instance:

file_output.FileOutput _fileOutput = file_output.FileOutput();
PrettyConsoleOutput _prettyConsoleOutput = PrettyConsoleOutput();

Logger getLogger({dynamic forObject}) {
  return Logger(
    printer: SimpleLogPrinter(forObject: forObject), 
    output: MultiOutput([_fileOutput, _prettyConsoleOutput]),
    filter: ProductionFilter(),
    level: kDebugMode ? Level.verbose : Level.info,
  );
}

And then create a mixin:

mixin LoggableMixin {
  @protected
  late final Logger logger = getLogger(forObject: this);

  @override
  String toString() {
    return runtimeType.toString();
  }
}

So in the code we could do:

class AnyClassImpl extends AnyClass with LoggableMixin {
  Future<void> anyMethod() {
    logger.i('This is a logger message!');
    return Future.value();
  }
}

// This will be printed
01.02.03 04:05 [I] AnyClassImpl : This is a logger message!

Note: the message above contains the AnyClassImpl string that makes that line of information contextualized. To achieve this, SimpleLogPrinter that is created in the getLogger method does forObject.toString() under the hood. Once we obfuscate our code the toString overriding in the LoggableMixin will stop working. To fix this, we need to change the mixin a bit:

mixin LoggableMixin {
  @protected
  late final Logger logger = getLogger(forObject: this);
  
  // This property will be used by the logger printer later on
  // Could be overwritten in the class itself! 
  final String loggableName = runtimeType.toString();
}

So the simple printer could do sth like this to get the context name:

final contextName = forObject is LoggableMixin ? forObject.loggableName : forObject.toString();

Then in our AnyClass:

class AnyClassImpl extends AnyClass with LoggableMixin {
  @override
  final String loggableName = 'AnyClassImpl =)';

  Future<void> anyMethod() {
    logger.i('This is a logger message!');
    return Future.value();
  }
}

// This will be printed
01.02.03 04:05 [I] AnyClassImpl =) : This is a logger message!

nivisi avatar Jul 11 '22 04:07 nivisi

Now it's a separate package: https://pub.dev/packages/contextual_logging

nivisi avatar Jan 20 '23 14:01 nivisi