harmony_log
Harmony Logging Mechanism
Installation
Add the following lines to your pubspec.yaml file:
dependencies:
harmony_log: ^latest.version
import harmony_log.
import 'package:harmony_log/harmony_log.dart';
Usage
Log system contains two main blocks, A logger which is represented using Log
class, and an output which is represented
using LogOutput
class.
Each log request on Log
class is transformed to a LogEvent
by adding identification and time information, and is
redirected to the given LogOutput
. LogEvent
s contain message
, error
, stackTrace
and level
(which is
represented using LogLevel
enum) as well as some extra information like id
(which is generated by the given LogId
)
, time
and extra
. extra
is a general field with type Object
which is reserved for adding extra information to a
log event.
A LogId
is needed for Log
class to generate an id for each log event. You can use LogId factories like
counter
which will simply count from a starting value, constant
which will always generate the same given id
and custom
which you can provide your custom logic for id generation. You can check out on dart docs.
As well as a generic log
method on Log
class you have access to helper methods
v
, d
, i
, w
, e
and wtf
which add level
according to their acronyms. You have also access to write
method
on Log
class which is a low level method to directly specify log events. LogLevel
enum has 6 different log levels
like LogLevel.verbose
with significance of 0
and LogLevel.wtf
with significance of 5
. LogLevel
s are comparable
using compareTo
method or comparison operators like >=
.
Each Log
class should be initialized using init
and closed using close
. After calling init
it should be
immediately usable. But after calling close the behaviour is unspecified meaning using log after being closed can
actually log events, don't log events without giving an error or throwing an error. Also closing may be finished
asynchronously. you must call logging methods after calling init
and before calling close
. init
and close
commands are redirected to LogOutput
.
Log
class is created by providing a LogOutput
, LogId
and a tag
to its constructor. tag is by default null. it is
recommended that you create one Log
class with null tag for your app log, then create tagged loggers from your logger
using tagged
method. This has the advantage to using the same output and id generation systems, So by initializing one
them all of them are initialized and the same for closing.
LogOutput
s are created using its factories. Some of the outputs are responsible to redirect events to other outputs,
like filtered
to filter events and redirect to other output, multi
to output to multiple other outputs, redirect
to redirect output to other output conditionally and redirectOnDebug
to redirect output to other output only on debug.
Some of them are terminal outputs like noop
which does not do anything. And there is a special custom
output which
can be used to create custom outputs by providing functions for init
, write
and close
operations. And a
special plain
factory which is used to output plain String
data. There is also developerLog
log output which can
be used to log to dart:developer
.
Filtering events is done by using special filtered
LogOutput factory. You should provide and output for filtered
events to go to and a LogFilter
to filter events. LogFilter
s are created using its factories, for example level
which will allow levels equal or greater than the specified level, all
which will allow all events, none
which won't
allow any events, debug
which will allow events only on debug, and several other factories which you can check out on
dart docs. And there is a custom
factory which is used to create custom filtering logic. All LogFilters support
standard set operations like |
, &
, -
and !
.
Plain output is where you will output logs to for example console and file. Outputting plain data is done by using
special plain
LogOutput factory. You should provide a LogPlainFilter
and a LogPlainOutput
.
LogPlainFilter
is responsible for transforming a log event to a list of string lines. It can be created using its
factories like simple
which will format a simple single-line output, pretty
which will format events in a box like
view, json
which will format events in json. json
formatter is specially good when outputting to a
file. LogPlainFilter
will also add start and end sequences to log output, for example in json we need [
and ]
at
start and end of file to be meaningful. There is a custom
factory which you can use to implement your custom
formatting mechanism.
LogPlainOutput
is responsible to output a list of string lines. Some of the plain outputs are responsible to redirect
events to other plain outputs, like multi
to output to multiple other outputs, redirect
to redirect output to other output conditionally. Some of them are terminal outputs like noop
which does not do
anything, console
which is used to output data using print
statements and file
which is used to output data to
file in the given directory path. File name is based on creating time with a prefix, postfix and extension which can be
changed of needed. File output can not be used on flutter web.
As a note filtering will always pass init
and close
operations. It is better to use redirect
and its dialects for
enabling/disabling logging completely on different situations.
A basic example, which only logs on debug and outputs to cosnole using simple formatting:
import 'package:harmony_log/harmony_log.dart';
void main() {
final log = Log(
id: LogId.counter(),
child: LogOutput.redirectOnDebug(
child: LogOutput.plain(
format: LogPlainFormat.simple(),
child: LogPlainOutput.console(),
),
),
);
log.init();
log.i('hello, there!');
log.e('bad code!', error: AssertionError());
log.close();
}
A basic example, which only logs on debug and outputs to cosnole using pretty formatting, and also filtering logs to
have at least info
level and using counter id generation:
final log = Log(
id: LogId.counter(),
child: LogOutput.redirectOnDebug(
child: LogOutput.filtered(
filter: LogFilter.level(LogLevel.info),
child: LogOutput.plain(
format: LogPlainFormat.pretty(),
child: LogPlainOutput.console(),
),
),
),
);
An example, which shows logs with level at least debug
on cosole only in debug with pretty formatting, saves all logs
to a file with json formatting, on release sends logs with level of at least error
to a server:
final log = Log(
id: LogId.counter(),
child: LogOutput.multi(
children: [
LogOutput.redirectOnDebug(
child: LogOutput.filtered(
filter: LogFilter.level(LogLevel.debug),
child: LogOutput.plain(
format: LogPlainFormat.simple(),
child: LogPlainOutput.console(),
),
),
),
LogOutput.plan(
format: LogPlainFormat.json(),
child: LogPlainOutput.file(
path: 'path/to/directory',
),
),
LogOutput.redirectOnRelease(
child: LogOutput.filtered(
filter: LogFilter.level(LogLevel.error),
child: LogOutput.custom(
write: (e) {
// send event to server
},
),
),
),
],
),
);
creating a tagged logger:
final log = Log(/* ... */);
final otherLog = log.tagged('OTHER');
Examples
see example: