txt: ## dist_cpan-ATROXAPER-LogP6.md
  ## Chunk 2 of 2
  )` - save or replace cliche;
  * `cliche(:$name!, :remove)` - remove and return a cliche with specified name.
  
  ### Cliche configuration file
  
  In the configuration file, cliches have to be listed in the `cliches` array. It has the following
  fields:
  
  * `"name": <string>` - obligatory name of cliche;
  * `"matcher": <string>` - cliche matcher. If the matcher value starts and ends with `/` symbol,
    then the matcher is interpreted as regex; in another case, it is a literal;
  * `"grooves": [<writer1-name>, <filter1-name>, <writer2-name>, <filter2-name>, ... ]` - grooves,
    list of writers' and filters' names;
  * defaults - the same fields with the same possible values as described in
    [Defaults configuration file](#defaults-configuration-file) excepts `default-wrapper` - you need
    to use the `wrapper` field name.
  
  Example:
  
  ```
  {
    "cliches": [{
      "name": "c1", "matcher": "/bo .+ om/", "grooves": [ "w1", "f1", "w2", "f1" ],
      "wrapper": { "type": "transparent" }, "default-pattern": "%level %msg"
    }]
  }
  
  ```
  
  ## Default logger
  
  In any way you configured your cliches by the factory routines or configuration file or did not
  use non of them, the `default cliche` will be in the logger library. Default cliche corresponds
  to the following configuration:
  `cliche(:name(''), :matcher(/.*/), grooves => (writer(:name('')), filter(:name(''))))`. In other
  words, default cliche has an empty string name, matches any trait, has only one groove with empty
  (uses all defaults) writer with an empty string name and with empty (uses all defaults) filter with
  an empty string name. It means, by default, you do not need to configure anything at all. But you
  can change the default cliche or default writer and filter by factory subroutines or in the
  configuration file. Note that if the `LogP6` library does not find cliche with matcher logger trait
  satisfies, then an exception will be thrown.
  
  ## Change configuration
  
  Sometimes you may need to change logger configuration in runtime execution. It can be simply done
  by factory subroutines. After calling any factory subroutine, all loggers for already used
  `logger traits` will be recreated, and you can get it by `get-logger($trait)` sub. If you already
  got logger, use synchronization wrapper, then the wrapper will sync the logger himself correspond
  to its algorithm.
  
  Another way of change configuration is by using configuration file modification. Changes in the
  configuration file will be detected only if you are already using any of the synchronization
  wrappers (in `defaults` or one of `cliches`). After any change detection, all already configured
  configuration will be dropped and created new from the file.
  
  # EXAMPLES
  
  Lets explore a few general use cases:
  
  ## Use an external library that uses LogP6
  
  `LogP6` can be used during library development, and a user of the library wants entirely turn off
  any logs from the library. Let's imagine that all library loggers' traits start with `LIBNAME`
  letters. In this case, we can create a `cliche` with corresponding `matcher` and empty `grooves` -
  all library logs will be dropped.
  
  In `Raku`:
  
  ```
  use LogP6 :configure;
  
  cliche(:name('turn off LIBNAME'), :matcher(/^LIBNAME .*/), :wrapper(LogP6::Wrapper::Transparent::Wrapper.new));
  
  ```
  
  Or in the configuration file:
  
  ```
  { "cliches": [{"name": "turn off LIBNAME", "matcher": "/^LIBNAME .*/", "wrapper": {"type": "transparent"}}] }
  
  ```
  
  We use wrapper without synchronization (transparent) because we do not plan to change the library
  loggers' configuration.
  
  ## Change console application verbosity level
  
  Let's imagine we are writing a console application, and we want to add the flag `--verbose` for
  getting a more detailed output. Lets using a particular logger for application console output
  instead of using simple `say` and change filter level according to the user's choice:
  
  In `Raku`:
  
  ```
  use LogP6 :configure;
  
  cliche(:name<output>, :matcher<say>, grooves => (
    writer(:pattern('%msg'), :handle($*OUT)),
    filter(:name<verbosity>, :level($info))
  ));
  
  sub MAIN(Bool :$verbose) {
    filter(:name<verbosity>, :level($debug), :update) if $verbose;
    my $say = get-logger('say');
    $say.info('Greetings');
    $say.debugf('You set verbose flag to %s value', $verbose);
  }
  
  ```
  
  In that case, we do not need to use the configuration file. But if you want, then you can remove
  the line with `cliche` creation and add the following configuration file:
  
  ```
  {
    "writers": [{ "type": "std", "name": "say", "pattern": "%msg", "handle": { "type": "std", "path": "out" }}],
    "filters": [{ "type": "std", "name": "verbosity", "level": "info"}],
    "cliches": [{ "name": "output", "matcher": "say", "grooves": [ "say", "verbosity" ]}]
  }
  
  ```
  
  ## Conditional log calls
  
  Sometimes you may need to log information that required additional calculation. It is useful
  to know whether the log will be written or not before the calculation. Logger's `-on` methods
  were created especially for that. It will return a particular object (or Any) with `log` and
  `logf` methods you can use to log with the corresponding log level. Please look at the example
  below:
  
  ```
  use LogP6 :configure;
  
  # set logger allowed level as INFO
  filter(:name(''), :level($info), :update);
  my $log = get-logger('condition');
  my %map;
  my $str;
  # ...
  
  # to-json will not be called here, because .debug-on returned Any for now
  .log(to-json(%map, :pretty, :sorted-keys)) with $log.debug-on;
  
  # from-json will be called here, because .warn-on returned a little logger
  # log will be with WARN level
  .log(from-json($str)<key>) with $log.warn-on;
  
  with $log.trace-on {
    # this block will not be executed for now
    my $user-id = retrive-the-first-user-id-from-db();
    # use logf method to use sprintf-style logs
    .logf('the first user id in the database is %d', $user-id);
  }
  
  # Be careful with '.?' operator. Sins it is not an operator but syntax-sugar
  # to-json will be called in any case, but log will not be written for now.
  $log.debug-on.?log(to-json(%map, :pretty, :sorted-keys));
  
  ```
  
  ## Associate logs with concrete user
  
  Let's imagine we write a server application. Many users can connect to the server simultaneously
  and do some action, which produces log messages in a log file. If some exception will be caught
  and log, we want to reconstruct the user's execution flow to understand what went wrong. But
  needful records in the log file will be alongside logs from other users' actions. In such cases,
  we need to associate each log entry with some user id. Then we can grep the log file for the user
  id. For that, use `MDC`.
  
  In `Raku`:
  
  ```
  use LogP6 :configure;
  
  cliche(:name<logfile>, :matcher<server-log>, grooves => (
    writer(
      :pattern('[%date{$hh:$mm:$ss:$mss}][user:%mdc{user-id}]: %msg'),
      :handle('logfile.log'.IO.open)),
    level($info)
  ));
  
  my $server-log = get-logger('server-log');
  
  sub database-read() { # note we do not pass $user in the sub
    $server-log.info('read from database'); # [23:35:43:1295][user:717]: read from database
    # read
    CATCH { default {
      $server-log.error('database fail', :x($_)); # [23:35:44:5432][user:717]: database fail Exception X::AdHoc "database not found" <trace> 
    }}
  }
  
  sub enter(User $user) {
    $server-log.mdc-put('user-id', $user.id);
    $server-log.info('connected');     # [23:35:43:1245][user:717]: connected
  
    database-read();
  
    $server-log.info('disconnected');  # [23:35:44:9850][user:717]: disconnected
    $server-log.mdc-remove('user-id'); # it is not necessary to remove 'user-id' value from MDC 
  }
  
  ```
  
  The same configuration you can write in the configuration file:
  
  ```
  {
    "writers": [{ "type": "std", "name": "logfile", "pattern": "[%date{$hh:$mm:$ss:$mss}][user:%mdc{user-id}]: %msg",
      "handle": { "type": "file", "path": "logfile.log" }}],
    "filters": [{ "type": "std", "name": "logfile", "level": "info"}],
    "cliches": [{ "name": "logfile", "matcher": "server-log", "grooves": [ "logfile", "logfile" ]}]
  }
  
  ```
  
  ## Filter log by its content
  
  Imagine we have an application that may write sensible content to log files, for example,
  user passwords. And we want to drop such sensible logs. We can use a particular sub in `do-before`
  action of log's `filter`.
  
  In `Raku`:
  
  ```
  unit module Main;
  use LogP6 :configure;
  
  sub drop-passwords($context) is export {...}
  
  cliche(:name<sensible>, :matcher<log>, grooves => (
    writer(:pattern('%msg'), :handle('logfile.log'.IO.open)),
    filter(:level($info), before-check => (&drop-passwords))
  ));
  
  sub drop-passwords($context) {
    return False if $context.msg ~~ / password /;
    # If you want to remove a password from the log entry instead of dropping it,
    # you can remove the password from the message and store it in the context like:
    #
    # $context.msg-set(remove-password($context.msg));
    True;
  }
  
  sub connect(User $user) {
    get-logger('log').infof('user with name %s and password %s connected', $user.id, $user.passwd);
  }
  
  ```
  
  The same configuration you can write in the configuration file:
  
  ```
  {
    "writers": [{ "type": "std", "name": "writer", "pattern": "%msg",
      "handle": { "type": "file", "path": "logfile.log" }}],
    "filters": [{ "type": "std", "name": "pass-filter", "level": "info",
      "before-check": [{ "require": "Main", "fqn-method": "Main::EXPORT::DEFAULT::&drop-passwords" }]}],
    "cliches": [{ "name": "logfile", "matcher": "server-log", "grooves": [ "writer", "pass-filter" ]}]
  }
  
  ```
  
  ## Write one log in several outputs
  
  Let's imagine we have an application that works with several types of a database -- for example,
  Oracle and SQLite. We want to log work with the databases. But we want to store Oracle related
  logs in `oracle.log` and `database.log` files, and SQLite related records only in `database.log`.
  In this case, we need a straightforward logger for SQLite related logs and another one (with two
  grooves) for Oracle associated logs.
  
  In `Raku`:
  
  ```
  use LogP6 :configure;
  
  set-default-pattern('%msg');
  writer(:name<database>, :handle('database.log'.IO.open));
  writer(:name<oracle>,   :handle(  'oracle.log'.IO.open));
  filter(:name<filter>, :level($info));
  cliche(:name<oracle>, :matcher<oracle>, grooves => ('database', 'filter', 'oracle', 'filter'));
  cliche(:name<sqlite>, :matcher<sqlite>, grooves => ('database', 'filter'));
  
  sub oracle-db-fetch() {
    get-logger('oracle').info('fetch data');
    # fetch
  }
  
  sub sqlite-db-fetch() {
    get-logger('sqlite').info('fetch data');
    # fetch
  }
  
  ```
  
  The same configuration you can write in the configuration file:
  
  ```
  {
    "default-pattern": "%msg",
    "writers": [
      { "name": "database", "type": "std", "handle": { "type": "file", "path": "database.log"}},
      { "name": "oracle",   "type": "std", "handle": { "type": "file", "path": "oracle.log"  }}
    ],
    "filters": [{ "name": "filter", "type": "std", "level": "info" }],
    "cliches": [
      { "name": "oracle", "matcher": "oracle", "grooves": [ "database", "filter", "oracle", "filter" ]},
      { "name": "sqlite", "matcher": "sqlite", "grooves": [ "database", "filter" ]}
    ]
  }
  
  ```
  
  ## Write logs in journald
  
  Let's imagine you want to store logs in journald service. You can use `LogP6::Writer::Journald`
  module for that. For more information, please look at the writer module README.
  Example of configuration:
  
  In `Raku`:
  
  ```
  use LogP6 :configure;
  
  writer(LogP6::WriterConf::Journald.new(
    # name, pattern and auto-exceptions as in standard writer
    :name<to-journald>, :pattern('%msg'), :auto-exeptions
    # which additional information must be written
    :use-priority, # write 'PRIORITY=' field to journald automatically
    :use-mdc       # write all MDC contend as field to journald in 'key=value' format
  ));
  
  ```
  
  The same configuration you can write in the configuration file:
  
  ```
  {"writers": [{
    "type": "custom",
    "require": "LogP6::WriterConf::Journald",
    "fqn-class": "LogP6::WriterConf::Journald",
    "args": {
      "name": "to-journald",
      "pattern": "%msg",
      "auto-exceptions": true,
      "use-priority": true,
      "use-mdc": true
    }
  }]}
  
  ```
  
  ## Write custom writer handle for your need
  
  Sometimes you may need to write log to some exotic place. In this case you will need to implement
  your own `Writer` and its `WriterConf`. In simple cases, it would be enough to implement your own
  `IO::Handle` for the standard writer. For example, there is no de-facto must-use library for
  working with databases yet. That is why there is no particular writer for it in `LogP6`. But let's
  try to write it now:
  
  Imagine we decided to use `SQLite` database and `DB::SQLite` library. We can ask the standard
  writer to prepare SQL insert expression for us. Therefore we can only write custom `IO::Handle`.
  Fortunately, it is easy in `6.d` version:
  
  ```
  unit module MyDBHandle;
  use DB;
  use DB::SQLite;
  
  class DBHandle is IO::Handle {
    has Str $.filename is required;
    has DB $!db;
  
    submethod TWEAK() {
      self.encoding: 'utf8';
      # open database file and create table for logging
      $!db = DB::SQLite.new(:$!filename);
      $!db.execute('create table if not exists logs (date text, level text, log text)');
    }
  
    method WRITE(IO::Handle:D: Blob:D \data --> Bool:D) {
      # decode Blob data and execute. we expect the valid sql dml expression in data.
      $!db.execute(data.decode());
      True;
    }
  
    method close() { $!db.finish } # close database
  
    method READ(|) { #`[do nothing] }
  
    method EOF { #`[do nothing] }
  }
  
  ```
  
  It is all we need. Now we can write the `LogP6` configuration. In `Raku`:
  
  ```
  use MyDBModule;
  use LogP6 :configure;
  
  writer(
    :name<db>,
    # pattern provides us a valid sql dml expression
    :pattern('insert into logs (date, level, log) values (\'%date\', \'%level\', \'%msg%x{ - $name $msg $trace}\')'),
    # handle with corresponding database filename
    handle => DBHandle.new(:filename<database-log.sqlite>),
    # turn off auto-exceptions because it will corrupt our sql dml expression
    :!auto-exceptions
  );
  cliche(:name<db>, :matcher<db>, grooves => ('db', level($trace)));
  
  my $log-db = get-logger('db');
  
  $log-db.info('database logging works well');
  
  ```
  
  The same configuration you can write in the configuration file:
  
  ```
  {
    "writers": [{
      "type": "std",
      "name": "db",
      "pattern": "insert into logs (date, level, log) values ('%date', '%level', '%msg%x{ - $name $msg $trace}'",
      "handle": {
        "type": "custom",
        "require": "MyDBModule",
        "fqn-class": "MyDBModule::DBHandle",
        "args": {
          "filename": "database-log.sqlite"
        }
      },
      "auto-exceptions": false
    }],
    "filters": [{ "name": "filter", "type": "std", "level": "trace" }],
    "cliches": [{
      "name": "db",
      "matcher": "db",
      "grooves": [ "db", "filter" ]
    }]
  }
  
  ```
  
  ## Rollover log files
  
  Log files tend to grow in size. There is `IO::Handle::Rollover` module to prevent such uncontrolled
  growth. For example, you decided to store only 30MB of logs separated into three files for
  convenience. For that, you only need to create a custom handle with an `open` routine like that:
  
  In `Raku`:
  
  ```
  use IO::Handle::Rollover;
  my $handle = open("log.txt", :w, :rollover, :file-size<10M>, :3history-size);
  
  ```
  
  The same initialization you can write in the configuration file:
  
  ```
  {
  ...
    "handle": {
      "type": "custom",
      "require": "IO::Handle::Rollover",
      "fqn-method": "IO::Handle::Rollover::EXPORT::DEFAULT::&open",
      "positional": [ "log.txt" ],
      "args": {
        "w": true,
        "rollover": true,
        "file-size": "10M",
        "history-size": 3
      }
    }
  ...
  }
  
  ```
  
  You can use the handle as any other output handles, for example, in LogP6 writers. For more
  information, see the documentation for the `IO::Handle::Rollover` module.
  
  # BEST PRACTICE
  
  Try to use good traits for your loggers. If you use loggers in your library, then probably using
  one prefix in all your traits is the best option. It allows users of your library to manage your
  loggers easily.
  
  Try to choose a logger trait according to logger semantic or location. For example, you can use
  `$?CLASS.^name` as a logger trait in any of your classes or traits like `database`,
  `user management`, or so.
  
  If you use logger within a class then make the logger be a class field like
  `has $!log = get-logger('$?CLASS.^name');` If you use logger withing a subroutines logic then make
  a special sub for retrieve logger like `sub log() { state $log = get-logger('trait'); }`. Then use
  it like `log.info('msg');` It prevents any side effects caused by precompilation.
  
  # SEE OLSO
  
  * [LogP6::Writer::Journald](https://modules.raku.org/dist/LogP6-Writer-Journald:cpan:ATROXAPER)
  * [IO::Handle::Rollover](https://modules.raku.org/dist/IO-Handle-Rollover:cpan:ATROXAPER)
  
  # ROADMAP
  
  * Add a `Cro::Transform` for using `LogP6` in `cro` applications.
  
  # AUTHOR
  
  Mikhail Khorkov [atroxaper@cpan.org](mailto:atroxaper@cpan.org)
  
  Source can be located at: [github](https://github.com/atroxaper/p6-LogP6). Comments and Pull Requests
  are welcome.
  
  # COPYRIGHT AND LICENSE
  
  Copyright 2020 Mikhail Khorkov
  
  This library is free software; you can redistribute it and/or modify it under
  the Artistic License 2.0.
  